AI
AI agents
Run an AI agent inside a conversation. The agent reads the customer's messages, retrieves knowledge you've approved, calls tools you define to act in your systems, and either replies or hands off to a person — all while you just exchange messages over the API. The same agent works across SMS, chat, email, and voice, and every run is logged so you can see exactly what it read, decided, and did.
How a turn works
Each time a customer message arrives, the agent performs a run. A run is a short, auditable loop:
- Understand — interpret the message in the context of the full conversation and any customer details you've attached.
- Retrieve — pull relevant passages from the agent's knowledge sources when an answer needs grounding.
- Act — call one or more tools to look things up or make changes in your systems. Tool calls can chain: the result of one can inform the next.
- Respond or hand off — produce a grounded reply, or, when a guardrail or low confidence is hit, return a handoff to a human with a summary.
You never orchestrate these steps yourself. You post a message and read back a completed run; AtomMatrix runs the loop, enforces the guardrails, and delivers the reply on the conversation's channel.
Concepts & object IDs
| Object | Prefix | Description |
|---|---|---|
| Agent | agt_ | A configured assistant: instructions, knowledge sources, tools, guardrails, and handoff rules. You build agents in the console and reference them by agent_id. |
| Conversation | cnv_ | An ongoing thread with one customer, spanning one or more channels. |
| Message | msg_ | A single turn, authored by the customer, the agent, or a human. |
| Run | run_ | Everything the agent did in response to a customer message — retrieval, tool calls, the reply, or a handoff. |
| Tool | — | A named action the agent can take by calling an HTTPS endpoint you own. |
The agent object
Agents are authored and versioned in the console; the API exposes them read-only so your code can discover which agents exist and what they're capable of before starting a conversation.
{
"id": "agt_support_en",
"name": "Support — English",
"status": "live",
"channels": ["sms", "chat", "email"],
"locales": ["en", "es", "fr"],
"knowledge_sources": ["kb_help_center", "kb_shipping_policy"],
"tools": ["get_order", "cancel_order", "issue_refund"],
"guardrails": {
"requires_approval": ["issue_refund"],
"restricted_topics": ["legal_advice"]
},
"handoff": { "default_queue": "support", "on_low_confidence": true },
"version": 7,
"updated_at": "2026-06-30T12:04:11Z"
}
live to start a conversation. A draft agent returns 422 agent_not_live. Publish from the console when you're ready.Create a conversation
| Parameter | Description |
|---|---|
agent_id stringREQUIRED | The agent to run. Must be live. |
channel stringOPTIONAL | sms, chat, email, or voice. Determines how replies are delivered. Defaults to chat (reply returned in the API response only). |
customer objectOPTIONAL | Known details the agent can use: id, name, phone, email, locale. |
context objectOPTIONAL | Structured facts to seed the run — e.g. { "plan": "pro", "account_status": "active" }. The agent may use these without a tool call. |
tags arrayOPTIONAL | Labels for routing and analytics, e.g. ["billing", "vip"]. |
metadata objectOPTIONAL | Your own key–value context, echoed back on every object. |
curl https://api.atommatrix.ai/v1/conversations \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_support_en",
"channel": "chat",
"customer": { "id": "cus_8842", "name": "Jordan", "locale": "en" },
"context": { "plan": "pro", "account_status": "active" },
"tags": ["support"]
}'
Retrieve & list conversations
List supports filtering by agent_id, status (active, handed_off, closed), channel, tag, and a created range, plus cursor pagination.
GET /v1/conversations?agent_id=agt_support_en&status=handed_off&limit=20
Send a message & get a run
Post the customer's message. The response includes the agent's run — the reply it generated and any tools it called. If the conversation is on a live channel like SMS or voice, the reply is also delivered on that channel automatically.
| Parameter | Description |
|---|---|
role stringREQUIRED | customer for an inbound message. Use human to inject an agent-desk reply into the transcript without triggering a run. |
content stringREQUIRED | The message text. For inbound media, use attachments. |
attachments arrayOPTIONAL | URLs of images or documents the agent should consider (e.g. a photo of a damaged item). |
trigger_run booleanOPTIONAL | Defaults to true. Set false to append a message without asking the agent to respond. |
curl https://api.atommatrix.ai/v1/conversations/cnv_01HN9V.../messages \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "role": "customer", "content": "Where is my order 10842?" }'
Response
{
"id": "msg_01HN9W...",
"role": "customer",
"content": "Where is my order 10842?",
"run": {
"id": "run_01HN9W...",
"status": "completed",
"confidence": 0.93,
"retrieval": [
{ "source": "kb_shipping_policy", "title": "Delivery windows", "score": 0.81 }
],
"tool_calls": [
{ "tool": "get_order", "arguments": { "order_id": "10842" }, "result_status": "ok", "latency_ms": 240 }
],
"reply": {
"role": "agent",
"content": "Your order 10842 shipped yesterday and is out for delivery today. Want the tracking link?"
},
"handoff": null,
"usage": { "input_tokens": 812, "output_tokens": 96 }
}
}
List messages
Return the full transcript in order, including customer, agent, and human turns. Use this to render a live chat panel or rebuild context after a handoff. Results are cursor-paginated, oldest first by default; pass order=desc for newest first.
The run object
A run is created for you whenever a message triggers the agent, but it's also a first-class resource you can retrieve — useful for auditing, for polling an asynchronous run, or for showing "the agent is working" states in your UI.
| Field | Description |
|---|---|
status string | See the lifecycle below. |
confidence number | 0–1 estimate of how well-grounded the reply is. Low values can trigger handoff. |
retrieval array | Knowledge passages the agent used, with source and relevance score. Powers citations. |
tool_calls array | Each tool the agent invoked, its arguments, result_status, and latency. |
reply object | The agent's message, or null if the run ended in a handoff. |
handoff object | Present when the run escalated to a human. |
usage object | Token counts for the run, for cost attribution. |
Run status lifecycle
| Status | Meaning |
|---|---|
queued | Accepted, not yet started. |
in_progress | The agent is understanding, retrieving, or calling tools. |
requires_action | Waiting on an asynchronous tool (see async tools) or a human approval. |
completed | Finished with a reply and/or a handoff. |
failed | An unrecoverable error occurred; inspect error. No customer reply was sent. |
expired | A required action wasn't fulfilled in time and the run was abandoned. |
Streaming a run
For chat UIs that show the reply as it's written, open the run as a stream of server-sent events instead of waiting for the full response. You'll receive incremental events as the agent retrieves, calls tools, and composes text.
event: run.started
data: { "run_id": "run_01HN9W..." }
event: tool.called
data: { "tool": "get_order", "arguments": { "order_id": "10842" } }
event: reply.delta
data: { "text": "Your order 10842 shipped " }
event: reply.delta
data: { "text": "yesterday and is out for delivery today." }
event: run.completed
data: { "run_id": "run_01HN9W...", "status": "completed" }
Tools
Tools are how an agent does more than talk. Each tool has a name, a description the agent uses to decide when to call it, a JSON Schema for its arguments, and an HTTPS endpoint you own. When the agent chooses a tool, AtomMatrix validates the arguments against your schema, calls your endpoint, waits for your JSON response, and lets the agent continue with the result.
Defining a tool
Tools are configured on the agent in the console. A definition looks like this:
{
"name": "get_order",
"description": "Look up the status and shipping details of an order by its ID.",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "The customer's order number." }
},
"required": ["order_id"]
},
"endpoint": "https://api.yourco.com/tools/get_order",
"timeout_ms": 5000
}
The call contract
AtomMatrix signs every tool request with the same HMAC signature scheme used for webhooks, so you can verify the call really came from us. Respond with 200 and a JSON body; the agent receives your body as the tool result.
// AtomMatrix calls your tool endpoint:
POST https://api.yourco.com/tools/get_order
Atom-Signature: t=1751894400,v1=5f1e...
{
"tool": "get_order",
"arguments": { "order_id": "10842" },
"conversation_id": "cnv_01HN9V...",
"run_id": "run_01HN9W..."
}
// Your endpoint responds:
{ "status": "shipped", "eta": "2026-07-07", "tracking": "1Z999AA10123456784" }
Returning an error
If the action can't be completed, return a non-2xx status or an error object. The agent sees the failure and adapts — it might apologize, try a different path, or hand off — rather than inventing a result.
// 404 — the agent will tell the customer the order wasn't found
{ "error": { "code": "not_found", "message": "No order matches 10842." } }
timeout_ms, the call is marked timed_out and the agent proceeds without it. We retry idempotent tool calls once on network errors; make your handlers idempotent using the run_id.Long-running (async) tools
For actions that can't finish in a few seconds — a manual review, a batch job — respond with 202 and { "status": "pending" }. The run moves to requires_action. Submit the result later and the agent resumes:
{ "tool_call_id": "call_01HN9X...", "result": { "approved": true } }
Knowledge & retrieval
An agent answers from the knowledge sources attached to it — your help center, policies, product data, or documents you've indexed — not from a generic guess. When retrieval is used, the passages appear in the run's retrieval array with a relevance score, so you can surface citations and audit where an answer came from.
"retrieval": [
{
"source": "kb_shipping_policy",
"title": "Delivery windows",
"snippet": "Standard orders ship within one business day...",
"score": 0.81
}
]
If nothing relevant is found and the agent isn't confident, it says so or hands off rather than fabricating an answer. You manage sources and re-indexing in the console; the agent always retrieves from the latest published version.
Guardrails
Guardrails are limits set on the agent that the platform enforces on every run — you don't have to police them in your own code:
- Restricted topics — subjects the agent must decline or route to a human (e.g. legal or medical advice).
- Approval gates — tools that require a human to approve before they execute, optionally above a value threshold.
- Data redaction — patterns (card numbers, national IDs) stripped from transcripts and logs.
- Confidence floor — below a set confidence, the agent hands off instead of answering.
When a guardrail stops an action, the run records why in the handoff or a guardrail note, so nothing happens silently.
Human handoff
When the agent hits a guardrail, low confidence, or an explicit customer request, the run returns a handoff object instead of (or alongside) a reply, and a conversation.handoff webhook fires so your agent desk can pick it up with the full transcript and a written summary attached.
"handoff": {
"reason": "requires_approval",
"queue": "billing",
"summary": "Customer requests a $240 refund on order 10842 (shipped, undamaged).",
"suggested_reply": "I can request that refund for you — a specialist will confirm shortly."
}
A human replies by posting a message with role: "human". To return control to the agent once the case is resolved, close the handoff:
{ "note": "Refund approved and processed. Agent may resume." }
Channel behavior
| Channel | How replies are delivered |
|---|---|
chat | Returned in the API response (and stream). You render them in your app. |
sms | Sent automatically over Messaging to the customer's number; long replies are segmented. |
email | Sent over Email, threaded on the conversation's subject. |
voice | Spoken by an AI Voice session; replies are synthesized and tool calls run mid-call. |
Because context lives on the conversation, a thread can start on SMS and continue by email without the customer repeating themselves.
Events
Subscribe via Webhooks to drive your own UI and systems:
| Event | Fires when |
|---|---|
conversation.message | A new inbound, agent, or human message is added. |
run.completed | A run finishes — inspect the reply, tool calls, and usage. |
run.requires_action | A run is waiting on an async tool result or an approval. |
conversation.handoff | The agent escalated to a human queue. |
conversation.closed | The conversation was resolved and closed. |
Common errors
| Code | Meaning |
|---|---|
agent_not_live 422 | The referenced agent is a draft. Publish it first. |
channel_not_supported 422 | The agent isn't configured for the requested channel. |
conversation_closed 409 | You posted to a closed conversation. Start a new one. |
tool_result_expired 409 | An async tool result arrived after the run expired. |
See Errors & limits for the shared error object, status codes, and rate limits.