ActualDocsAgent guide

API

API Reference

Reference for inference endpoints, request shapes, and response schemas

5 min readMarkdown10 sectionsopen computers →

The public relay exposes model inventory and HTTP inference, with Server-Sent Events (SSE) for streaming. Protocol compatibility depends on the serving client, inference engine, and model capabilities.

Follow API Quickstart for executable requests.

Base URL and authentication#

Use https://api.actual.inc and send an inference credential from User > Keys:

Authorization: Bearer ac_your_credential_here

Console Chat uses your signed-in web session. Device-management authentication is separate from public inference credentials.

Supported public inference routes#

EndpointMethodCompatibilityStreaming
/v1/modelsGETOpenAI-style inventory with Actual extensionsNo
/v1/chat/completionsPOSTOpenAI Chat CompletionsSSE
/v1/completionsPOSTLegacy text completionsSSE
/v1/responsesPOSTOpenResponsesSSE
/v1/messagesPOSTAnthropic MessagesSSE

The current relay does not serve public cluster-listing, model-loading, or legacy session-WebSocket endpoints. Use actual clusters --format json for the cluster roster, and the CLI or website for model installation and loading.

Cluster targeting and readiness#

Send X-Cluster-ID to scope inventory or inference to a specific cluster:

X-Cluster-ID: cl_example

Discover cluster IDs from clusters[].cluster_id in model inventory or the authenticated CLI. Without this header, inventory aggregates your account's clusters. Inference filters eligible online nodes by the loaded model and required protocol or media capabilities, then selects among them. It does not select a fixed first cluster.

Installed is not loaded. A model must be resident and ready on an eligible node before inference. Cluster connectivity also does not establish that a model executes across multiple machines.

GET /v1/models#

Returns models advertised by online devices, including installed models available to load and currently loaded models.

FieldMeaning
idPublic model identifier. May be a display name, or a stable selector when names collide.
selectorOptional stable selector for a particular installed model.
display_name / canonical_nameOptional descriptive names.
object, created, owned_byModel metadata; owned_by is actual.
loadedWhether the model is loaded anywhere in the returned inventory. Missing or false is not readiness.
clustersCluster availability, with cluster_id and optional per-cluster loaded.

Use a returned id or selector as the request's model. For pinned inference, check loading on that specific cluster instead of using the aggregate flag.

Illustrative response:

{
  "object": "list",
  "data": [{
    "id": "Gemma 4 12B IT",
    "object": "model",
    "created": 1700000000,
    "owned_by": "actual",
    "selector": "mdl_a81e2d366803a648",
    "display_name": "Gemma 4 12B IT",
    "loaded": true,
    "clusters": [
      {"cluster_id": "cl_loaded", "loaded": true},
      {"cluster_id": "cl_installed", "loaded": false}
    ]
  }]
}

POST /v1/chat/completions#

Provide a model identifier and an array of messages. This example uses an illustrative model selector; replace it with inventory output.

{
  "model": "mdl_a81e2d366803a648",
  "messages": [
    {"role": "system", "content": "Answer concisely."},
    {"role": "user", "content": "Say hello."}
  ],
  "max_tokens": 128,
  "stream": false
}

Common optional sampling fields include temperature, top_p, frequency_penalty, presence_penalty, and stop. Supported fields, bounds, defaults, reasoning levels, and tool behavior depend on the serving engine and model; the website's Chat defaults are not universal API defaults.

A successful non-streaming response includes choices[].message, finish_reason, and usage when available:

{
  "id": "chatcmpl-example",
  "object": "chat.completion",
  "created": 1700000000,
  "model": "mdl_a81e2d366803a648",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "Hello!"},
    "finish_reason": "stop"
  }],
  "usage": {"prompt_tokens": 12, "completion_tokens": 3, "total_tokens": 15}
}

With stream: true, consume SSE data events and accumulate deltas. A simplified text stream is:

data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello!"},"finish_reason":null}]}

data: [DONE]

Handle tool-call deltas and stream errors separately from text. A chunk arriving is not proof that generation completed successfully.

POST /v1/completions#

The legacy text-completion route accepts model, prompt, and optional generation controls such as max_tokens, temperature, top_p, stop, and stream. Prefer Chat Completions or Responses for new conversational integrations.

{
  "model": "mdl_a81e2d366803a648",
  "prompt": "Complete this sentence: The computer",
  "max_tokens": 64,
  "stream": false
}

Non-streaming completions return text in choices[].text, with finish reason and usage where available.

POST /v1/responses#

Provide model and input, which can be text or an array of input items. Common fields include:

FieldUse
instructionsSystem instructions.
max_output_tokensOutput token limit.
streamEnable SSE.
tools, tool_choiceTool definitions and selection, when supported by the model and engine.
reasoningReasoning settings, including model-supported effort levels.
previous_response_idContinue a previous response when it is still available.
temperature, top_p, top_k, min_pSampling controls supported by the runtime.
frequency_penalty, presence_penalty, repetition_penaltyPenalty controls.
seed, stop, metadataAdditional generation and request metadata.

Do not assume every runtime supports every optional field. A previous response may become unavailable after restarting or changing the serving computer. Keep your conversation history so the application can recover if continuation fails.

Example with a function tool:

{
  "model": "mdl_a81e2d366803a648",
  "input": "What is the temperature in Boston?",
  "tools": [{
    "type": "function",
    "name": "get_temperature",
    "description": "Get the current temperature for a city",
    "parameters": {
      "type": "object",
      "properties": {"city": {"type": "string"}},
      "required": ["city"]
    }
  }],
  "max_output_tokens": 256
}

Your application validates and executes any requested function, then returns a function_call_output using the returned call_id. The inference endpoint does not execute your application functions for you.

A text response has an output array containing message items:

{
  "id": "resp-example",
  "object": "response",
  "status": "completed",
  "output": [{
    "type": "message",
    "role": "assistant",
    "content": [{"type": "output_text", "text": "Hello!"}]
  }],
  "error": null
}

Streaming uses named events. Check the terminal event and response status.

EventMeaning
response.createdResponse created.
response.in_progressProcessing started.
response.output_item.addedNew output item.
response.output_text.deltaIncremental text.
response.output_text.doneText complete.
response.function_call_arguments.deltaIncremental tool arguments.
response.function_call_arguments.doneTool arguments complete.
response.output_item.doneOutput item complete.
response.completedResponse complete.
response.failedResponse failed.

POST /v1/messages#

The Anthropic-compatible route accepts model, messages, max_tokens, and optional system, tools, and stream. Use the same bearer authentication and cluster header as the other public routes.

{
  "model": "mdl_a81e2d366803a648",
  "max_tokens": 128,
  "messages": [{"role": "user", "content": "Say hello."}],
  "stream": false
}

Responses use the Anthropic-style message shape; streaming uses Anthropic-style SSE events. Client compatibility still depends on the selected model's capabilities and the application's provider configuration.

Images, audio, and request size#

Use a model and serving runtime that advertise the required input capabilities. Console Chat currently accepts text only. See Vision & Audio for application and file guidance.

The current standard request limit is 16 MiB for the complete prepared body, including encoded attachments. A smaller source file can become a larger encoded request.

Errors#

Inspect both HTTP status and content type. Errors are not uniformly JSON. For example, a missing bearer credential can produce a 401 plain-text body:

missing_or_malformed_credential

A no-node condition can produce 503 with plain text no node for user. Other failures return structured JSON. Preserve the received diagnostic instead of assuming legacy error codes such as invalid_api_key or user_offline.

StatusTypical interpretation
400Invalid parameters or unsupported request shape.
401 / 403Authentication or authorization failed.
404Unknown route or unavailable target; inspect the body.
413Prepared request body exceeds the limit.
429Rate limit or capacity policy; honor retry information when supplied.
503No eligible serving node or temporary service unavailability.

Refresh inventory after disconnects or model changes. Retry transient failures with backoff; correct credentials, unsupported inputs, and invalid routes before retrying.