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.
Run agent (recommended)
POST /agents/run
Send a message to a template or ad-hoc agent. Creates a new chat when chat_id is omitted.
Request
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | string | No | Existing chat ID (omit to start a new chat) |
agent | string | No | Template agent ref (namespace/name@version) |
agent_config | object | No | Ad-hoc agent config (tools, system prompt, core app) |
agent_name | string | No | Display name for ad-hoc chats |
context | object | No | Per-chat context for call-tool URL templates (only when starting a new chat) |
input | object | Yes | Message payload (see below) |
stream | boolean | No | If true, response is SSE instead of JSON |
input fields:
| Field | Type | Description |
|---|---|---|
text | string | User message text |
attachments | array | File refs (uri, filename, content_type) from Files API |
role | string | Usually "user" |
Template agent (new chat):
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:
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:
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.
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:
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
| Field | Type | Required | Description |
|---|---|---|---|
agent | string | No | Template agent ref (namespace/name or namespace/name@version) |
name | string | No | Display 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 status | Code | When |
|---|---|---|
| 400 | invalid_request | Invalid request body |
| 404 | not_found | Agent ref not found |
Example — two-step new conversation
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
| Field | Type | Description |
|---|---|---|
id | string | Chat ID |
status | string | busy, idle, awaiting_input, or completed |
output | object | Structured result from the agent's finish tool (when set) |
context | object | Per-chat context values |
active_run | object | Current agent run, when one is in progress |
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 field | Type | Description |
|---|---|---|
limit | integer | Page size (default 50). Pass -1 to return up to 10,000 messages in one response — see Cursor pagination. |
cursor | string | Opaque cursor from next_cursor (ignored when limit=-1) |
direction | string | forward or backward (ignored when limit=-1) |
sort | array | Sort orders (default [{ "field": "order", "dir": "desc" }]) |
filters | array | Filter 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):
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:
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):
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
| Field | Type | Description |
|---|---|---|
graph_id | string | Graph identifier (same as the chat ID) |
nodes | array | Execution steps (type, label, status, resource_id, started_at, completed_at, duration_ms, …) |
edges | array | Dependencies between nodes (from_node, to_node, type) |
total_steps | number | Total nodes in the graph |
completed_steps | number | Steps that finished successfully |
running_steps | number | Steps still in progress |
failed_steps | number | Steps that failed |
Example:
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
| Status | Meaning |
|---|---|
busy | Agent is working — LLM generation, or automated tool execution (apps, sub-agents, hooks, client tools, MCP) |
idle | Agent finished; chat can accept new messages |
awaiting_input | A human must act on this chat: HIL tool approval or an interactive widget |
completed | Terminal 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.
| Status | Meaning |
|---|---|
pending | Assistant message still being generated (LLM stream in progress) |
queued | User message queued while the agent is still running — via Send chat message or auto-queue on run/message — waiting for the next turn |
ready | Complete — included in LLM context on subsequent turns |
failed | Generation failed |
cancelled | Cancelled — 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
| Field | Type | Required | Description |
|---|---|---|---|
agent | string | Yes | Template 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 status | Code | When |
|---|---|---|
| 400 | invalid_request | Missing or empty agent |
| 404 | not_found | Chat or agent not found |
Example
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
1{2 "message": "Also prioritize tickets from enterprise customers"3}| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User 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 status | Code | When |
|---|---|---|
| 400 | invalid_request | Missing or empty message |
| 404 | not_found | Chat does not exist |
Example
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 status | Code | When |
|---|---|---|
| 400 | missing_id | messageId path parameter is missing |
| 404 | not_found | Message does not exist |
| 409 | not_cancellable | Message is not queued |
Example
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
| Status | Meaning |
|---|---|
pending | Queued |
in_progress | Executing — apps, sub-agents, client tools, hooks, MCP |
awaiting_input | Interactive widget or form needs human input |
awaiting_approval | HIL approval needed before execution |
completed | Done |
failed | Error |
cancelled | Cancelled |
Status transitions
The API enforces valid transitions server-side. Terminal states (completed, failed, cancelled) cannot change again. A typical successful path is pending → in_progress → completed, 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
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.
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
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
1{2 "tool_name": "delete_file"3}Human-in-the-loop workflow
- Run an agent with tools marked
require_approval(see Approval settings). - Poll or stream the chat until
statusisawaiting_input. - In List chat messages (or the chat stream), find invocations with
status: awaiting_approval. - Call
POST /tools/:id/invoketo approve orPOST /tools/:id/rejectto reject. - Poll or stream again until the agent reaches
idleorcompleted.
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:
| Field | Description |
|---|---|
interrupt_reason | Why the run paused (see Interrupt reasons) |
interrupt_tool_id | Related tool invocation ID (when applicable) |
interrupt_meta | Additional 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:
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
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:
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
| Field | Type | Description |
|---|---|---|
id | string | Interrupt ID |
run_id | string | Agent run ID |
chat_id | string | Chat ID |
reason | string | Why the run paused |
source | string | Gate origin (runtime component) |
resource_id | string | Related resource ID (for example a tool invocation) |
resource_type | string | tool_invocation (tool approval gates) or hook_event (lifecycle hook gates) |
status | string | pending, resolved, expired, or cancelled |
resolution | string | allow or deny (set when resolved) |
resolved_data | object | Optional JSON from the resolve request data field |
expires_at | string | RFC3339 expiry (if set) |
meta | object | Additional context (JSON). For hook_gate, contains the hook payload. |
Interrupt reasons
| Reason | When |
|---|---|
tool_approval | Tool requires human approval before execution |
client_tool | Client-side tool must run in your app or browser |
widget | Interactive widget needs user input |
auth | Missing integration credentials |
confirmation | Agent is waiting for explicit user confirmation |
hook_gate | Lifecycle 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:
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
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}
| Parameter | Description |
|---|---|
id | Agent ID |
provider | Integration 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):
1https://api.inference.sh/agents/agent_abc123/channel/slackOn 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.
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:
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.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Agent name (unique per team namespace) |
namespace | string | No | Optional — server uses your team username |
id | string | No | Agent ID — include to create a new version of a known agent (alternative to name-based upsert) |
version | object | No | Version 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:
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.
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.