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.

modeWhat happensIn 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.
asyncA 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
  }
}
FieldTypeDefaultDescription
urlstring—Your endpoint: https://…, or {{secrets.NAME}} holding the whole URL. Up to 2,048 characters, with variables.
headersarray[]Up to 20 { "name", "value" } pairs; values up to 2,048 characters, with variables and secrets.
instructionsstring—The route's prompt, sent as instructions: up to 8,000 characters, with variables, no secrets.
toolsarray[]Up to 50 tool names your agent knows — letters, digits and _ . : -, up to 64 characters each.
modestringsyncsync or async.
replyPathstring—sync only: where the reply is in the agent's JSON answer, as a dot path such as data.answer.
timeoutMsinteger200001,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
Body
{
  "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
  }
}
FieldDescription
inputThe whole state, as the caller sent it.
instructionsThe route's prompt with its variables filled in; null when the destination has none.
toolsThe route's tool names.
paramsThe destination's params.
decisionWhat 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:

  1. the field at replyPath, when you set one;
  2. otherwise the first of these top-level fields that exists: reply, output_text, output, text, message, content;
  3. or the whole body, when the answer isn't JSON.

A string is used as-is; other values are returned as JSON text.

In the response
{
  "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.

On this page