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

ParameterDescription
slugThe decision's slug, shown in the editor and the Deploy tab — for example lead-qualification.

Query parameters

ParameterDescription
includeOptional. probabilities adds the full distribution of every question to the response. Comma-separated; other values are ignored.

Headers

HeaderRequiredDescription
AuthorizationYesBearer dcs_live_… or Bearer dcs_test_… — see Authentication.
Content-TypeYesapplication/json.
Idempotency-KeyNo1–128 characters from A–Z a–z 0–9 . _ : -. Replays the stored response of a successful call for 24 hours — see Idempotency.
X-Request-IDNoYour 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 } }
FieldTypeDescription
stateobject, string or arrayThe 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
  }
}
FieldTypeDescription
decision_idstringThe decision's ID (dec_…).
execution_idstringThe ID of this run (exec_…), searchable in Executions and GET /v1/executions.
schemastringThe decision's slug.
versionintegerThe deployed version that answered.
resultobjectOne 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.
confidenceobjectOne number from 0 to 1 per question — see Confidence.
scoresobjectOnly for decisions with score questions: the weighted level of each one, counted from 1 — see Weighted levels.
compositesobjectOnly for decisions with composites: the value of each one.
actionstringcontinue, block, escalate or fallback — see Policies and actions.
action_reasonobjectWhy the action was chosen (below).
probabilitiesobjectOnly with ?include=probabilities: the distribution of every question.
destinationsarrayOnly for decisions with destinations: one entry per destination that fired, in schema order — [] when none did. See below.
metrics.latency_msintegerTime the run took inside Dcision — state validation, engine call, composites and policies. LLM and agent destinations, which run after it, aren't included.
metrics.enginestringjev.
metrics.modelstringThe 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_usdnumberEstimated engine cost of the call: input tokens × the model's price.
metrics.input_tokensintegerInput tokens of the engine call — what Jev bills.
metrics.output_tokensintegerOutput tokens of the engine call, for observability: Jev doesn't bill them.

action_reason

typeExtra fieldsMeaning
policyrulePolicy rule number rule matched (0-based).
other_optionquestionThat choice question answered other; the fallback action applies.
low_confidencequestion, confidence, minConfidenceThat question's confidence was below its minimum; the fallback action applies.
engine_errorcodeThe 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.

Every kind of 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" }
]
FieldTypeDescription
keystringThe destination's key in the decision.
typestringreply, llm, agent, workflow, webhook, http or function.
statusstringWhat happened (below). Fixed replies and functions that fired have no status.
textstringreply: the message. llm: the model's answer.
buttonsarrayreply: the button labels.
modelstringllm: the model that answered, as reported by the provider.
usage.input_tokensintegerllm: input tokens counted by the provider — billed by the provider, not by Dcision.
usage.output_tokensintegerllm: output tokens counted by the provider.
replystring or nullagent in sync mode: the agent's reply, or null when its answer had no reply field.
responseanyagent in sync mode: the agent's whole answer, or null when it is larger than 16,000 characters.
errorstringfailed entries: why the LLM or agent call failed.
delivery_idstringqueued entries: the dlv_… ID sent with every attempt — deduplicate on it.
functionstringfunction: the handler your code should call.
paramsobjectfunction: the arguments of the call.
reasonstringskipped entries: why the destination didn't run (below).
paramstringskipped with missing_param: the required param that had no value.
statusTypesMeaning
completedllm, agentThe model or the agent answered — the caller waited for it.
failedllm, agentThe call failed; the decision is still valid. See error.
queuedwebhook, http, workflow, agentDcision delivers it in the background, with retries — see Delivery and retries.
skippedallThe destination matched but didn't run.
reasonMeaning
missing_paramA param marked required had no value.
limitMore than 10 destinations matched, or more than 3 that make the caller wait (LLM answers and sync agents).
unavailableThe 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:

StatusCodes
400INVALID_REQUEST — malformed JSON, a body that isn't an object, an invalid Idempotency-Key
401INVALID_API_KEY
402CREDITS_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
404DECISION_NOT_FOUND
409DECISION_NOT_DEPLOYED, DECISION_DISABLED, IDEMPOTENCY_CONFLICT — with Retry-After: 1 while a request with the same key is still running
413PAYLOAD_TOO_LARGE — body larger than 128 KB
422INVALID_STATE — including a state over the engine's token budget — and INVALID_SCHEMA
424ENGINE_NOT_CONFIGURED
429RATE_LIMITED
500INTERNAL_ERROR
502ENGINE_AUTH_FAILED, ENGINE_INVALID_REQUEST, ENGINE_ERROR
503ENGINE_RATE_LIMITED, ENGINE_UNAVAILABLE
504ENGINE_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:

  1. API key → 401 INVALID_API_KEY.
  2. Rate limit of the workspace → 429 RATE_LIMITED. From here on, responses carry the X-RateLimit-* headers.
  3. Slug format → 404 DECISION_NOT_FOUND; body → 400 INVALID_REQUEST.
  4. 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_CONFLICT with Retry-After: 1. Otherwise the key is reserved for this request — and freed again if any later step fails.
  5. Decision exists, is enabled and has a deployed version → 404 / 409.
  6. 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.
  7. State validation, then the engine's token budget for the state and the questions → 422 INVALID_STATE.
  8. Engine call, with retries inside timeoutMs → 424 / 502 / 503 / 504 on failure, or the fallback action with onEngineError: "fallback".
  9. Composites and policies → action and action_reason.
  10. Destinations → replies and functions are returned, LLM and sync agent calls are awaited, deliveries are queued — then 200 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.

On this page