Actions
An action is a tool the chatbot can decide to use mid-conversation. Without them a bot can only talk about what it has read. With them it can check an order, book a slot, capture a lead, or pull in a human.
The bot chooses when to call an action from its description, the same way it chooses what to say. Your job is to describe it well and handle the request.
Types
| Type | What it does |
|---|---|
http_request | Calls an endpoint you own and feeds the response back into the answer |
mcp | Calls a tool exposed by an MCP server |
handoff | Flags the conversation for a human |
collect_info | Gathers contact details and records a lead |
notify | Sends a notification |
button | Renders a call-to-action in the transcript |
GET /v1/actions/types returns the live list with the fields each one takes.
Registering an HTTP action
The one you will use most: the bot calls your endpoint and uses the answer.
POST/v1/actions
curl -X POST https://api.agentency.com/v1/actions \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: action-order-lookup-1" \
-d '{
"chatbot_id": 1,
"name": "lookup_order",
"description": "Look up the status of a customer order by its order number.",
"type": "http_request",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/{order_number}",
"headers": { "Authorization": "Bearer <YOUR_SERVICE_TOKEN>" }
},
"parameters": {
"order_number": {
"type": "string",
"description": "The customer order number, e.g. ORD-1234",
"required": true
}
}
}'The model decides whether to call an action by reading `description` and the parameter descriptions. "Look up the status of a customer order by its order number" gets used correctly. "Order endpoint" does not. Write them for a colleague who has never seen your system.
Testing before you ship
POST/v1/actions/{id}/test
Runs the action for real with arguments you supply, so you can confirm the wiring without going through a conversation.
curl -X POST https://api.agentency.com/v1/actions/1/test \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{"arguments": {"order_number": "ORD-1234"}}'It hits your live endpoint with real side effects. Point it at staging, or use an order number that is safe to touch.
Watching runs
Every invocation is recorded: the arguments, a summary of the request and response, the duration, and whether it worked.
GET/v1/actions/{id}/runs
{
"object": "list",
"data": [
{
"object": "action_run",
"id": 1,
"chatbot_id": 1,
"type": "http_request",
"status": "success",
"duration_ms": 214,
"error_code": null
}
]
}status is success, failed, skipped, timed_out, pending, or
confirmation_pending. Runs across a whole bot are at
GET /v1/chatbots/{id}/action_runs.
Get told instead of asking
Actions are the point where the product reaches into your systems, so "did my tool run, and did it work" is the question you cannot answer any other way.
`action.run.succeeded` and `action.run.failed` fire whenever a run reaches a terminal state, from both inline and queued execution. An action quietly failing for a week is a real failure mode; this is how you avoid it.
Handoff
The handoff action flags a conversation for a human. When it fires, the
conversation gets handoff_requested: true and a reason, and
conversation.handoff.requested is emitted once.
Wire that event to wherever your team actually looks. A handoff nobody sees is worse than no handoff, because the visitor has been told someone is coming.
MCP tools
If you already expose tools over MCP, point at the server and discover them rather than transcribing definitions by hand:
POST/v1/actions/mcp/discover
It fetches the server's tool list so you can register the ones you want.
Secrets
Action configuration can hold credentials for your own systems. Two things follow from that:
- Configuration is stored encrypted and never returned in full. Reads show enough to identify the action, not enough to reuse the credential.
- Provider error text is redacted before it reaches a run record, so a failing upstream cannot leak a token into your logs.
Use a dedicated, narrowly-scoped credential for each action, and rotate it on your side without touching the action.
Common mistakes
- A vague description. The commonest reason an action "never fires".
- No timeout on your side. A slow endpoint makes the whole answer slow. Return quickly, even if that means returning "still working".
- Assuming it ran. Check the run status, or subscribe to the events.
- Testing against production.
POST /v1/actions/{id}/testreally calls it.
What's next
- Events — the action events and everything else.
- Conversations — see actions in context.
- Security — credential handling.