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. Patterns

Publishable keys

A key made to be seen, for browsers and mobile apps: decisions only, no face search, always rate-limited and capped, and limited to the origins and models you allow.

on this page5 sections
  1. Make one
  2. In a browser
  3. In a mobile app
  4. Refusals
  5. What a publishable key can never do

A publishable key is the one kind of key meant to be seen: it ships inside a web page or an app, where anyone can read it. It can make decisions and nothing else, not even list the models, and every publishable key has rate limits and a daily spend cap, so a copied key can cost at most its cap a day. When you have a server of your own, keep the key there instead and let the page call your server.

Make one#

  • In the console only, under API keys, of the kind Publishable. /v1/keys never makes one.
  • Allowed origins for a web page: each one exact, as the browser sends it, https://hosthttps://host or https://host:porthttps://host:port such as https://shop.example.comhttps://shop.example.com, with no path and no wildcard; http://http:// only for localhost, 127.0.0.1 and [::1]. At most 20, kept in lowercase. A newly listed origin is answered within about 10 seconds.
  • Apps send no Origin. Tick Allow native apps (no Origin) for a key an app uses; a key with no origins must have it ticked. Without origins, this key's limits are its only protection.
  • Models it may use: the key may call only the models it lists, DecisionNode Flash unless you add more, and never a fine-tuned model. Nor does a fallback answer it with another: when the fallback is not one of its models, the call gets what it would without one, the usual 529 while nothing is ready.
  • Rate limits and a daily spend cap are always on. A new key starts at 5 requests and 20,000 input tokens a second, 1 request a second per client address (bursts of 10), 8 requests in flight, and USD 5 a day; set them to what your traffic needs (calls in flight from 1 to 64). One client address may make the per-address rate times 10 calls in each 10-second window, so a burst of 10 at the default; an IPv6 visitor is counted by its /64, an IPv4 address on its own.
  • The cap counts each request at its worst case until it is answered. Every request reserves its largest possible cost before it runs, and the key is charged the real cost once it is answered. So a key near its cap is refused a little early, by up to one request's worst case, and a capped key can have only about its cap divided by one request's worst-case cost in flight at once: with a very low cap and many calls at the same time, 429 key_spend_cap_reached can come before the day's real spend reaches the cap. Set the cap with headroom over what you expect to spend.
  • A capped key's answer never costs more than was reserved for it. If an answer would cost more (one priced as another version than the one reserved for, or more tokens than the model's context, as the model reports them), its cost is cut to the reservation and DecisionNode absorbs the difference. The answer's usage.cost, the usage page, per-key usage, the invoice and a prepaid balance all carry the cut amount, and the usage page and per-key usage count the tokens billed; the answer's own token counts stay as the model reported them, so on such an answer its tokens times the price come to more than its usage.cost.

In a browser#

A page on an allowed origin calls https://api.decisionnode.com/v1/decidehttps://api.decisionnode.com/v1/decide straight from the browser with the key. A publishable key starts with dn_pub_, so it is never taken for a secret one. Name a model the key lists: a request without model goes to DecisionNode MoE, which a key on DecisionNode Flash alone refuses with 403 model_not_allowed.

From a page on an allowed origin
const r = await fetch("https://api.decisionnode.com/v1/decide", {
  method: "POST",
  headers: {
    Authorization: "Bearer dn_pub_...", // publishable: safe to ship
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "decisionnode-1.0-flash",
    state: { review: text },
    questions: { abusive: { type: "truth", instructions: "Is the review abusive?" } },
  }),
});
const { answers } = await r.json();

A browser may call POST /v1/decide: the preflight gets 204 only from an origin a publishable key lists. From an origin no publishable key lists, and for any other route these docs describe, it gets 400 Disallowed CORS origin, so list the origin first. A publishable key's answers to a listed origin, its errors and a 401 included, carry Access-Control-Allow-Origin and let the page read x-request-id, retry-after, retry-after-ms, x-decisionnode-cache and x-decisionnode-model: read the request id from x-request-id, and the wait from retry-after on a 429. No other kind of key's answers carry them, whatever origin the page sends, so only a publishable key works in a page: a Full access key pasted into one fails, as it should.

In a mobile app#

An app sends no Origin. A call without one is accepted only on a key with Allow native apps (no Origin) ticked (otherwise it is 403 origin_not_allowed), and then the rate limits and the spend cap are its only protection: anyone with the app can take the key out of it. If the app has a server of yours behind it, give each install its own Decisions-only key from that server instead: see Keys on devices.

The origin list is not a lock

Browsers send the page's origin and keep other websites from using your key in their visitors' browsers. Outside a browser anyone can send any origin, or none. Count on the limits and the cap to bound a copied key, never on the origin list.

Refusals#

  • 400 Disallowed CORS origin

    When
    A browser's preflight for any route these docs describe but POST /v1/decide, or from an origin no publishable key lists
  • 403 origin_not_allowed

    When
    An Origin the key does not list: "This key does not answer calls from this origin." Or no Origin on a key without Allow native apps (no Origin): "This key answers calls from its origins only: a call without an Origin needs a key that allows native apps."
  • 403 model_not_allowed

    When
    A model the key does not list, a fine-tuned model, or no model on a key without DecisionNode MoE (a body without one asks DecisionNode MoE): "This key may not ask this model. Its models: <the key's models>.", with details.allowed, the key's list
  • 403 key_scope_denied

    When
    Any route but a decision, the models list included: "A publishable key may only make decisions." Or a face_recognition booster: "This key may not search collections."
  • 401 authentication_error

    When
    A revoked key
  • 429 key_spend_cap_reached

    When
    The daily spend cap is spent, until 00:00 UTC
  • 429 rate_limit_error

    When
    Over the key's rate: "Rate limit reached. Retry after 1 s." Over one client address's (an IPv6 visitor's /64): "Rate limit reached for this address. Retry after <n> s.", details.reason client_rate. Too many calls running at once: "Too many of this key's calls are running at once. Retry after 1 s.", details.reason in_flight. Each with Retry-After
What a publishable key is refused
AnswerWhen
400 Disallowed CORS originA browser's preflight for any route these docs describe but POST /v1/decide, or from an origin no publishable key lists
403 origin_not_allowedAn Origin the key does not list: "This key does not answer calls from this origin." Or no Origin on a key without Allow native apps (no Origin): "This key answers calls from its origins only: a call without an Origin needs a key that allows native apps."
403 model_not_allowedA model the key does not list, a fine-tuned model, or no model on a key without DecisionNode MoE (a body without one asks DecisionNode MoE): "This key may not ask this model. Its models: <the key's models>.", with details.allowed, the key's list
403 key_scope_deniedAny route but a decision, the models list included: "A publishable key may only make decisions." Or a face_recognition booster: "This key may not search collections."
401 authentication_errorA revoked key
429 key_spend_cap_reachedThe daily spend cap is spent, until 00:00 UTC
429 rate_limit_errorOver the key's rate: "Rate limit reached. Retry after 1 s." Over one client address's (an IPv6 visitor's /64): "Rate limit reached for this address. Retry after <n> s.", details.reason client_rate. Too many calls running at once: "Too many of this key's calls are running at once. Retry after 1 s.", details.reason in_flight. Each with Retry-After

What a publishable key can never do#

  • List the models: GET /v1/models is 403 key_scope_denied, "A publishable key may only make decisions."
  • Search collections: a face_recognition booster is 403 key_scope_denied, whatever the key's settings.
  • Make uploads, sessions, or read, change or delete collections.
  • Make, list or revoke keys.
  • Call a model it does not list, spend past its daily spend cap, or run without rate limits.

When a publishable key is abused, revoke it in the console and publish a new one; the revocation is immediate.

previousBill your customers per devicenextHome Assistant

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. Make one
  2. In a browser
  3. In a mobile app
  4. Refusals
  5. What a publishable key can never do