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, nothing to install.
  • POST /v1/decideAPI referenceAnswer typed questions about a state, optional images and video frames. 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 on the data we measure.
  • ImagesConceptsSend up to 16 images and text in the same request. The model reads printed and handwritten text, amounts, dates, objects and layout, and…
  • 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
  • CLI
  • Playground
  • Console and keys
  • With coding agents
  • MCP server
  • Examples

Concepts

  • State
  • Questions
  • Choice
  • Score
  • Truth
  • Number
  • Points and boxes
  • Images
  • Video
  • Boosters
  • Collections
  • Confidence
  • Determinism
  • API keys

Models

  • DecisionNode MoE
  • DecisionNode Flash
  • Limits
  • Versions
  • Dedicated capacity

Fine-tuning

  • Overview
  • Prepare your dataset
  • Upload and validation
  • Start a training run
  • Watch a run
  • The quality gate
  • Use your model
  • Limits and pricing

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loops
  • Keys on devices
  • Bill your customers per device
  • Publishable keys

Integrations

  • Home Assistant
  • MQTT bridge
  • ROS 2

API reference

  • Overview
  • POST/v1/decide
  • GET/v1/models
  • POST/v1/uploads
  • /v1/keys
  • GET/v1/keys/{key_id}/usage
  • GET/v1/usage
  • Errors
  • Safety check
  • Rate limits

Sessions API

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

Collections API

  • Overview
  • POSTCreate a collection
  • GETList collections
  • GETRetrieve a collection
  • DELDelete a collection
  • POSTEnroll a face
  • GETList items
  • PATCHUpdate an item
  • DELDelete an item

Pricing and billing

  • Pricing and billing
  • Refer & earn

Policies

  • Responsible use
  • Data and privacy
  • Benchmarks
  • Pricing
  • Playground
Get API key
  • Guides
  • API reference
  • Examples
  • Playground

Get started

  • Introduction
  • Quickstart
  • CLI
  • Playground
  • Console and keys
  • With coding agents
  • MCP server
  • Examples

Concepts

  • State
  • Questions
  • Choice
  • Score
  • Truth
  • Number
  • Points and boxes
  • Images
  • Video
  • Boosters
  • Collections
  • Confidence
  • Determinism
  • API keys

Models

  • DecisionNode MoE
  • DecisionNode Flash
  • Limits
  • Versions
  • Dedicated capacity

Fine-tuning

  • Overview
  • Prepare your dataset
  • Upload and validation
  • Start a training run
  • Watch a run
  • The quality gate
  • Use your model
  • Limits and pricing

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loops
  • Keys on devices
  • Bill your customers per device
  • Publishable keys

Integrations

  • Home Assistant
  • MQTT bridge
  • ROS 2

API reference

  • Overview
  • POST/v1/decide
  • GET/v1/models
  • POST/v1/uploads
  • /v1/keys
  • GET/v1/keys/{key_id}/usage
  • GET/v1/usage
  • Errors
  • Safety check
  • Rate limits

Sessions API

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

Collections API

  • Overview
  • POSTCreate a collection
  • GETList collections
  • GETRetrieve a collection
  • DELDelete a collection
  • POSTEnroll a face
  • GETList items
  • PATCHUpdate an item
  • DELDelete an item

Pricing and billing

  • Pricing and billing
  • Refer & earn

Policies

  • Responsible use
  • Data and privacy
  1. docs
  2. /
  3. API reference

Key usage

One key's usage and cost over a period: requests, tokens, booster calls and what you were charged, as totals or by hour, day or month.

on this page7 sections
  1. Headers
  2. Path parameters
  3. Query parameters
  4. Response
  5. The cost
  6. as_of
  7. Which keys you can read
  8. Errors
  9. Limits

Read what one key did and cost: a device's month so far, a day by the hour, a customer's year by the month. To read many keys at once, for an invoice run, use GET /v1/usage. How to bill from it is on Bill your customers per device.

GEThttps://api.decisionnode.com/v1/keys/{key_id}/usage
curl -G https://api.decisionnode.com/v1/keys/key_3b8f0d6e2a914c7b9e5f1a2c4d6e8f01/usage \
  -H "Authorization: Bearer $DECISIONNODE_MANAGE_KEY" \
  --data-urlencode "start=2026-10-01T00:00:00Z" \
  --data-urlencode "end=2026-10-03T00:00:00Z" \
  --data-urlencode "granularity=day"

Headers#

Authorizationheaderrequired
Bearer a Full access or Manage device keys key, on your server. A Decisions-only key gets 403 key_scope_denied, "This key may only make decisions.", and a Publishable key "A publishable key may only make decisions."

Path parameters#

key_idstringrequired
The key: key_ and 32 hex digits, as /v1/keys and the console show it.

Query parameters#

startstring
RFC 3339, with any offset, read in UTC and rounded down to the hour: the first moment counted. Left out, 00:00 UTC on the 1st of this month.
endstring
RFC 3339, rounded up to the hour: the first moment not counted, so a month is 2026-10-01T00:00:00Z to 2026-11-01T00:00:00Z. Left out, now, rounded up to the next hour. A period is at most 400 days. There is no limit on how far back it starts: usage is kept indefinitely.
granularitystringdefault "none"
none, hour, day or month: none answers the totals only; the others add buckets of an hour, a day or a calendar month, in UTC. hour takes a period of at most 7 days.
group_bystring
model or channel: adds by_model or by_channel, the totals split by model or by channel.

Response#

200 OK with the key, the period read, how current the figures are, the totals, and the buckets or splits you asked for.

200 OK
{
  "key": {
    "id": "key_3b8f0d6e2a914c7b9e5f1a2c4d6e8f01",
    "name": "Drone 0042",
    "scope": "decide",
    "device_id": "acme-drone-0042",
    "labels": { "customer": "acme", "plan": "pro" },
    "status": "active",
    "created_at": "2026-09-02T08:14:00Z",
    "revoked_at": null,
    "expires_at": null
  },
  "period": {
    "start": "2026-10-01T00:00:00Z",
    "end": "2026-10-03T00:00:00Z"
  },
  "as_of": "2026-10-03T00:00:00Z",
  "totals": {
    "requests": 798,
    "free_tier_requests": 0,
    "input_tokens": 4364000,
    "image_tokens": 3167000,
    "video_tokens": 0,
    "booster_calls": 0,
    "cost": { "usd": 0.183288, "model_usd": 0.183288, "boosters_usd": 0 }
  },
  "buckets": [
    {
      "start": "2026-10-01T00:00:00Z",
      "end": "2026-10-02T00:00:00Z",
      "totals": {
        "requests": 410,
        "free_tier_requests": 0,
        "input_tokens": 2255000,
        "image_tokens": 1640000,
        "video_tokens": 0,
        "booster_calls": 0,
        "cost": { "usd": 0.09471, "model_usd": 0.09471, "boosters_usd": 0 }
      }
    },
    {
      "start": "2026-10-02T00:00:00Z",
      "end": "2026-10-03T00:00:00Z",
      "totals": {
        "requests": 388,
        "free_tier_requests": 0,
        "input_tokens": 2109000,
        "image_tokens": 1527000,
        "video_tokens": 0,
        "booster_calls": 0,
        "cost": { "usd": 0.088578, "model_usd": 0.088578, "boosters_usd": 0 }
      }
    }
  ]
}
keyobject
The key, as it is now: revoked and expired keys included.
id, name, scopestring
As on the key object.
device_idstring or null
null on a key without one.
labelsobject
{} on a key without any.
statusstring
active, expired or revoked.
created_at, revoked_at, expires_atstring or null
RFC 3339.
periodobject
{"start", "end"} as applied: start rounded down and end up to the hour, in UTC.
as_ofstring
RFC 3339, never past period.end: the figures are complete up to here, and a call after it may not be counted yet. Once as_of equals period.end, the period's figures are final: see as_of.
totalsobject
The period's totals.
requestsinteger
Every call the key made, answered or not: decisions (refused and failed ones included) and session opens. A session's frames are not counted. Refused and failed calls cost nothing.
free_tier_requestsinteger
Of those, the requests whose model part the free tier covered, at no cost; their booster calls past the day's free booster calls are billed.
input_tokensinteger
Every input token, as each answer's usage.input_tokens counts it, image and video tokens included. A refused or failed decision's tokens count. There is no output count: output is free.
image_tokens, video_tokensinteger
The images' and the videos' shares of input_tokens.
booster_callsinteger
The booster calls billed, collection enrollments included.
costobject
{"usd", "model_usd", "boosters_usd"}, in USD, written as an answer's usage.cost is but in whole micro-dollars, so at most 6 decimals, and without its free_tier flag (free_tier_requests counts those calls): what your workspace is charged for these calls, usd = model_usd + boosters_usd. See the cost.
bucketsarray
With a granularity other than none: {"start", "end", "totals"} for each hour, day or month that has usage, oldest first, in UTC. The first and the last are cut to the period.
by_modelarray
With group_by=model: {"model", "totals"} for each model the key used, in name order.
by_channelarray
With group_by=channel: {"channel", "totals"} for all three channels, always, a quiet one at 0: live for decisions and every other call (collection enrollments and the free tier's requests included), session for sessions (an open counted as a request, its frames' tokens and cost included) and batch at 0.

The cost#

totals.cost is what your workspace is charged for the key's calls in the period: refused and failed calls, and the free tier's model part, at 0, and a capped key's answers at most what was reserved for them. On a prepaid workspace it is the list price as metered: what your balance was drawn down by, and your usage page's spend. On a workspace invoiced at a negotiated rate, it is the invoice's own arithmetic at the month's rate: summed over your keys, it matches the usage on your invoice, each answer's usage.cost, and your usage page's spend, which on such a workspace runs a couple of minutes behind. An invoice's workspace-wide lines (reserved capacity, collection storage, and the credit for usage drawn before invoicing began) are in no key's figures. Totals are priced per key, month and model, and each bucket and split on its own sums: at list prices they add up exactly; at a negotiated rate a part can differ from its share by a micro-dollar.

as_of#

  • Complete up to as_of. Usage is folded in every minute, from calls at least two minutes old, and as_of says how far: every call before it is counted. It never passes period.end.
  • Final once it equals period.end. For a calendar month that is normally a few minutes after 00:00 UTC on the 1st.
  • Nothing arrives late in a past hour. Usage is dated when it is recorded.
  • One exception, a rebuild. When past hours are rebuilt from their records, as_of steps back to the first of them until the rebuild is done, then comes forward again. Wait for it before you invoice.

Which keys you can read#

  • Your workspace's keys only. A Full access key reads every key made in its workspace, in the console or through /v1/keys, of any kind. A Manage device keys key reads the keys /v1/keys lists for it, the Decisions-only ones.
  • Any other id is 404 not_found_error, "Not found.", another workspace's key, a key that does not exist and an id that is not one alike, never 403, so a key id cannot be probed.
  • Revoked and expired keys stay readable, as their usage is kept indefinitely: billing a device after it is revoked is the usual case.
  • Always open: an empty balance never makes these routes 402, and a suspended workspace can still read its usage, to bill its own customers.

Errors#

  • 400

    Type
    api_usage_error
    When
    A query it cannot take, with one of: "start is an RFC 3339 time, such as 2026-10-01T00:00:00Z." (or end), "end is after start.", "A period is at most 400 days.", "granularity hour is for a period of at most 7 days; use day or month.", "granularity is none, hour, day or month.", "group_by is model or channel.", "start and end are times between the years 1 and 9999.", "Unknown query parameter: <name>."
    What to do
    Fix the query
  • 401

    Type
    authentication_error
    When
    The key is missing, unknown or revoked, or has expired (details.reason key_expired)
    What to do
    Check the Authorization header
  • 403

    Type
    key_scope_denied
    When
    A Decisions-only key, "This key may only make decisions.", or a Publishable key, "A publishable key may only make decisions."
    What to do
    Read usage from your server, with a Full access or Manage device keys key
  • 404

    Type
    not_found_error
    When
    "Not found." No key with this id that your key may read, or an id that is not one
    What to do
    Check the id, and that it is a key of this workspace
  • 429

    Type
    rate_limit_error
    When
    More than 10 usage reads a second for your workspace, both routes together: "Rate limit reached. Retry after 1 s.", with Retry-After and retry-after-ms
    What to do
    Wait Retry-After, then retry
  • 529

    Type
    overloaded_error
    When
    Usage cannot be read right now, or this read waited more than a second behind your workspace's others, with Retry-After
    What to do
    Read one page at a time; wait Retry-After, then retry
Errors this call returns
StatusTypeWhenWhat to do
400api_usage_errorA query it cannot take, with one of: "start is an RFC 3339 time, such as 2026-10-01T00:00:00Z." (or end), "end is after start.", "A period is at most 400 days.", "granularity hour is for a period of at most 7 days; use day or month.", "granularity is none, hour, day or month.", "group_by is model or channel.", "start and end are times between the years 1 and 9999.", "Unknown query parameter: <name>."Fix the query
401authentication_errorThe key is missing, unknown or revoked, or has expired (details.reason key_expired)Check the Authorization header
403key_scope_deniedA Decisions-only key, "This key may only make decisions.", or a Publishable key, "A publishable key may only make decisions."Read usage from your server, with a Full access or Manage device keys key
404not_found_error"Not found." No key with this id that your key may read, or an id that is not oneCheck the id, and that it is a key of this workspace
429rate_limit_errorMore than 10 usage reads a second for your workspace, both routes together: "Rate limit reached. Retry after 1 s.", with Retry-After and retry-after-msWait Retry-After, then retry
529overloaded_errorUsage cannot be read right now, or this read waited more than a second behind your workspace's others, with Retry-AfterRead one page at a time; wait Retry-After, then retry

Limits#

  • Their own rate: 10 reads a second per workspace, both routes together, apart from your rate limits: reading usage never slows your decisions, and decisions never slow it. Over it, 429 rate_limit_error with Retry-After.
  • One read at a time: your workspace's usage reads are answered one after another, so reads sent in parallel queue, and one that waits more than a second is 529. Read pages in turn.
  • Never cached: every answer carries Cache-Control: private, no-store, and x-decisionnode-request-id as every call does.
previousKeysnextUsage by key

DecisionNode is built and run by Bynn Intelligence, Inc.

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

on this page

  1. Headers
  2. Path parameters
  3. Query parameters
  4. Response
  5. The cost
  6. as_of
  7. Which keys you can read
  8. Errors
  9. Limits