Agents
Hand a result to your own agent endpoint with the route's instructions and tools — wait for its reply in the response, or hand off in the background with retries.
An agent destination calls an endpoint you run — your support agent, a LangGraph or Agents SDK service, an internal assistant — with the state, the route's instructions and the tools it may use on that route. Dcision decides which agent and with what brief; your agent does the work.
mode | What happens | In the response |
|---|---|---|
sync (default) | Dcision calls the agent while the caller waits and extracts its reply. | "status": "completed" with reply, or "status": "failed" with error. |
async | A hand-off: the request is queued and delivered in the background, with retries. | "status": "queued" with delivery_id. |
Configure it
{
"key": "shipping_agent",
"type": "agent",
"when": { "conditions": [{ "field": "department", "on": "output", "operator": "eq", "value": "shipping" }] },
"params": { "awb": { "from": "state.awb", "required": true } },
"agent": {
"url": "https://agents.example.com/support",
"headers": [{ "name": "Authorization", "value": "Bearer {{secrets.AGENT_TOKEN}}" }],
"instructions": "Help with the shipment {{params.awb}}. Confirm the address before rescheduling.",
"tools": ["track_package", "reschedule_delivery"],
"mode": "sync",
"replyPath": "data.answer",
"timeoutMs": 20000
}
}| Field | Type | Default | Description |
|---|---|---|---|
url | string | — | Your endpoint: https://…, or {{secrets.NAME}} holding the whole URL. Up to 2,048 characters, with variables. |
headers | array | [] | Up to 20 { "name", "value" } pairs; values up to 2,048 characters, with variables and secrets. |
instructions | string | — | The route's prompt, sent as instructions: up to 8,000 characters, with variables, no secrets. |
tools | array | [] | Up to 50 tool names your agent knows — letters, digits and _ . : -, up to 64 characters each. |
mode | string | sync | sync or async. |
replyPath | string | — | sync only: where the reply is in the agent's JSON answer, as a dot path such as data.answer. |
timeoutMs | integer | 20000 | 1,000 to 25,000 — sync only. |
The request your agent receives
POST /support HTTP/1.1
Host: agents.example.com
Content-Type: application/json
Authorization: Bearer agt_live_51Hq…
User-Agent: Dcision-Destinations/1.0 (+https://docs.dcision.io/destinations)
Dcision-Delivery: dlv_Hq2Lm9Xc4Rv1Tz8Wn3Bk
Dcision-Attempt: 1
Dcision-Event: decision.completed
Dcision-Signature: t=1759658400,v1=6f1c2b…
Idempotency-Key: dlv_Hq2Lm9Xc4Rv1Tz8Wn3Bk{
"input": { "message": "Where is my package? It was due yesterday.", "awb": "4471-2210" },
"instructions": "Help with the shipment 4471-2210. Confirm the address before rescheduling.",
"tools": ["track_package", "reschedule_delivery"],
"params": { "awb": "4471-2210" },
"decision": {
"decision": "support-desk",
"decision_id": "dec_7Wq2Lm9Xc4Rv1Tz8Wn3B",
"version": 2,
"execution_id": "exec_Vn4Kp8Wd2Lq6Tz1Xc9Bs",
"result": { "department": "shipping", "urgency": "medium" },
"confidence": { "department": 0.92, "urgency": 0.71 },
"action": "continue",
"action_reason": { "type": "default" },
"destination": "shipping_agent",
"delivery_id": "dlv_Hq2Lm9Xc4Rv1Tz8Wn3Bk",
"livemode": true
}
}| Field | Description |
|---|---|
input | The whole state, as the caller sent it. |
instructions | The route's prompt with its variables filled in; null when the destination has none. |
tools | The route's tool names. |
params | The destination's params. |
decision | What the decision answered — slug and IDs, version (null in the Playground), result, confidence, action, action_reason — plus the destination's key, the delivery_id and livemode (false for test keys and the Playground). |
The request is signed like a webhook: verify Dcision-Signature over the raw body with verifySignature — the body is an agent request, not a webhook event. In async mode, deduplicate on Idempotency-Key, the delivery ID, which stays the same on every attempt.
The answer Dcision expects
In sync mode, answer 2xx within timeoutMs. Dcision takes the reply from:
- the field at
replyPath, when you set one; - otherwise the first of these top-level fields that exists:
reply,output_text,output,text,message,content; - or the whole body, when the answer isn't JSON.
A string is used as-is; other values are returned as JSON text.
{
"key": "shipping_agent",
"type": "agent",
"status": "completed",
"reply": "Your package is at the local depot. I can deliver it tomorrow between 9 and 12 — shall I book it?",
"response": { "data": { "answer": "Your package is at the local depot. I can deliver it tomorrow between 9 and 12 — shall I book it?", "session": "s_9f2c" } }
}response is the agent's whole answer, parsed when it is JSON, and null when it is larger than 16,000 characters. reply is null when no reply field was found — set Reply field (replyPath) in the destination.
A sync call is made once: no retries, redirects aren't followed, and the answer is read up to 64 KB. Anything else becomes a failed entry, and the decision still answers:
{ "key": "shipping_agent", "type": "agent", "status": "failed", "error": "The agent answered 503. upstream overloaded" }Failures include a non-2xx answer (with the start of its body), a timeout, a network error, an address that isn't public and a missing secret.
The caller waits for sync agents, in parallel with LLM answers; at most 3 of them run per execution.
Hand-offs (async)
With "mode": "async", the same signed request is queued and delivered with the retry schedule of webhooks — six attempts over about seven hours, 10 seconds each. Only the status of your answer matters: answer 2xx once you've accepted the task, and continue the work on your side.
A minimal agent endpoint
import express from "express";
import { DcisionError, verifySignature } from "@dcision/sdk";
const app = express();
// The raw body: the signature covers the exact bytes Dcision sent.
app.post("/support", express.raw({ type: "application/json" }), async (req, res) => {
try {
await verifySignature(req.body, req.header("dcision-signature"), process.env.DCISION_WEBHOOK_SECRET!);
} catch (error) {
return res.sendStatus(error instanceof DcisionError ? 400 : 500);
}
const task = JSON.parse(req.body.toString("utf8"));
const reply = await supportAgent.run({
input: task.input,
instructions: task.instructions,
tools: task.tools,
conversationId: task.decision.execution_id,
});
res.json({ reply });
});The examples use the SDKs, which are built from source until they are published.
Data
An agent receives the whole state as input, plus the params you map. In the Playground, previews show the exact request — with secrets masked — without calling your agent.
LLM answers
Let a model answer for a route — Dcision calls OpenRouter or Vercel AI Gateway with your workspace's own key, the route's prompt as the system message and the state as input.
Workflows
Trigger n8n, Make, Zapier, Pipedream or any workflow URL with the route's variables as flat JSON fields, plus the decision under dcision — signed and retried in the background.