Skip to content

Your first deny

One paste, six calls, and the sixth answers deny. That deny means your tool would not have run.

In a few minutes · Free test key · curl, PowerShell or Python: no install · Node 20+: npm install ordainer

  1. Sign up with your work email and open the link we send you. No credit card needed. The link works once, for 60 minutes; check spam if it hasn’t arrived. Stuck? Email support@ordainer.ai and we’ll help. On the page it opens, tick the box to agree to the Terms of Service and Data Processing Addendum and click Continue.

    The Your sandbox is ready page shows two test keys, both for the customer “Example Co (sandbox)”. They work with any tool name; the examples use the toy tool research.run:

    • pilot: 5 allows a month, across all tools. Use this one; you’ll hit its limit right away.
    • prod: no plan allowance limit. Only your organization’s monthly test limit applies. Its answers show limit and remaining as null.

    Keys are shown once, so copy the pilot key now. You don’t need your own product yet. Test keys start with ord_test_ (live keys: ord_live_). An old sk_test_… / sk_live_… key from before the rename gets 401 old_key_format: create a new key in Console → Keys.

    Get started
  2. On Node 20+, use the Node SDK tab: it installs our SDK and calls decide() six times. The bash tab is the welcome page’s call, with the key and the base URL in environment variables instead of filled in: run it 6 times, and the sixth answer is deny. The PowerShell tab runs the same call six times for you. Every tab, and every example below, reads the key from ORDAINER_KEY and the base URL from ORDAINER_BASE_URL, the same names the console uses.

    Terminal window
    export ORDAINER_KEY=ord_test_... # your pilot key
    export ORDAINER_BASE_URL=https://api.ordainer.dev
    npm install ordainer
    node -e '
    const { Ordainer } = require("ordainer");
    const ordainer = new Ordainer(); // reads ORDAINER_KEY and ORDAINER_BASE_URL
    (async () => {
    for (let i = 1; i <= 6; i++) {
    const d = await ordainer.decide({ tool: "research.run" });
    console.log(i, d.decision, d.reason, d.remaining + "/" + d.limit, d.requestId);
    }
    })();
    '

    Calls 1 to 5 answer allow, with remaining going 4, 3, 2, 1, 0. Call 6 answers:

    Response to call 6
    {
    "decision": "deny",
    "reason": "quota_exceeded",
    "remaining": 0,
    "limit": 5,
    "resets_at": "2026-11-01T00:00:00.000Z",
    "mode": "enforce",
    "request_id": "req_CuJXkng3xBlh4g3h"
    }

    The Node SDK tab (like the welcome page’s Node 20+ version) prints one short line per call instead:

    output
    1 allow ok 4/5 req_nLdLl5aMrmmrMYcp
    2 allow ok 3/5 req_GIPD1vUyw3h4MfUB
    3 allow ok 2/5 req_U3vV27ZOCb6EIqd9
    4 allow ok 1/5 req_pTiutAI2ThuRY-pU
    5 allow ok 0/5 req_58JB9SJoM050SEH6
    6 deny quota_exceeded 0/5 req_mXeb0hk-kaepjzaj

    Click Reset sandbox (on the welcome page or the console Overview). It clears the sandbox customer’s test-key counters, audit rows and idempotency keys, so the pilot key allows 5 again. Customers you’ve added yourself keep their test rows, and live data is never touched.

  3. The same call in your language. On Node, use our SDK (npm install ordainer, Node 20+). The other tabs are plain HTTP examples: no client library to install. We also have a Ruby gem (gem install ordainer) and a Python package (pip install ordainer). The Python tab below is plain HTTP with no install; every other language calls the HTTP API like this. Each one sends a new Idempotency-Key per call, runs the tool on allow, stops on a deny, and prints reason, remaining and X-Request-Id.

    Run it in the shell from step 2’s Node SDK tab (ordainer installed, ORDAINER_KEY and ORDAINER_BASE_URL set). Came from the curl tab? Run npm install ordainer first.

    decide.js
    // Node 20+. Run it 6 times: node decide.js
    const { Ordainer } = require('ordainer');
    // Reads ORDAINER_KEY and ORDAINER_BASE_URL. Timeout 300 ms per try (1 retry by default, 650 ms at most), fails open on timeouts and outages.
    const ordainer = new Ordainer();
    async function main() {
    // decide() never throws, and a deny is a normal result. Each call sends a new idempotency key.
    const d = await ordainer.decide({ tool: 'research.run' });
    const left = d.decision === null ? 'no decision' : d.limit === null ? 'no cap' : `${d.remaining}/${d.limit} left`;
    if (d.allowed) {
    const why = d.wouldDeny ? `would deny (shadow): ${d.reason}, run the tool anyway`
    : d.decision === 'allow' ? 'allow: run the tool'
    : 'no answer (failed open): run the tool';
    console.log(`${why} (${left}, request_id ${d.requestId})`);
    return 0;
    }
    if (d.error) {
    console.error(`error: ${d.error.code}: ${d.error.message}`); // for example missing_key or unknown_key
    return 1;
    }
    console.log(`deny: ${d.reason}, don't run the tool (${left}, resets ${d.resetsAt}, request_id ${d.requestId})`);
    return 2;
    }
    main().then((code) => { process.exitCode = code; });

    d.allowed already accounts for shadow mode and fail-open, so it’s the one field to branch on. Putting it in front of an Express route instead? The Node / Express quickstart uses ordainer.middleware().

    Every example applies one rule, the same in any language:

    The one rule: run the tool if decision is allow or mode is shadow.

    For Java, see the Java example (JDK HttpClient, no SDK). More languages (Node fetch, TypeScript, Go, C#, PHP, Rust, Kotlin, Swift) are coming to these docs.

    Using Node and Express? Our SDK is on npm: npm install ordainer. The Node / Express quickstart walks you through it. Using Ruby or Rails? Our gem is on RubyGems: gem install ordainer. The Ruby / Rails quickstart walks you through it. Using Python? Our package is on PyPI: pip install ordainer. The Python quickstart walks you through it. Or keep using plain HTTP as above.

Before you block real calls, run your plan in shadow mode for about a week. Your code runs every call, and the ones your plan would have blocked are logged as would-denies, so you see what Ordainer would deny on real traffic without blocking anyone. To turn it on, open Console → Plans and click Shadow on the plan’s row (new plans you add start in shadow), and leave mode out of your requests so the plan’s setting applies. When the would-denies on the Overview look right, click Enforce, with no deploy. Roll out with shadow mode walks through each step.