Introduction
Our API answers one question: may this tool call run? You ask it before your handler does any work, and we check the calling agent’s key against the plan it belongs to. The answer is allow or deny, and every answer writes one audit row you can find later.
Base URL
Section titled “Base URL”The API lives at this base URL, and every example on these pages calls it:
https://api.ordainer.devEvery path starts with the version, /v1. Today there is one endpoint, POST /v1/decide.
Your key
Section titled “Your key”An agent key is the key you issue to each integration or AI agent.
Send your agent key in the Authorization header on every call:
Authorization: Bearer ord_test_...A key is 41 characters: ord_test_ or ord_live_ followed by 32 letters, digits, - or _. Test keys are free and work in your sandbox. Live keys are not available yet. Live keys open when paid plans launch. Your test keys cover everything in your beta.
We show each key once, when it’s created, and keep only a hash of it. Please copy it with the Copy button rather than retyping it. If a key is lost, create a new one in Console → Keys.
Keep keys on your server. The API is for server-to-server calls only and sends no CORS headers, so a key in a web page won’t work and shouldn’t be there.
Your first call
Section titled “Your first call”The body names the tool. We recommend an idempotency key on every call, so a retry is answered once and counted once. You can also send an optional mode. That’s the whole request: your key travels in the header, and we ignore any other field. Extra fields such as units, quantity, cost or customer_id are ignored. Every allowed call counts as 1.
curl -s -X POST https://api.ordainer.dev/v1/decide \ -H "Authorization: Bearer $ORDAINER_KEY" \ -H "Content-Type: application/json" \ -d '{"tool":"research.run","idempotency_key":"job-42-step-3"}'{"decision":"allow","reason":"ok","remaining":4,"limit":5,"resets_at":"2026-11-01T00:00:00.000Z","mode":"enforce","request_id":"req_bUyY8o6enyPzZvEL"}A deny comes back the same way, as a normal 200 with "decision":"deny" and a reason. Read decision, not the status code. New here? Your first deny takes a few minutes.
When we can’t answer
Section titled “When we can’t answer”If a call times out or our side has a problem, your code decides what happens. Our Node SDK lets the tool run by default and logs a warning (failMode: 'open'), and you can set it to block instead. With plain HTTP, it’s your choice in your own code. More in Fail open or closed.
The first call can be slower. The first call from a new process, a cold JVM or a fresh serverless instance also has to open the connection, so it can take longer than the calls after it. Two things keep it inside your timeout: make one warm-up request at startup (GET /livez on the API address; it needs no key, moves no usage counter and writes no audit row), and on a timeout retry once with the same idempotency key, so the retry is answered once and counted once. Our Node, Python and Ruby SDKs retry this way by default: 300 ms per attempt and one retry, 650 ms in all. They don’t send a warm-up request; add one yourself if your first call matters.
Errors
Section titled “Errors”Anything that isn’t a decision is a non-200 with one shape:
{"error":{"code":"missing_key","message":"Send your agent key as \"Authorization: Bearer ord_...\".","request_id":"req_EWJ4qahPhrssV5a5"}}Branch on error.code, since the message wording can change. An error never moves a counter and writes no audit row. Every response carries an X-Request-Id header, so please include it if you write to us at support@ordainer.ai. Each code and its fix is on Errors.
Limits
Section titled “Limits”- Plan limits. On live keys, limits are per agent key today. Two keys on the same plan each get the full limit. Test keys include 1,000 test decisions a month. Only allowed decisions count, and denies are always free, on test keys and live keys. Every allowed call counts as 1.
- Traffic limits, per key. About 100 requests a second, each server counts separately. 4 requests can be in flight at once, with up to 20 more waiting briefly. Both are counted on each of our servers separately, so treat them as approximate. Past either, you get a
429withRetry-After. Wait that long, then retry with the same idempotency key. - Monthly reset. Counts run by UTC calendar month. Each answer’s
resets_atsays when it can change. - Failed keys, per address. 30 requests a minute with a missing or unknown key. A valid key is never refused by this one.
The details, including how several agents can share one limit, are on Decide.
Versioning
Section titled “Versioning”Within /v1 we only add things, such as new response fields or new error codes. Please ignore fields you don’t know, and treat an unknown deny reason as a deny. A breaking change goes to /v2, and we’ll give at least 6 months’ notice before an old version is turned off. More in Versioning.
You don’t need one. Every example in these docs is plain HTTP and works from any language, an MCP tool included. Our Node SDK (npm install ordainer) adds middleware and the fail-open default. There’s also a Python package (pip install ordainer) and a Ruby gem (gem install ordainer). For Java, the Java example uses the JDK’s own HttpClient, with nothing to install.