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…
  • Batch jobsPatternsSend up to 10,000 requests in one file and collect the answers later, at half the live price.
  • Pricing and billingYou pay for input tokens only. Output is free because the model generates no text.
↑↓ moveopen7 suggestions
Get API keyGet API key
DecisionNodeDecisionNde

Get started

  • Introduction
  • Quickstart
  • Playground
  • Console and keys
  • With coding agents
  • Examples

Concepts

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

Models

  • DecisionNode-1.0
  • DecisionNode-1.0 Flash
  • Limits
  • Versions
  • Dedicated capacity

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loops
  • Batch jobs

API reference

  • Overview
  • POST/v1/decide
  • GET/v1/models
  • Errors
  • Safety check
  • Rate limits

Sessions API

  • Sessions overview
  • POSTOpen a session
  • WSStream frames
  • DELEnd a session

Batch API

  • The batch object
  • POSTCreate a batch
  • POSTAdd requests
  • POSTFinalize a batch
  • GETRetrieve a batch
  • GETGet batch results
  • POSTCancel a batch
  • GETList batches

Pricing and billing

  • Pricing and billing

Policies

  • Responsible use
  • Data and privacy

Migrate

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

Get started

  • Introduction
  • Quickstart
  • Playground
  • Console and keys
  • With coding agents
  • Examples

Concepts

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

Models

  • DecisionNode-1.0
  • DecisionNode-1.0 Flash
  • Limits
  • Versions
  • Dedicated capacity

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loops
  • Batch jobs

API reference

  • Overview
  • POST/v1/decide
  • GET/v1/models
  • Errors
  • Safety check
  • Rate limits

Sessions API

  • Sessions overview
  • POSTOpen a session
  • WSStream frames
  • DELEnd a session

Batch API

  • The batch object
  • POSTCreate a batch
  • POSTAdd requests
  • POSTFinalize a batch
  • GETRetrieve a batch
  • GETGet batch results
  • POSTCancel a batch
  • GETList batches

Pricing and billing

  • Pricing and billing

Policies

  • Responsible use
  • Data and privacy

Migrate

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

Stream frames

Connect to the session's WebSocket, send one frame per message and read one typed reply per answered frame. The socket stays open until you close it, end the session, or it expires.

on this page8 sections
  1. Authentication
  2. Path parameters
  3. Refused connections
  4. Frames
  5. Replies
  6. Frame errors
  7. Close codes
  8. Limits
  9. Notes
WSwss://api.decisionnode.com/v1/sessions/{id}/stream
# npm install -g wscat
WSS=wss://api.decisionnode.com/v1
SESSION=ses_3f9a1c07b2e84d5f6a0c9e12  # from the open response

wscat -c "$WSS/sessions/$SESSION/stream" \
  -H "Authorization: Bearer $DECISIONNODE_API_KEY"

# then type one frame per line:
> {"seq": 1, "frame": {"battery": 0.31, "obstacle": "none"}}

Each text message you send is one JSON frame. Each message you receive is the reply to one frame, or an error about one frame. Frames are answered on the session's fixed part plus its recent frames.

Authentication#

Authorizationheaderrequired
Bearer dn_live_... on the WebSocket handshake, the key that opened the session. Browsers cannot set this header on a WebSocket, so connect from your server.

Path parameters#

idstringrequired
The session_id that Open a session returned, for example ses_3f9a1c07b2e84d5f6a0c9e12.

Refused connections#

A connection the server cannot take opens and closes at once, before any frame, with a close code rather than an HTTP status:

  • 4401: the key is missing or unknown. Check the Authorization header.
  • 4404: no open session with this id for your key: it ended, expired, never existed or belongs to another key. Open a new session.
  • 4409: a socket is already connected to this session. Use the socket you have, or open a new session.

Frames#

{
  "seq": 17,
  "frame": {
    "battery": 0.31,
    "wind_mps": 11.4,
    "distance_home_m": 820,
    "obstacle": "none"
  }
}

Frame

seqintegerdefault the last answered plus one
Your number for the frame, 0 or more. The reply carries it back, and dropped names passed-over frames by it.
framestring | object | array
What changed: text or a JSON value, at most the session's max_frame_tokens. Required unless the frame carries an image; beside an image it is an optional short note, held to the same limit, that joins the window like any frame.
imageobject
One image, which makes this an image frame, held to the image limits of /v1/decide (up to 10 MB and 40 megapixels). The image is seen with this frame only; it does not join the window. It has no id, since a frame holds one image.
media_typestringrequired
image/jpeg, image/png or image/webp.
datastringrequired
Base64 bytes, no data: prefix.
questionsstring[]default all
The keys of the questions to answer this time, a non-empty list. Each frame is billed for the questions it asks.
resetbooleandefault false
true drops the recent frames from context before this one is answered.

Replies#

{
  "seq": 17,
  "answers": {
    "mode": {
      "type": "choice",
      "choice": "return",
      "confidence": 0.79,
      "probabilities": {
        "continue": 0.09,
        "hold": 0.05,
        "land": 0.02,
        "return": 0.84
      }
    },
    "abort": { "type": "truth", "truth": 0.07 }
  },
  "usage": { "input_tokens": 91, "output_tokens": 0 },
  "safety": {
    "checked": true,
    "harm": 0.0021,
    "self_harm": 0.0008,
    "flagged": []
  }
}

Reply

seqinteger
The sequence number of the frame this answers.
answersobject
One answer per question asked, exactly as /v1/decide shapes it, with the same calibration.
droppedinteger[]
Only when frames were passed over: the seq of each frame that arrived while an earlier one was being answered and was superseded by this one. A dropped frame is not answered, does not join the window and is not billed.
usageobject
What this frame billed: input_tokens (the frame, its image if it has one, and the questions it asked) and output_tokens, always 0.
latency_msnumber
Time spent on our side for this frame, in milliseconds. On every reply; the samples here leave it out.
safetyobject
What the safety check found for this frame, as on every call: checked, the probability of each category (harm, self_harm) and flagged, the categories above your key's thresholds. When the check did not run, checked is false with a reason.
  1. Authentication
  2. Path parameters
  3. Frames
  4. Replies
  5. Frame errors
  6. Close codes
  7. Limits
  8. Notes
request
# npm install -g wscatWSS=wss://api.decisionnode.com/v1SESSION=ses_3f9a1c07b2e84d5f6a0c9e12  # from the open responsewscat -c "$WSS/sessions/$SESSION/stream" \  -H "Authorization: Bearer $DECISIONNODE_API_KEY"# then type one frame per line:> {"seq": 1, "frame": {"battery": 0.31, "obstacle": "none"}}

Frame errors#

A frame the API cannot answer gets a message with its seq and an error object, error_type and message, and the socket stays open for the next frame. A refused frame's error also names the category, and the message carries safety with blocked. A rate_limit_error or overloaded_error carries retry_after_ms, the wait before you send the frame again. A frame that got an error leaves the window as it was and is not billed, except a refused frame: the model read it, so its tokens are billed.

Frame error
{
  "seq": 21,
  "error": {
    "error_type": "api_usage_error",
    "message": "The frame is 5210 tokens, the limit is 1535."
  }
}
  • api_usage_error

    When
    The message is not a JSON object; a field a frame does not take; seq that is not an integer of 0 or more; no frame and no image; an image that is not an object with media_type and data, cannot be read or is over the image limits; questions that is not a non-empty list of the session's question keys; reset that is not true or false; text or JSON over the session's max_frame_tokens
    What to do
    Fix the frame; seq is null, or missing, when the frame could not be read. For a long frame, send only what changed and move constant detail into the session's state
  • rate_limit_error

    When
    Your key's input-token rate is used up for now: frames count against it
    What to do
    Wait retry_after_ms, then send the frame again
  • overloaded_error

    When
    No capacity for this frame right now
    What to do
    Wait retry_after_ms when it is set, then send the frame again: it joins the window once
  • api_error

    When
    A fault on our side
    What to do
    Send the frame again
  • refusal_error

    When
    The safety check refused this frame; error.category names harm or self_harm
    What to do
    Change what the frame asks. The session carries on; the refused frame never joins the window, and its tokens are billed
Errors about one frame
TypeWhenWhat to do
api_usage_errorThe message is not a JSON object; a field a frame does not take; seq that is not an integer of 0 or more; no frame and no image; an image that is not an object with media_type and data, cannot be read or is over the image limits; questions that is not a non-empty list of the session's question keys; reset that is not true or false; text or JSON over the session's max_frame_tokensFix the frame; seq is null, or missing, when the frame could not be read. For a long frame, send only what changed and move constant detail into the session's state
rate_limit_errorYour key's input-token rate is used up for now: frames count against itWait retry_after_ms, then send the frame again
overloaded_errorNo capacity for this frame right nowWait retry_after_ms when it is set, then send the frame again: it joins the window once
api_errorA fault on our sideSend the frame again
refusal_errorThe safety check refused this frame; error.category names harm or self_harmChange what the frame asks. The session carries on; the refused frame never joins the window, and its tokens are billed

Close codes#

The close code says why the socket closed, and whether to open a new session with the same body. 4401, 4404 and 4409 come as soon as a connection opens; any other close ends the session. Branch on the code: the reason text beside it is for your logs and may change.

  • 1000

    When
    The session ended on purpose: you closed the socket, or it was ended with DELETE /v1/sessions/{id}
    What to do
    Open a new session when you need one
  • 1001

    When
    The server holding the session is going away. The session has ended
    What to do
    Open a new session with the same body; it opens on a server that is up
  • 1006

    When
    The connection dropped without a close frame: a network fault or a proxy reset. Your WebSocket library reports this code; the server never sends it. The session has ended
    What to do
    Open a new session with the same body, after a capped backoff with jitter
  • 1011

    When
    Something failed on our side. The session has ended
    What to do
    Open a new session with the same body, after a capped backoff with jitter
  • 4401

    When
    The key is missing or unknown. Sent as soon as the connection opens
    What to do
    Check the Authorization header; opening again with the same key will not help
  • 4404

    When
    No open session with this id for your key where the connection landed: it ended, expired, never existed or belongs to another key. Sent as soon as the connection opens
    What to do
    Open a new session with the same body
  • 4408

    When
    No frame arrived for idle_timeout_seconds (120 seconds). Pings do not count. The session has ended
    What to do
    Open a new session with the same body when your next frame is ready
  • 4409

    When
    A socket is already connected to this session. Sent as soon as the second connection opens; the first keeps the session
    What to do
    Use the socket you have, or open a new session
  • 4410

    When
    The session reached expires_at. The session has ended
    What to do
    Open a new session with the same body at once; set ttl_seconds up to 3,600 for longer runs
WebSocket close codes
CodeWhenWhat to do
1000The session ended on purpose: you closed the socket, or it was ended with DELETE /v1/sessions/{id}Open a new session when you need one
1001The server holding the session is going away. The session has endedOpen a new session with the same body; it opens on a server that is up
1006The connection dropped without a close frame: a network fault or a proxy reset. Your WebSocket library reports this code; the server never sends it. The session has endedOpen a new session with the same body, after a capped backoff with jitter
1011Something failed on our side. The session has endedOpen a new session with the same body, after a capped backoff with jitter
4401The key is missing or unknown. Sent as soon as the connection opensCheck the Authorization header; opening again with the same key will not help
4404No open session with this id for your key where the connection landed: it ended, expired, never existed or belongs to another key. Sent as soon as the connection opensOpen a new session with the same body
4408No frame arrived for idle_timeout_seconds (120 seconds). Pings do not count. The session has endedOpen a new session with the same body when your next frame is ready
4409A socket is already connected to this session. Sent as soon as the second connection opens; the first keeps the sessionUse the socket you have, or open a new session
4410The session reached expires_at. The session has endedOpen a new session with the same body at once; set ttl_seconds up to 3,600 for longer runs

Limits#

  • Frame

    Value
    Text or JSON of at most max_frame_tokens tokens (never more than 4,096; the open response's limits gives this session's value), or one image
    Over the limit
    A frame error, api_usage_error; the socket stays open
  • Image in a frame

    Value
    One, of up to 10 MB and 40 megapixels, as on /v1/decide
    Over the limit
    A frame error, api_usage_error
  • Recent frames

    Value
    Together at most window_tokens tokens, in the open response's limits
    Over the limit
    The oldest frames leave the window first
  • Window

    Value
    1 to 64 frames, 8 by default, the frame being answered included
    Over the limit
    422 when opening
  • Lifetime

    Value
    300 seconds by default, 1 to 3,600
    Over the limit
    422 when opening; close code 4410 when reached
  • Idle

    Value
    120 seconds with no frame on an open socket
    Over the limit
    Close code 4408
  • Sockets

    Value
    One per session
    Over the limit
    Close code 4409 on a second connect
  • Token rate

    Value
    Each frame's usage.input_tokens counts against your key's input-token rate, not its request rate
    Over the limit
    A frame error, rate_limit_error, with retry_after_ms; the socket stays open
Limits on the stream
LimitValueOver the limit
FrameText or JSON of at most max_frame_tokens tokens (never more than 4,096; the open response's limits gives this session's value), or one imageA frame error, api_usage_error; the socket stays open
Image in a frameOne, of up to 10 MB and 40 megapixels, as on /v1/decideA frame error, api_usage_error
Recent framesTogether at most window_tokens tokens, in the open response's limitsThe oldest frames leave the window first
Window1 to 64 frames, 8 by default, the frame being answered included422 when opening
Lifetime300 seconds by default, 1 to 3,600422 when opening; close code 4410 when reached
Idle120 seconds with no frame on an open socketClose code 4408
SocketsOne per sessionClose code 4409 on a second connect
Token rateEach frame's usage.input_tokens counts against your key's input-token rate, not its request rateA frame error, rate_limit_error, with retry_after_ms; the socket stays open

Notes#

  • Send one frame per message, as a JSON object in a text message.
  • seq is yours: an integer of 0 or more, carried back in the reply. Rising numbers keep dropped easy to read; gaps are fine, so a client may skip readings.
  • Latest wins: if frames arrive faster than they are answered, the newest is answered and the ones passed over are listed in its reply's dropped. To have every frame answered, send the next one after its reply.
  • A frame error never closes the socket. Only the close codes above end a session.
  • Keepalive. The server sends WebSocket pings on an open socket, so proxies and load balancers see traffic on a quiet socket. Your library answers them with a pong on its own (both websockets and ws do); a socket that stops answering pings is treated as gone, and your side sees 1006. Pings and pongs are not frames: they are not billed and do not reset the idle timer.
  • Idle sockets. A socket that carries no frame for idle_timeout_seconds (120 seconds) closes with 4408 and the session ends. A loop that may pause longer, such as a drone parked on its pad, either sends its current reading inside that time, with questions naming one question so the frame bills little, or lets the session close and opens a new one when it resumes. The supervisor under Clients does the second.
  • For a client that stays up, see the complete supervisor: it opens a new session after every close it did not ask for, except 4401.
previousOpen a sessionnextEnd a session

DecisionNode is built and run by Bynn Intelligence, Inc.

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