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. 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.
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}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 the chat, message history, and tool invocations.
Response
| Field | Type | Description |
|---|---|---|
id | string | Chat ID |
status | string | busy, idle, awaiting_input, or completed |
chat_messages | array | Messages with content, role, and optional tool_invocations |
output | object | Structured result from the agent's finish tool (when set) |
context | object | Per-chat context values |
1{2 "id": "chat_abc123",3 "status": "awaiting_input",4 "chat_messages": [5 {6 "role": "assistant",7 "tool_invocations": [8 {9 "id": "ti_abc123",10 "status": "awaiting_approval",11 "function": { "name": "delete_file", "arguments": { "path": "/tmp/old.txt" } }12 }13 ]14 }15 ]16}output is populated when the agent calls the internal finish tool. See Structured Output.
For live updates, use Stream chat messages or GET /chats/:id/status for status-only polling.
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.
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().
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 |
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
GET /chats/:id, find invocations withstatus: 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.
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.sh", "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.