Agents

Chat with AI agents via REST. For SDK helpers (sendMessage, run, streaming), see the Agent SDK.

Most integrations use POST /agents/run — it creates a chat when needed, sends the user message, and returns assistant output. For a pure chat-first flow (create a chat, then send messages separately), use POST /chats followed by POST /chats/:id/messages — the same pattern the workspace and React AgentChatProvider use for new conversations (client.agent().sendMessage() still uses POST /agents/run). Use the tool endpoints below for client tools, widgets, and human-in-the-loop (HIL) approval.


POST /agents/run

Send a message to a template or ad-hoc agent. Creates a new chat when chat_id is omitted.

Request

FieldTypeRequiredDescription
chat_idstringNoExisting chat ID (omit to start a new chat)
agentstringNoTemplate agent ref (namespace/name@version)
agent_configobjectNoAd-hoc agent config (tools, system prompt, core app)
agent_namestringNoDisplay name for ad-hoc chats
contextobjectNoPer-chat context for call-tool URL templates (only when starting a new chat)
inputobjectYesMessage payload (see below)
streambooleanNoIf true, response is SSE instead of JSON

input fields:

FieldTypeDescription
textstringUser message text
attachmentsarrayFile refs (uri, filename, content_type) from Files API
rolestringUsually "user"

Template agent (new chat):

json
1{2  "agent": "myteam/support-agent@latest",3  "context": {4    "project_id": "proj_456"5  },6  "input": {7    "text": "Summarize open tickets"8  }9}

Continue chat:

json
1{2  "chat_id": "chat_abc123",3  "input": {4    "text": "What about priority ones?"5  }6}

Response

With X-API-Version: 2 (recommended; sent automatically by the SDKs and belt/infsh CLIs), the response body is the DTO directly:

json
1{2  "user_message": { "id": "msg_...", "chat_id": "chat_abc123", "role": "user", "content": [...] },3  "assistant_message": { "id": "msg_...", "chat_id": "chat_abc123", "role": "assistant", "content": [...], "tool_invocations": [...] }4}

Without the version header, the same payload is nested under data in a { "success": true, "data": { ... } } wrapper. See REST overview — API version.

Set stream: true to receive message updates over SSE. See Stream chat updates. Streaming applies when a new agent run starts — if the chat already has an active run, the message is auto-queued instead (no SSE stream).

Queuing while a run is active

When chat_id points to a chat with a non-terminal agent run (status: busy), POST /agents/run and POST /agents/message do not start a new run. The user message is queued for the agent's next turn — the same behavior as Send chat message.

Each chat has at most one active agent run. Rapid follow-up messages are queued rather than starting a parallel run or stopping the in-progress response — use Stop chat to cancel explicitly.

The response returns immediately with only user_message (status: "queued"). assistant_message is omitted. Poll or stream the chat to see when the agent picks up the message and generates a reply.

json
1{2  "user_message": {3    "id": "msg_...",4    "chat_id": "chat_abc123",5    "role": "user",6    "status": "queued",7    "content": [{ "type": "text", "text": "Focus on P0 tickets only" }]8  }9}

Use POST /chats/:id/messages when you only need to send a message without other run parameters. See Chat message status.


Send message (alternate)

POST /agents/message or POST /agents/messages

Same request body as Run agent, using agent_id / agent_version_id instead of agent when referencing a stored template:

json
1{2  "agent_id": "agent_abc123",3  "agent_version_id": "ver_xyz789",4  "input": { "text": "Hello" }5}

Create chat

POST /chats

Create a chat, optionally assigning a template agent before the first message. Pair with Send chat message for a two-step new conversation: create the chat, then post the first user message.

Use POST /agents/run when you want one call that creates the chat, sends the message, and returns assistant output — recommended for scripts, ad-hoc agent_config, per-chat context, attachments, and A2A integrations. Use the chat-first flow when you need a chat ID before sending (for example to open a stream or navigate in a SPA).

Requires conversations:write scope.

Request

FieldTypeRequiredDescription
agentstringNoTemplate agent ref (namespace/name or namespace/name@version)
namestringNoDisplay name for the chat

When agent is provided, the API resolves the template and pins agent_id and agent_version_id on the chat (same version pinning as Run agent). Omit agent to create an empty chat and assign an agent later with Set agent.

Response

Returns the chat DTO (same shape as Get chat).

Errors

HTTP statusCodeWhen
400invalid_requestInvalid request body
404not_foundAgent ref not found

Example — two-step new conversation

bash
1# 1. Create chat with agent2curl -X POST https://api.inference.sh/chats \3  -H "Authorization: Bearer inf_your_key" \4  -H "Content-Type: application/json" \5  -H "X-API-Version: 2" \6  -d '{"agent": "myteam/support-agent@latest", "name": "Support"}'78# 2. Send first message (starts the agent run)9curl -X POST https://api.inference.sh/chats/chat_abc123/messages \10  -H "Authorization: Bearer inf_your_key" \11  -H "Content-Type: application/json" \12  -H "X-API-Version: 2" \13  -d '{"message": "Summarize open tickets"}'

JavaScript SDK: AgentChatProvider / useAgentActions().sendMessage() on a template agent with no bound chat ID calls POST /chats, then POST /chats/{id}/messages; call reset() before the next message to start a new chat. client.agent().sendMessage() still uses POST /agents/run.


List chats

GET /chats or POST /chats/list

Cursor-paginated list of chats. POST /chats/list accepts a JSON body with limit, cursor, direction, and filters (for example status in busy, awaiting_input).


Get chat

GET /chats/:id

Returns chat metadata — status, structured output, context, and active run. Does not include message history or tool invocations.

Response

FieldTypeDescription
idstringChat ID
statusstringbusy, idle, awaiting_input, or completed
outputobjectStructured result from the agent's finish tool (when set)
contextobjectPer-chat context values
active_runobjectCurrent agent run, when one is in progress
json
1{2  "id": "chat_abc123",3  "status": "awaiting_input",4  "output": null,5  "context": { "project_id": "proj_123" },6  "active_run": {7    "id": "run_abc123",8    "state": "awaiting_input"9  }10}

output is populated when the agent calls the internal finish tool. See Structured Output.

For message history and tool invocations, use List chat messages. For live updates, use Stream chat updates or GET /chats/:id/status for status-only polling.


List chat messages

GET /chats/:id/messages or POST /chats/:id/messages/list

Returns cursor-paginated messages for a chat. Requires the conversations:read scope (same as Get chat). The chat must exist and belong to your team.

Returns every message in the chat. Access is verified when loading the chat ID — message rows are not filtered by creator or team.

Default sort is order descending (newest first) — load older pages as users scroll up. POST /chats/:id/messages/list accepts the same fields in a JSON body (cursor, limit, direction, sort, filters).

Query / body fieldTypeDescription
limitintegerPage size (default 50). Pass -1 to return up to 10,000 messages in one response — see Cursor pagination.
cursorstringOpaque cursor from next_cursor (ignored when limit=-1)
directionstringforward or backward (ignored when limit=-1)
sortarraySort orders (default [{ "field": "order", "dir": "desc" }])
filtersarrayFilter expressions (same pattern as other list endpoints)

Uses the same cursor pagination shape as List skills. Each items entry is a ChatMessageDTO with role, status, content, order, and optional tool_invocations.

Example (newest page):

bash
1curl "https://api.inference.sh/chats/chat_abc123/messages?limit=50" \2  -H "Authorization: Bearer inf_your_key" \3  -H "X-API-Version: 2"

Example response:

json
1{2  "items": [3    {4      "id": "msg_xyz789",5      "chat_id": "chat_abc123",6      "role": "assistant",7      "status": "ready",8      "order": 4,9      "content": [{ "type": "text", "text": "Here is the summary." }],10      "tool_invocations": []11    }12  ],13  "next_cursor": "msg_abc456",14  "has_next": true,15  "has_previous": false,16  "items_per_page": 50,17  "total_items": 1218}

Pass cursor from next_cursor to fetch older messages.

Example (full transcript — no pagination):

bash
1curl "https://api.inference.sh/chats/chat_abc123/messages?limit=-1" \2  -H "Authorization: Bearer inf_your_key" \3  -H "X-API-Version: 2"

When limit=-1, has_next is false and up to 10,000 matching messages are returned in items (sorted and filtered as requested). Prefer cursor pagination for UI scroll-back; use limit=-1 for exports or debugging on chats with fewer than 10,000 messages.

JavaScript SDK (@inferencesh/sdk agent module, v0.6.40+): AgentChatProvider loads one page of history using the server default limit (50) when GET /chats/:id omits chat_messages. v0.6.41+: useAgentActions().loadOlderMessages() fetches the next page via cursor and prepends deduplicated messages to useAgentChat().messages; hasOlderMessages is true when another page exists. For custom UIs outside AgentChatProvider, call this endpoint directly with cursor or limit=-1.

For live updates while the agent runs, use Stream chat updates instead of polling this endpoint.


Get chat trace

GET /chats/:id/trace

Returns the execution graph for an agent chat: nodes (assistant turns, tool invocations, sync barriers), edges (dependencies), and step counts by status. Requires the conversations:read scope (same as other chat endpoints).

Use this to debug agent runs programmatically — see which tools ran, how long each step took, and where failures occurred. The workspace shows the same trace in the chat UI.

Response

FieldTypeDescription
graph_idstringGraph identifier (same as the chat ID)
nodesarrayExecution steps (type, label, status, resource_id, started_at, completed_at, duration_ms, …)
edgesarrayDependencies between nodes (from_node, to_node, type)
total_stepsnumberTotal nodes in the graph
completed_stepsnumberSteps that finished successfully
running_stepsnumberSteps still in progress
failed_stepsnumberSteps that failed

Example:

bash
1curl https://api.inference.sh/chats/chat_abc123/trace \2  -H "Authorization: Bearer inf_your_key" \3  -H "X-API-Version: 2"

JavaScript SDK: client.chats.getTrace('chat_abc123').

Observability · Stream chat updates


Chat status values

StatusMeaning
busyAgent is working — LLM generation, or automated tool execution (apps, sub-agents, hooks, client tools, MCP)
idleAgent finished; chat can accept new messages
awaiting_inputA human must act on this chat: HIL tool approval or an interactive widget
completedTerminal state (archived chat)

Client tools and hooks keep the chat in busy while they execute. Use chat.status === "awaiting_input" when you need a human on the conversation; use tool invocation status for per-tool progress.


Chat message status

Each entry in chat_messages has its own status, separate from the chat-level status above.

StatusMeaning
pendingAssistant message still being generated (LLM stream in progress)
queuedUser message queued while the agent is still running — via Send chat message or auto-queue on run/message — waiting for the next turn
readyComplete — included in LLM context on subsequent turns
failedGeneration failed
cancelledCancelled — via Cancel message or when the chat run is stopped

Messages from POST /agents/run or POST /agents/message start as status: ready when no run is active. When a run is already active, they are auto-queued with status: queued instead. Messages sent via POST /chats/:id/messages (or auto-queue) transition to ready when the agent begins its next turn, before context is built.

Poll List chat messages or stream chat updates to detect queued messages — the stream emits chat_messages events as status transitions from queued to ready. To withdraw a queued message before the agent consumes it, use Cancel message.

SDK types: Import ChatMessageStatusQueued from @inferencesh/sdk (JavaScript) or ChatMessageStatus.QUEUED from inferencesh.types (Python) when comparing message status values. JavaScript SDK (v0.6.36+): client.chats.cancelMessage(messageId) and useAgentActions().cancelMessage(messageId) with AgentChatProvider cancel queued messages. Python SDK does not expose cancelMessage yet.


Stop chat

POST /chats/:id/stop

Stop the current agent run and cancel pending tool invocations.

JavaScript SDK: client.chats.stop('chat_abc123') — or agent.stopChat() when using client.agent().


Set agent

POST /chats/:id/agent

Change which template agent handles an existing chat. Message history and per-chat context are preserved; the next run uses the new agent's tools and system prompt.

Agent switching is explicit — call this endpoint to change agents. Sending a message via POST /agents/run, POST /agents/message, or POST /chats/:id/messages does not change which agent owns the chat, and there is no per-message agent binding.

Requires conversations:write scope. Not yet exposed in the JavaScript or Python SDKs — call the REST endpoint directly.

Request

FieldTypeRequiredDescription
agentstringYesTemplate agent ref (namespace/name or namespace/name@version)

Response

Returns the updated chat DTO (same shape as Get chat), with agent_id, agent, and agent_version_id set to the resolved agent.

Errors

HTTP statusCodeWhen
400invalid_requestMissing or empty agent
404not_foundChat or agent not found

Example

bash
1curl -X POST https://api.inference.sh/chats/chat_abc123/agent \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -H "X-API-Version: 2" \5  -d '{"agent": "myteam/escalation-agent@latest"}'

Send chat message

POST /chats/:id/messages

Send a user message to an existing chat. If the agent is idle, this starts a new run. If a run is already active (status: busy), the message is queued and picked up on the agent's next turn — the same auto-queue behavior as POST /agents/run with an existing chat_id.

Use this endpoint when you only need to send a message without other run parameters (for example a follow-up while tools are executing, or continuing a chat without specifying agent config again).

Legacy alias: POST /chats/:id/steer — same request and response. Prefer POST /chats/:id/messages.

Requires conversations:write scope. JavaScript SDK: used internally by AgentChatProvider / useAgentActions().sendMessage() (client.agent().sendMessage() still uses POST /agents/run). Python SDK: call the REST endpoint directly.

Request

json
1{2  "message": "Also prioritize tickets from enterprise customers"3}
FieldTypeRequiredDescription
messagestringYesUser message text

Response

Returns the created message DTO with role: "user". When a run is active, status is "queued" and agent_run_id is set; when idle, a new run starts and the message is "ready". Queued messages transition to ready when the agent begins its next turn, before context is built. See Chat message status.

Errors

HTTP statusCodeWhen
400invalid_requestMissing or empty message
404not_foundChat does not exist

Example

bash
1curl -X POST https://api.inference.sh/chats/chat_abc123/messages \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -H "X-API-Version: 2" \5  -d '{"message": "Focus on P0 tickets only"}'

Cancel message

POST /chats/messages/:messageId/cancel

Cancel a queued user message before the agent picks it up — for example a message from Send chat message or an auto-queued message you want to withdraw. The message status becomes cancelled and content is cleared.

Only messages with status: "queued" can be cancelled. Messages that are ready, failed, or already cancelled return 409 not_cancellable.

Requires conversations:write scope. JavaScript SDK: client.chats.cancelMessage('msg_abc123') — or useAgentActions().cancelMessage(messageId) with AgentChatProvider. Python SDK: call the REST endpoint directly.

Response

Returns the updated message DTO with status: "cancelled". The chat stream emits a chat_messages event when the cancellation succeeds.

Errors

HTTP statusCodeWhen
400missing_idmessageId path parameter is missing
404not_foundMessage does not exist
409not_cancellableMessage is not queued

Example

bash
1curl -X POST https://api.inference.sh/chats/messages/msg_abc123/cancel \2  -H "Authorization: Bearer inf_your_key" \3  -H "X-API-Version: 2"

Tool invocations

Tool calls on assistant messages expose tool_invocations. Each invocation has an id used by the endpoints below.

Tool invocation status

StatusMeaning
pendingQueued
in_progressExecuting — apps, sub-agents, client tools, hooks, MCP
awaiting_inputInteractive widget or form needs human input
awaiting_approvalHIL approval needed before execution
completedDone
failedError
cancelledCancelled

Status transitions

The API enforces valid transitions server-side. Terminal states (completed, failed, cancelled) cannot change again. A typical successful path is pendingin_progresscompleted, with optional stops at awaiting_approval or awaiting_input. Skipping required steps is rejected — for example pending cannot go directly to awaiting_input.

If a chat run is cancelled while a tool is executing, the invocation moves to failed and any late completion attempt from the tool is rejected.

Tool invocation updates are delivered on the chat stream as chat_messages events (the server republishes the parent message when an invocation changes).


Submit tool result

POST /tools/:toolInvocationId

Submit a result for client tools (in_progress) or interactive widgets (awaiting_input).

Request

json
1{2  "result": "{\"temperature\": 72}"3}

For widget actions, JSON-serialize { "action": {...}, "form_data": {...} } into result, or use the deprecated POST /tools/:id/widget endpoint.

The JavaScript and Python SDKs call this via submitToolResult / submit_tool_result. See Client Tools.


Approve tool (HIL)

POST /tools/:toolInvocationId/invoke

Approve a tool with status: awaiting_approval and execute it. Returns 400 invalid_state if the invocation is not awaiting approval.

bash
1curl -X POST https://api.inference.sh/tools/ti_abc123/invoke \2  -H "Authorization: Bearer inf_your_key"

Approval settings · Human-in-the-loop


Reject tool (HIL)

POST /tools/:toolInvocationId/reject

Reject a pending approval. The agent continues with the rejection reason.

Request

json
1{2  "reason": "Not authorized to delete files"3}

reason is optional.


Always allow tool

POST /chats/:chatId/tools/:toolInvocationId/always-allow

Approve the current invocation and add the tool to this chat's always-allowed list.

Request

json
1{2  "tool_name": "delete_file"3}

Human-in-the-loop workflow

  1. Run an agent with tools marked require_approval (see Approval settings).
  2. Poll or stream the chat until status is awaiting_input.
  3. In List chat messages (or the chat stream), find invocations with status: awaiting_approval.
  4. Call POST /tools/:id/invoke to approve or POST /tools/:id/reject to reject.
  5. Poll or stream again until the agent reaches idle or completed.

List chats with status in busy or awaiting_input to find conversations needing action. The belt CLI wraps this flow with belt chat pending, belt chat approve, and belt chat reject — see CLI Setup.


Interrupt gates

When an agent run needs external input — tool approval, widget input, missing credentials, or a lifecycle hook gate — the platform creates an interrupt record. Interrupts provide a unified gate model alongside the tool-invocation endpoints above.

Lifecycle hooks — gate handlers for hook-driven interrupts

GET /chats/:id includes active_run when a run is in progress. When the run is interrupted, active_run.state is input_required or auth_required and active_run may include:

FieldDescription
interrupt_reasonWhy the run paused (see Interrupt reasons)
interrupt_tool_idRelated tool invocation ID (when applicable)
interrupt_metaAdditional context (JSON)

List pending interrupts for a run

GET /agent-runs/:runId/interrupts

Returns pending interrupts for the given agent run. Requires the conversations:read scope.

Example:

bash
1curl https://api.inference.sh/agent-runs/ar_abc123/interrupts \2  -H "Authorization: Bearer inf_your_key"

JavaScript SDK: client.agents.listRunInterrupts('ar_abc123')

CLI: belt interrupt list ar_abc123 — see CLI setup — Interrupt.

Python SDK: use the REST endpoint with typed responses — import InterruptDTO from inferencesh.types (see Python SDK — Interrupt gates). Client helpers are not yet exposed in Python.

Resolve interrupt

POST /interrupts/:id/resolve

Resolve a pending interrupt with allow or deny. Requires the conversations:write scope. When all pending interrupts for the run are resolved, the agent run resumes automatically.

Request

json
1{2  "decision": "allow",3  "data": { "reviewer": "security-bot" }4}

decision must be "allow" or "deny". Optional data (any JSON object) is stored on the interrupt as resolved_data and returned in the response — useful for audit metadata on hook gate resolutions.

Example:

bash
1curl -X POST https://api.inference.sh/interrupts/int_abc123/resolve \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{"decision": "allow"}'

JavaScript SDK: client.agents.resolveInterrupt('int_abc123', 'allow') — or agent.resolveInterrupt() when using client.agent().

CLI: belt interrupt resolve int_abc123 allow (or deny).

Python SDK: POST /interrupts/:id/resolve with {"decision": "allow"} or "deny" — type the response as InterruptDTO from inferencesh.types.

For tool approval interrupts (reason: tool_approval), resolving with allow is equivalent to POST /tools/:id/invoke; deny is equivalent to POST /tools/:id/reject.

To automate interrupt resolution from webhooks or schedules, create a trigger with action: resolve_interrupt — see Triggers API — resolve_interrupt action.

InterruptDTO fields

FieldTypeDescription
idstringInterrupt ID
run_idstringAgent run ID
chat_idstringChat ID
reasonstringWhy the run paused
sourcestringGate origin (runtime component)
resource_idstringRelated resource ID (for example a tool invocation)
resource_typestringtool_invocation (tool approval gates) or hook_event (lifecycle hook gates)
statusstringpending, resolved, expired, or cancelled
resolutionstringallow or deny (set when resolved)
resolved_dataobjectOptional JSON from the resolve request data field
expires_atstringRFC3339 expiry (if set)
metaobjectAdditional context (JSON). For hook_gate, contains the hook payload.

Interrupt reasons

ReasonWhen
tool_approvalTool requires human approval before execution
client_toolClient-side tool must run in your app or browser
widgetInteractive widget needs user input
authMissing integration credentials
confirmationAgent is waiting for explicit user confirmation
hook_gateLifecycle hook gate awaiting an external decision

Approval settings · Lifecycle hooks


List hook events

GET /agents/hook-events

Returns the canonical list of lifecycle hook events with metadata. Requires the agents:read scope.

Response:

json
1[2  {3    "event": "agent.tool_call",4    "description": "Fires before a tool is executed",5    "can_gate": true6  },7  {8    "event": "agent.tool_result",9    "description": "Fires after a tool completes",10    "can_gate": false11  }12]

Use can_gate to choose between a synchronous webhook (decision: deny) and a gate handler (type: "gate") that creates an interrupt. See Lifecycle hooks.


cURL example

bash
1# Start or continue a chat2curl -X POST https://api.inference.sh/agents/run \3  -H "Authorization: Bearer inf_your_key" \4  -H "Content-Type: application/json" \5  -d '{6    "agent": "myteam/support-agent@latest",7    "input": { "text": "Hello!" }8  }'910# Get chat (includes tool invocations)11curl https://api.inference.sh/chats/chat_abc123 \12  -H "Authorization: Bearer inf_your_key"1314# Approve a tool awaiting HIL15curl -X POST https://api.inference.sh/tools/ti_abc123/invoke \16  -H "Authorization: Bearer inf_your_key"

Messaging channel webhooks

Connect external chat platforms so users can talk to an agent where they already work. Inbound platform events create agent messages; agent replies are delivered back through the same channel.

Receive events

POST /agents/{id}/channel/{provider}

ParameterDescription
idAgent ID
providerIntegration provider slug (currently slack)

Legacy route (Slack only): POST /agents/{id}/slack — same behavior as provider=slack.

No API key is required. The platform verifies the request (for Slack, using your app's Signing Secret from the Slack integration). URL verification challenges (for example Slack url_verification) are answered automatically.

Example (Slack event subscription Request URL):

code
1https://api.inference.sh/agents/agent_abc123/channel/slack

On a valid inbound message, the API acknowledges immediately (200 with {"status":"ok"}) and processes the message asynchronously. Thread history is fetched when the channel supports it, so the agent has prior context.

Supported providers are registered per deployment; check integration guides for availability. See Chatting with Agents for the workspace workflow.


Get A2A agent card

GET /agents/{id}/card
GET /agents/{namespace}/{name}/card

Returns an Agent2Agent (A2A) protocol agent card for agent discovery and marketplace listings (for example GCP AI Agents Producer Portal). Requires agents:read.

The response is raw A2A JSON — not wrapped in the usual { "success": true, "data": … } envelope. Resolve the agent by ID or by namespace/name ref.

bash
1curl https://api.inference.sh/agents/myteam/support-agent/card \2  -H "Authorization: Bearer inf_your_key" \3  -H "X-API-Version: 2"

Example response:

json
1{2  "name": "myteam/support-agent",3  "description": "Customer support assistant",4  "version": "1.0.0",5  "url": "https://app.inference.sh/agents/myteam/support-agent",6  "provider": { "name": "inference shell", "url": "https://inference.sh" },7  "capabilities": { "streaming": true, "pushNotifications": false },8  "defaultInputModes": ["text/plain"],9  "defaultOutputModes": ["text/plain", "application/json"],10  "skills": [11    { "id": "search-tickets", "name": "Search tickets", "tags": ["app"] }12  ],13  "securitySchemes": [{ "scheme": "bearer" }]14}

Skills are derived from the agent's current version tools (apps, sub-agents, MCP servers, HTTP hooks) and attached knowledge skills. In the workspace, open an agent and use a2a card on the detail header to copy this JSON to the clipboard.

JavaScript SDK (@inferencesh/sdk ≥ v0.6.12): client.agents.getA2ACard('agent_abc123') — returns the same raw card object. See JavaScript SDK — Agent SDK.


Create or update agent template

POST /agents

Creates a saved agent template, or adds a new version when an agent with the same name already exists in your team's namespace. Requires the agents:write scope.

This mirrors app deploy semantics: the namespace is assigned server-side from your team username, so redeploying only requires the agent name and a version payload. You do not need to look up the agent ID first, and the namespace field in the request is optional.

FieldTypeRequiredDescription
namestringYesAgent name (unique per team namespace)
namespacestringNoOptional — server uses your team username
idstringNoAgent ID — include to create a new version of a known agent (alternative to name-based upsert)
versionobjectNoVersion config (system_prompt, core_app, tools, skills, context, output_schema, …)

When name matches an existing agent, the API creates a new version and sets it as current (same outcome as POST /agents/{id} with a version payload). Omitting visibility on redeploy preserves the existing visibility setting.

Example — first deploy:

bash
1curl -X POST https://api.inference.sh/agents \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -H "X-API-Version: 2" \5  -d '{6    "name": "support-agent",7    "version": {8      "description": "Customer support assistant",9      "system_prompt": "You help users with billing questions.",10      "core_app": { "ref": "openrouter/claude-sonnet-46" }11    }12  }'

Example — redeploy same name (creates new version):

Send the same request shape with updated version fields — no agent ID lookup required.

To update by ID instead (for example when you already have agent_abc123), use POST /agents/{id} with a version object. To change visibility or store images without a new version, use POST /agents/{id}/visibility or include only agent-level fields in POST /agents/{id}.

CLI: belt agent deploy <path> and belt agent create both use this endpoint. See CLI setup — Agents.


Delete agent template

DELETE /agents/{id}

Permanently deletes a saved agent template (workspace definition). Requires the agents:write scope. This does not delete chat history from past runs.

bash
1curl -X DELETE https://api.inference.sh/agents/agent_abc123 \2  -H "Authorization: Bearer inf_your_key"

CLI equivalent: belt agent delete <ref> (accepts agent ID or namespace/name). See CLI setup — Agents.


we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.