Skip to content
DecisionNodeDecisionNdedocs
  • Guides
  • API reference
  • Examples
  • Playground

start here

  • QuickstartGet startedGet an API key, send one request with three questions, and branch your code on the typed answers. Plain HTTPS, no SDK to install.
  • POST /v1/decideAPI referenceAnswer typed questions about a state and optional images. One request, one buffered JSON response, one answer per question.
  • QuestionsConceptsQuestions say what to decide. Each one has a type that fixes the shape of its answer: a choice from your options, a score on your scale, a…
  • ConfidenceConceptsProbabilities are calibrated per question type, so a threshold means what it says.
  • ImagesConceptsSend images and text in the same request. The model reads printed and handwritten text, amounts, dates, objects and layout, and answers…
  • Pricing and billingYou pay for input tokens only. Output is free because the model generates no text.
↑↓ moveopen6 suggestions
Get API keyGet API key
DecisionNodeDecisionNde

Get started

  • Introduction
  • Quickstart
  • With coding agents
  • Examples

Concepts

  • State
  • Questions
  • Choice
  • Score
  • Truth
  • Number
  • Images
  • Confidence
  • Determinism

Models

  • DecisionNode-1.0
  • DecisionNode-1.0 Flash
  • Limits

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loopscomingcoming soon

API reference

  • POST/v1/decide
  • POST/v1/sessionscomingcoming soon
  • GET/v1/models
  • Errors
  • Rate limits

Pricing and billing

  • Pricing and billing

Policies

  • Responsible use

Migrate

  • Coming from a Jev-shaped API
  • Benchmarks
  • Pricing
  • Playground
Get API key
  • Guides
  • API reference
  • Examples
  • Playground

Get started

  • Introduction
  • Quickstart
  • With coding agents
  • Examples

Concepts

  • State
  • Questions
  • Choice
  • Score
  • Truth
  • Number
  • Images
  • Confidence
  • Determinism

Models

  • DecisionNode-1.0
  • DecisionNode-1.0 Flash
  • Limits

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loopscomingcoming soon

API reference

  • POST/v1/decide
  • POST/v1/sessionscomingcoming soon
  • GET/v1/models
  • Errors
  • Rate limits

Pricing and billing

  • Pricing and billing

Policies

  • Responsible use

Migrate

  • Coming from a Jev-shaped API
  1. docs
  2. /
  3. API reference

Errors

Every error is a status code plus a JSON body with a detail field. A request succeeds for every question or fails as a whole. Retry 429 and 529, retry 402 once you have added credit, and fix the request for 400, 401, 403 and 422.

on this page5 sections
  1. Error bodies
  2. Over a limit: 400
  3. Out of credit: 402
  4. Refused by the safety check: 403
  5. Retrying
  • 400 Bad request

    Meaning
    Valid JSON, but outside a limit (too many options or levels, over 64k tokens), an unknown model or a bad image
    Retry?
    No
    What to do
    Fix the request; the message names the limit
  • 401 Unauthorized

    Meaning
    Missing or invalid API key
    Retry?
    No
    What to do
    Check the Authorization header and that the key is not revoked
  • 402 Out of credit

    Meaning
    The workspace's prepaid balance is zero. Not billed
    Retry?
    After top-up
    What to do
    Add credit in the console, or turn on auto reload, then retry
  • 403 Safety refusal

    Meaning
    Refused by the safety check: the request asks for a use the Acceptable Use Policy forbids. Rolling out in shadow mode
    Retry?
    No
    What to do
    Change what the request asks; see Responsible use
  • 422 Unprocessable

    Meaning
    The body failed validation
    Retry?
    No
    What to do
    Fix the fields listed in detail
  • 429 Too many requests

    Meaning
    Over your rate limit
    Retry?
    Yes
    What to do
    Wait Retry-After seconds, then retry
  • 529 Overloaded

    Meaning
    No capacity right now
    Retry?
    Yes
    What to do
    Wait Retry-After seconds, then retry with backoff and jitter
Statuses
StatusMeaningRetry?What to do
400 Bad requestValid JSON, but outside a limit (too many options or levels, over 64k tokens), an unknown model or a bad imageNoFix the request; the message names the limit
401 UnauthorizedMissing or invalid API keyNoCheck the Authorization header and that the key is not revoked
402 Out of creditThe workspace's prepaid balance is zero. Not billedAfter top-upAdd credit in the console, or turn on auto reload, then retry
403 Safety refusalRefused by the safety check: the request asks for a use the Acceptable Use Policy forbids. Rolling out in shadow modeNoChange what the request asks; see Responsible use
422 UnprocessableThe body failed validationNoFix the fields listed in detail
429 Too many requestsOver your rate limitYesWait Retry-After seconds, then retry
529 OverloadedNo capacity right nowYesWait Retry-After seconds, then retry with backoff and jitter

Error bodies#

detail is a list for 422. For 401, 402, 403, 429 and 529 it is an object with error_type and message. A 400 can take any of three shapes: an object with error_type and message, an object with only error_type (max_tokens_exceeded), or a plain string for a question's limits. Check its type before you branch on it.

A 422 lists every invalid field at once: loc is the path into your body (with the question's key and type), and input echoes what you sent there, so your own error message can point at the exact field.

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "questions", "route", "choice", "criteria"],
      "msg": "Field required",
      "input": { "type": "choice", "instructions": "Where should this go?" }
    }
  ]
}

Over a limit: 400#

A request that parses but asks for more than a model allows is refused before any model work, and nothing is charged. The body says which limit you crossed, so log it as it is. The limits themselves are on Limits.

  • More than 255 options

    Body
    {"detail": "Too many choices. Must have at most 255 choices."}
  • More than 10 score levels

    Body
    {"detail": "Too many score levels. Must have at most 10 levels."}
  • A number grid the first release does not serve (over 256 values, or a step below 1)

    Body
    The api_usage_error shape, with a message that names the grid. Do not depend on the exact message wording
  • Over 64k tokens

    Body
    {"detail": {"error_type": "max_tokens_exceeded"}}
  • Unknown model

    Body
    {"detail": {"error_type": "api_usage_error", "message": "Unknown model: <name>"}}
  • Bad image

    Body
    The api_usage_error shape, with a message that names the problem: undecodable, an unsupported type or over the size limit
What a request over a limit gets back
CaseBody
More than 255 options{"detail": "Too many choices. Must have at most 255 choices."}
More than 10 score levels{"detail": "Too many score levels. Must have at most 10 levels."}
A number grid the first release does not serve (over 256 values, or a step below 1)The api_usage_error shape, with a message that names the grid. Do not depend on the exact message wording
Over 64k tokens{"detail": {"error_type": "max_tokens_exceeded"}}
Unknown model{"detail": {"error_type": "api_usage_error", "message": "Unknown model: <name>"}}
Bad imageThe api_usage_error shape, with a message that names the problem: undecodable, an unsupported type or over the size limit

A number question needs min at or below max, and uses no criteria: its grid is the set of answers. Keep the grid within the supported limits.

Never resend a 400 unchanged

The same request gets the same 400. Trim the state, split the options across two questions or fix the model name, then send it again.

Out of credit: 402#

Requests draw down a prepaid balance. When it reaches zero, every request returns HTTP 402 with {"detail": {"error_type": "insufficient_credit", "message": "..."}} until credit is added, so a runaway job cannot spend money you did not put in. A refused request is not billed and runs no model work.

Do not retry a 402 in a loop: it keeps failing until the balance has credit again. Turn on auto reload under Billing so the balance tops itself up before it runs out, and alert on any 402 your service still sees. Pricing and billing has the details.

Refused by the safety check: 403#

Every request passes an automatic safety check before the model answers. It refuses a narrow set of uses that the Acceptable Use Policy forbids: choosing whom to harm by a protected trait (race, religion, sex, disability and the others the policy lists), the lethal targeting of people, and help to self-harm. Detecting a risk is never refused: asking whether a message shows a risk of self-harm, or whether a post is a threat, is answered as usual.

A refused request returns HTTP 403 with {"detail": {"error_type": "safety_refusal", "message": "..."}} instead of answers. Nothing about it is worth retrying: the same request is refused again. A refused request is not charged (pending confirmation).

Rolling out in shadow mode

The check runs in shadow mode first: it records and flags requests it would refuse, and still answers them. Handle the 403 now, so your code is ready when enforcement starts. Responsible use covers the rest of the rules.

Retrying#

import random, time, requests

def decide(body, key, attempts=5):
    for attempt in range(attempts):
        r = requests.post(
            "https://api.decisionnode.com/v1/decide",
            json=body,
            headers={"Authorization": f"Bearer {key}"},
            timeout=10,
        )
        if r.status_code == 429:
            time.sleep(float(r.headers.get("Retry-After", 1)))
            continue
        if r.status_code == 529:
            # Retry-After is the server's estimate: wait at least that long
            backoff = min(8, 0.25 * 2 ** attempt)
            wait = max(float(r.headers.get("Retry-After", 0)), backoff)
            time.sleep(wait + random.random() / 4)
            continue
        if not r.ok:
            # 400, 401, 402, 403, 422: retrying will not help.
            # (402 clears only once credit is added.)
            # detail is a list, an object or a string: log it whole.
            detail = r.json().get("detail")
            raise RuntimeError(f"{r.status_code}: {detail!r}")
        return r.json()
    raise RuntimeError("DecisionNode: out of retries")

Log the request id

Every response, errors included, carries x-request-id. Log it with the status; it is the fastest way for us to find your request.

previousGET /v1/modelsnextRate limits

DecisionNode is built and run by Bynn Intelligence, Inc.

  • Home
  • Playground
  • Examples
  • Console
  • Responsible use
  • Terms
  • Acceptable use
  • Privacy
  • Data processing
  • Defence addendum
  • Cookies

on this page

  1. Error bodies
  2. Over a limit: 400
  3. Out of credit: 402
  4. Refused by the safety check: 403
  5. Retrying