Run a decision
POST /v1/decisions/{slug} — path, query, headers, request body, every response field and the errors, with examples in cURL, JavaScript and Python.
POST https://api.dcision.io/v1/decisions/{slug}Runs the active version of the decision slug on a state and returns one typed answer per question, the confidence of each answer, the weighted levels and composites, the action chosen by the decision's policies and — for decisions with destinations — what happens next. All the questions are answered in one engine call.
Request
Path parameters
| Parameter | Description |
|---|---|
slug | The decision's slug, shown in the editor and the Deploy tab — for example lead-qualification. |
Query parameters
| Parameter | Description |
|---|---|
include | Optional. probabilities adds the full distribution of every question to the response. Comma-separated; other values are ignored. |
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer dcs_live_… or Bearer dcs_test_… — see Authentication. |
Content-Type | Yes | application/json. |
Idempotency-Key | No | 1–128 characters from A–Z a–z 0–9 . _ : -. Replays the stored response of a successful call for 24 hours — see Idempotency. |
X-Request-ID | No | Your correlation ID (same character set). Echoed in the response and stored with the execution. |
Body
{ "state": { "message": "We need pricing for 500 users and want to start next month.", "company_size": 500 } }| Field | Type | Description |
|---|---|---|
state | object, string or array | The input of the decision: an object for an object state, a non-empty string for a text state, a non-empty array (up to 500 items) for a list state. Validated against the decision's state schema; undeclared fields of an object are accepted and forwarded to the engine. |
Other top-level fields are ignored. The whole body must fit in 128 KB, and the state must fit the engine's token budget together with the questions.
Examples
curl -X POST https://api.dcision.io/v1/decisions/lead-qualification \
-H "Authorization: Bearer $DCISION_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: lead-8421" \
-d '{
"state": {
"message": "We need pricing for 500 users and want to start next month.",
"company_size": 500,
"source": "website"
}
}'A decision with a text state takes a string:
curl -X POST https://api.dcision.io/v1/decisions/spam-detection \
-H "Authorization: Bearer $DCISION_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "state": "Congratulations! You won a $1000 gift card, click here to claim now." }'A decision with a list state takes an array — here a chat transcript, for a decision named chat-triage whose state is a list of strings:
curl -X POST https://api.dcision.io/v1/decisions/chat-triage \
-H "Authorization: Bearer $DCISION_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "state": ["Hi!", "Can I get a quote for 50 seats?"] }'Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Request-ID: req_Qm8vT2kLx9Pw4Rz7Nc1B
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1791195060{
"decision_id": "dec_3fKq9ZtW1mXcV7bN2pLa",
"execution_id": "exec_8HsT2kQw9ZyR4vMn1cXe",
"schema": "lead-qualification",
"version": 1,
"result": {
"purchase_intent": 0.9412,
"priority": "high",
"route": "sales"
},
"confidence": {
"purchase_intent": 0.8824,
"priority": 0.69,
"route": 0.85
},
"scores": { "priority": 2.81 },
"composites": { "lead_score": 0.8414 },
"action": "continue",
"action_reason": { "type": "default" },
"metrics": {
"latency_ms": 412,
"engine": "jev",
"model": "jev-1.13.0",
"estimated_cost_usd": 0.00001575,
"input_tokens": 375,
"output_tokens": 36
}
}| Field | Type | Description |
|---|---|---|
decision_id | string | The decision's ID (dec_…). |
execution_id | string | The ID of this run (exec_…), searchable in Executions and GET /v1/executions. |
schema | string | The decision's slug. |
version | integer | The deployed version that answered. |
result | object | One answer per question, in question order: an option (choice), the label of the most likely level (score) or the probability of yes from 0 to 1 (probability). Empty after an engine-error fallback. |
confidence | object | One number from 0 to 1 per question — see Confidence. |
scores | object | Only for decisions with score questions: the weighted level of each one, counted from 1 — see Weighted levels. |
composites | object | Only for decisions with composites: the value of each one. |
action | string | continue, block, escalate or fallback — see Policies and actions. |
action_reason | object | Why the action was chosen (below). |
probabilities | object | Only with ?include=probabilities: the distribution of every question. |
destinations | array | Only for decisions with destinations: one entry per destination that fired, in schema order — [] when none did. See below. |
metrics.latency_ms | integer | Time the run took inside Dcision — state validation, engine call, composites and policies. LLM and agent destinations, which run after it, aren't included. |
metrics.engine | string | jev. |
metrics.model | string | The exact model version that answered, for example jev-1.13.0 — also when the workspace uses an alias such as jev-latest. |
metrics.estimated_cost_usd | number | Estimated engine cost of the call: input tokens × the model's price. |
metrics.input_tokens | integer | Input tokens of the engine call — what Jev bills. |
metrics.output_tokens | integer | Output tokens of the engine call, for observability: Jev doesn't bill them. |
action_reason
type | Extra fields | Meaning |
|---|---|---|
policy | rule | Policy rule number rule matched (0-based). |
other_option | question | That choice question answered other; the fallback action applies. |
low_confidence | question, confidence, minConfidence | That question's confidence was below its minimum; the fallback action applies. |
engine_error | code | The engine failed with the error code and the decision's onEngineError is fallback: the fallback action applies, result is empty and the call isn't billed. |
default | — | Nothing matched: continue. |
Question keys are yours
result, confidence, scores and probabilities are keyed by your question keys, and composites by your composite keys, so they never collide with the top-level fields: the Spam Detection template's result.action (its action question: allow, review or block) is distinct from the top-level action chosen by its policies.
destinations
Present when the decision has destinations. Each entry describes one destination that fired — what it returned, or what Dcision queued — and destinations never fail the call: a problem shows up on its entry.
[
{
"key": "sales_reply",
"type": "reply",
"text": "Thanks! Someone from our sales team will contact you today.",
"buttons": ["Book a demo", "See pricing"]
},
{
"key": "sales_llm",
"type": "llm",
"status": "completed",
"text": "Thanks for reaching out! Someone from our sales team will contact you today to talk about your 500 users.",
"model": "openai/gpt-5-mini",
"usage": { "input_tokens": 61, "output_tokens": 27 }
},
{
"key": "sales_agent",
"type": "agent",
"status": "completed",
"reply": "I booked a demo for tomorrow at 10:00. You'll get an invitation by e-mail.",
"response": { "reply": "I booked a demo for tomorrow at 10:00. You'll get an invitation by e-mail.", "booking_id": "bk_5521" }
},
{ "key": "sales_summary", "type": "llm", "status": "failed", "error": "No answer from OpenRouter within 20 s." },
{ "key": "sales_crm", "type": "workflow", "status": "queued", "delivery_id": "dlv_8kJx2mQp4LzN7vR1tY6w" },
{ "key": "notify_sales", "type": "webhook", "status": "queued", "delivery_id": "dlv_Q2w9Lm4Xc7Rv1Tz8Wn3B" },
{ "key": "handoff", "type": "agent", "status": "queued", "delivery_id": "dlv_Hq2Lm9Xc4Rv1Tz8Wn3Bk" },
{ "key": "assign_owner", "type": "function", "function": "assignToSales", "params": { "email": "ana@acme.com", "route": "sales" } },
{ "key": "create_ticket", "type": "http", "status": "skipped", "reason": "missing_param", "param": "ticket_id" }
]| Field | Type | Description |
|---|---|---|
key | string | The destination's key in the decision. |
type | string | reply, llm, agent, workflow, webhook, http or function. |
status | string | What happened (below). Fixed replies and functions that fired have no status. |
text | string | reply: the message. llm: the model's answer. |
buttons | array | reply: the button labels. |
model | string | llm: the model that answered, as reported by the provider. |
usage.input_tokens | integer | llm: input tokens counted by the provider — billed by the provider, not by Dcision. |
usage.output_tokens | integer | llm: output tokens counted by the provider. |
reply | string or null | agent in sync mode: the agent's reply, or null when its answer had no reply field. |
response | any | agent in sync mode: the agent's whole answer, or null when it is larger than 16,000 characters. |
error | string | failed entries: why the LLM or agent call failed. |
delivery_id | string | queued entries: the dlv_… ID sent with every attempt — deduplicate on it. |
function | string | function: the handler your code should call. |
params | object | function: the arguments of the call. |
reason | string | skipped entries: why the destination didn't run (below). |
param | string | skipped with missing_param: the required param that had no value. |
status | Types | Meaning |
|---|---|---|
completed | llm, agent | The model or the agent answered — the caller waited for it. |
failed | llm, agent | The call failed; the decision is still valid. See error. |
queued | webhook, http, workflow, agent | Dcision delivers it in the background, with retries — see Delivery and retries. |
skipped | all | The destination matched but didn't run. |
reason | Meaning |
|---|---|
missing_param | A param marked required had no value. |
limit | More than 10 destinations matched, or more than 3 that make the caller wait (LLM answers and sync agents). |
unavailable | The delivery couldn't be queued. |
In the Playground, entries are previews — "status": "preview" with the request or prompt that would be sent — unless you run the destinations for real. See Testing destinations.
Errors
Errors use the standard format. The ones this endpoint returns:
| Status | Codes |
|---|---|
| 400 | INVALID_REQUEST — malformed JSON, a body that isn't an object, an invalid Idempotency-Key |
| 401 | INVALID_API_KEY |
| 402 | CREDITS_EXHAUSTED — the included volume is used up and there are no credits left; SPEND_CAP_REACHED — the cycle's spend cap is reached; QUOTA_EXCEEDED — the included volume of a plan with a hard cap is used up |
| 404 | DECISION_NOT_FOUND |
| 409 | DECISION_NOT_DEPLOYED, DECISION_DISABLED, IDEMPOTENCY_CONFLICT — with Retry-After: 1 while a request with the same key is still running |
| 413 | PAYLOAD_TOO_LARGE — body larger than 128 KB |
| 422 | INVALID_STATE — including a state over the engine's token budget — and INVALID_SCHEMA |
| 424 | ENGINE_NOT_CONFIGURED |
| 429 | RATE_LIMITED |
| 500 | INTERNAL_ERROR |
| 502 | ENGINE_AUTH_FAILED, ENGINE_INVALID_REQUEST, ENGINE_ERROR |
| 503 | ENGINE_RATE_LIMITED, ENGINE_UNAVAILABLE |
| 504 | ENGINE_TIMEOUT |
Engine errors come after Dcision's own retries, within the decision's timeoutMs. When the decision's onEngineError is fallback, the 502, 503 and 504 engine errors become a 200 with the fallback action and action_reason.type = "engine_error" — see Decision settings.
Order of checks
When several things are wrong, the first failing step decides the error:
- API key →
401 INVALID_API_KEY. - Rate limit of the workspace →
429 RATE_LIMITED. From here on, responses carry theX-RateLimit-*headers. - Slug format →
404 DECISION_NOT_FOUND; body →400 INVALID_REQUEST. - Idempotency: a stored response for the same key and request is replayed now; the same key with another request →
409 IDEMPOTENCY_CONFLICT; the same request still running →409 IDEMPOTENCY_CONFLICTwithRetry-After: 1. Otherwise the key is reserved for this request — and freed again if any later step fails. - Decision exists, is enabled and has a deployed version →
404/409. - Included volume and credits: past the plan's included decisions, the available credits →
402 CREDITS_EXHAUSTED, then the spend cap →402 SPEND_CAP_REACHED; a plan with a hard cap →402 QUOTA_EXCEEDED. - State validation, then the engine's token budget for the state and the questions →
422 INVALID_STATE. - Engine call, with retries inside
timeoutMs→424/502/503/504on failure, or the fallback action withonEngineError: "fallback". - Composites and policies →
actionandaction_reason. - Destinations → replies and functions are returned, LLM and
syncagent calls are awaited, deliveries are queued — then200 OK.
Steps 7 to 10 are recorded as executions, failures included. Only a fresh 200 from step 10 is billed — not errors, not replays, not engine-error fallbacks. See Plans, quotas and billing.
Authentication
Authenticate with Bearer API keys (dcs_live_ and dcs_test_) — create, store, rotate and revoke them — and know where they work.
List and get decisions
GET /v1/decisions and GET /v1/decisions/{slug} — the decisions of an API key's workspace and the contract of each one's active version, with an example state.