Zero to first gate call
in under 10 minutes.
One endpoint. One header. POST before each step — the gate returns ALLOW or DENY. Your agent only proceeds on ALLOW. Every decision, ALLOW or DENY, writes a cryptographic receipt — a signed, numbered record of what was authorised or refused, in what order, issued before the gate answers. The same gate answers the engineering question and the compliance question.
Three steps. No magic.
Every gate call follows the same path. Your agent sends a request. The gate enforces sequence. You get a receipt or a halt.
POST to /v1/evaluate with your agent's current step, a unique nonce, and a timestamp. Auth via the Authorization: Bearer header.
The gate validates step order, checks the nonce for replay, verifies function and action_type against the policy for this step, and confirms the sequence is not sealed. All checks must pass.
On pass: a cryptographic receipt with decision ALLOW — Ed25519-signed, chained, written to R2 as a tamper-evident record. That receipt is your compliance record: structural proof of what was permitted and in what order, with the timestamp the caller supplied, bounded by the gate's clock. On failure: a DENY with a reason code. Your agent only proceeds on ALLOW.
One header. Every request.
Send your key in the Authorization: Bearer header on every request. The demo key is public and rate-limited — use it to test without signing up. A request with no key, or one the gate does not recognise, runs on the public demo lane and the response says so.
Add to every request: Authorization: Bearer DEMO-AGENTICRAIL-PUBLIC-2026
A private key is US$39 a month, bought without speaking to anyone: buy a developer key. For a deployment, write to hello@agenticrail.nz.
Wrapper, Gate, and Report
AgenticRail runs three services. Two are public; the gate is reached only through the wrapper:
Endpoint: https://api.agenticrail.nz/v1/evaluate
Auth: Authorization: Bearer <key>
Adds API key management, rate limiting, D1 logging, and demo key bypass. All client requests should use this endpoint.
Pure sequence enforcement layer — validates step order, nonce, function/action_type policy, and sequence seal. Called internally by the wrapper via service binding. Not publicly accessible — all client traffic enters through the wrapper.
Your own sequences: POST https://api.agenticrail.nz/v1/report
Auth: Authorization: Bearer <key>
Public demo sequences: https://report.agenticrail.nz/report — no key required.
Generates HTML/JSON compliance reports. Read-only; no state mutation.
Demo key DEMO-AGENTICRAIL-PUBLIC-2026 works with the wrapper and both report routes. Wrapper prefixes demo sequence IDs with demo- and isolates receipts.
Fields, one by one.
All requests are Content-Type: application/json via POST. Only three fields are required: sequence_id, step and action_type. Everything else is optional, and the defaults are listed below. A missing or malformed field is refused with HTTP 400 invalid_request, naming the field, before anything is evaluated, so it produces no receipt.
| Field | Type | Description |
|---|---|---|
| sequence_id | string | Required. Groups the steps of one run together, e.g. a UUID. Use a fresh id for every run: sealing is permanent, and on the shared demo lane a fixed id is shared with everyone. On the demo key it comes back prefixed demo-; use the id returned for later steps. A keyed caller may not send an id starting demo- (400). |
| step | string | Required. Your step name, e.g. "verify_identity". Must appear in step_order and is compared byte for byte, so spell it identically on every call. |
| action_type | string | Required. One of eight enforcement classes: CHECK_STATE, CLARIFY_NEXT_STEP, SELECT_NEXT_STEP, RECORD_RESULT, WAIT_FOR_SIGNAL, VALIDATE_INPUT, PAUSE_CYCLE, REDUCE_STIMULUS. Built-in steps accept only a subset; a step in your own step_order accepts all eight. |
| step_order | string[] | Optional, but send it on every call for your own process. All step names in order, e.g. ["verify_identity", "assess_risk", "execute_transfer"]. One step is a valid sequence. If absent, the built-in eight-step MSMD spine applies. The order is locked on the sequence's first permitted call (a refused call locks nothing): a different list later is refused STEP_ORDER_MISMATCH and the refusal returns the locked list. Omitting it later does not clear the lock; it selects the spine and your steps come back UNKNOWN_STEP. Not an array, or empty: 400. |
| function | string | Optional, defaults to step. If sent, must equal step, or the call is refused FUNCTION_STEP_MISMATCH. |
| action | string | Optional, defaults to "run <step>". A human-readable label, e.g. "verify identity". Put descriptive sentences here, never in step. |
| nonce | string | Optional, generated for you if absent. A value used once per request, for replay protection. If you send one, never reuse it: a repeat is refused REPLAY_NONCE. |
| ts_ms | number | Optional. Your clock, Unix milliseconds, e.g. Date.now(). If sent, it must be within 300 seconds of the gate's clock or the step is refused STALE_TIMESTAMP. If absent, the gate's own clock is used and the freshness check is skipped. Not a number: 400. |
| model_id | string | Optional. Your agent's identifier, e.g. "my-agent-v2", up to 64 characters, no ::. It is kept, prefixed by your account: client:<your client>:my-agent-v2. |
| inputs | object | Optional. Context for the step. It is never stored in the receipt: the receipt carries only a SHA-256 hash of the whole request. Use attestation for anything that must be kept as evidence. |
| attestation | object | Optional. Evidence signed into the receipt at this step, e.g. {"aml_check": "passed", "approved_by": "risk-committee-id"}. It is stored and published verbatim with the receipt, so any later alteration is detectable. On the public demo key the report needs no key, so anything placed here on a demo sequence is world-readable. Can carry hashes, ids and long strings. |
Timestamp freshness: The gate enforces a ±300 second window around the current time. If your request arrives more than 5 minutes early or late, it will be rejected with reason STALE_TIMESTAMP. Always generate ts_ms fresh using Date.now() or equivalent.
Copy. Paste. Run.
This example works against the live wrapper right now. Each snippet generates a fresh timestamp, a unique nonce, and a unique sequence ID on every run, so it returns ALLOW the moment you paste it.
# Paste the whole block. Fresh timestamp, unique nonce + sequence id - runs every time. TS=$(( $(date +%s) * 1000 )) NONCE=$(openssl rand -hex 8) SEQ="my-seq-$(date +%s)" curl -X POST https://api.agenticrail.nz/v1/evaluate \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer DEMO-AGENTICRAIL-PUBLIC-2026' \ -d "{ \"schema_version\": \"1.0\", \"model_id\": \"my-agent\", \"sequence_id\": \"$SEQ\", \"step\": \"verify_identity\", \"function\": \"verify_identity\", \"action_type\": \"CHECK_STATE\", \"action\": \"verify identity\", \"nonce\": \"$NONCE\", \"ts_ms\": $TS, \"inputs\": {}, \"step_order\": [\"verify_identity\", \"assess_risk\", \"execute_transfer\", \"audit_ledger\"] }"
# Paste the whole block into PowerShell $ts = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() $nonce = -join ((1..16) | ForEach-Object { '0123456789abcdef'[(Get-Random -Maximum 16)] }) $seq = "my-seq-$ts" $body = '{"schema_version":"1.0","model_id":"my-agent","sequence_id":"' + $seq + '","step":"verify_identity","function":"verify_identity","action_type":"CHECK_STATE","action":"verify identity","nonce":"' + $nonce + '","ts_ms":' + $ts + ',"inputs":{},"step_order":["verify_identity","assess_risk","execute_transfer","audit_ledger"]}' $headers = @{ "Content-Type" = "application/json"; "Authorization" = "Bearer DEMO-AGENTICRAIL-PUBLIC-2026" } (Invoke-WebRequest -UseBasicParsing -Uri https://api.agenticrail.nz/v1/evaluate -Method POST -Headers $headers -Body $body).Content
ts_ms is optional — the current Unix time in milliseconds; if sent it must be within 300 seconds of server time, and if absent the gate's own clock is used. The snippets generate it for you. Note: date +%s%3N is GNU-only; the cross-platform form $(( $(date +%s) * 1000 )) works on macOS and Linux.
The order in this example is not arbitrary. Under UK anti-money-laundering law, the customer's identity must be verified before the transaction is carried out, so the order is the obligation; the customer due diligence specification sets out where the law says so.
// One gate call — adapt into your agent loop const res = await fetch('https://api.agenticrail.nz/v1/evaluate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer DEMO-AGENTICRAIL-PUBLIC-2026', }, body: JSON.stringify({ schema_version: '1.0', model_id: 'my-agent', sequence_id: 'my-seq-' + Date.now(), step: 'verify_identity', function: 'verify_identity', action_type: 'CHECK_STATE', action: 'verify identity', nonce: crypto.randomUUID().replace(/-/g, '').slice(0, 16), ts_ms: Date.now(), inputs: {}, step_order: ['verify_identity', 'assess_risk', 'execute_transfer', 'audit_ledger'], }), }); const gate = await res.json(); if (gate.decision === 'ALLOW') { // Proceed with your step } else { // Halt — do not proceed console.error('DENY:', gate.reasons); }
No SDK is required. The gate is a plain HTTPS JSON call, exactly as above, so any language that can POST can use it. These packages wrap that call for convenience and ship the integrations below.
# Python — ships LangGraph and CrewAI integrations pip install agenticrail # JavaScript / TypeScript — dual ESM + CJS, plain fetch, any framework npm install @agenticrail/core
Both are MIT-licensed and open source. Evaluate against the live gate with the public demo key above — no account, no signup. step_order is sent on every call, identical each time; the gate locks it on the sequence's first permitted call.
ALLOW or DENY. Nothing in between.
The wrapper returns a flattened response with all decision details at the top level. There is no pack wrapper — the decision, reasons, and execution_submitted fields are directly in the response object. execution_submitted reports whether the wrapper's executor accepted the action, with the detail in result. It is not the signed receipt's executed field, which records only that the step was permitted; the response carries no executed key. The nested receipt object carries the receipt metadata for that decision — key_id, signature_alg, payload_hash, decision_index, and version.
{ "decision": "ALLOW", "execution_submitted": true, "pack_id": "0d2f96b09dafaec463ffa606e65163c561792088756125896210a8874e3eddc5", "reasons": [], "sequence_id": "demo-whakamatau-docs-aa3e2f4f", "step": "verify_identity", "function": "verify_identity", "action_type": "CHECK_STATE", "model_id": "client:demo:my-agent", "result": { "status": "submitted", "data": { "state": null } }, "receipt": { "pack_id": "0d2f96b09dafaec463ffa606e65163c561792088756125896210a8874e3eddc5", "key_id": "k2_2026-06-07_ed25519", "signature": null, "signature_alg": "Ed25519", "payload_hash": "b067f50657de8c2cad4b98e0d4ac851c7be70ab53895e7bd12127d68ef53157f", "prev_receipt_id": null, "prev_receipt_hash": null, "decision_index": 1, "ts_ms": 1791444261196, "version": "slp8_receipt_v2", "attestation": null }, "log": { "ok": true, "error": null } }
{ "decision": "DENY", "execution_submitted": false, "pack_id": "42f5b4d20894835ea4883a48fca11650fa015a7fbe720011f694efa04dfad721", "reasons": ["SEQUENCE_VIOLATION"], "next_expected_step": "assess_risk", "sequence_id": "demo-whakamatau-docs-aa3e2f4f", "step": "execute_transfer", "function": "execute_transfer", "action_type": "CHECK_STATE", "model_id": "client:demo:my-agent", "result": { "status": "skipped", "message": "No execution triggered" }, "receipt": { "pack_id": "42f5b4d20894835ea4883a48fca11650fa015a7fbe720011f694efa04dfad721", "key_id": "k2_2026-06-07_ed25519", "signature": null, "signature_alg": "Ed25519", "payload_hash": "a50a3e8bec77377a35b09bd5829573b1698acf1959731b9fff2d654e80d2f878", "prev_receipt_id": "0d2f96b09dafaec463ffa606e65163c561792088756125896210a8874e3eddc5", "prev_receipt_hash": "cf530a36b8ddec8fa72124d9cfb139d3034452f0872b9e59fd2b2735f95a8f1f", "decision_index": 2, "ts_ms": 1791444262561, "version": "slp8_receipt_v2", "attestation": null }, "log": { "ok": true, "error": null } }
About the signature. The inline receipt carries the decision metadata and the payload_hash. The signature itself is finalized in the durable receipt written to storage — it is not echoed in the synchronous response, so the calling system cannot verify it at the moment of decision, only afterward. That's deliberate: the synchronous path stays lean, and every verification is forced through the one durable, tamper-evident record rather than trusting an ephemeral API response. To verify it, use report.agenticrail.nz/report — the compliance report includes each receipt's raw signature (base64) and its exact signed_canonical preimage, so you can run ed25519_verify(public_key, signed_canonical, signature) yourself, entirely offline, against the published key at /spec/receipt-public-keys.json — no callback to AgenticRail, no trust in our own verification claim required.
execute_transfer before verify_identity has completed.sequence_id.action_type is not in the allowed set for this function/step.step/function is not present in this sequence's own declared step_order. An unrecognised name alone does not deny — it falls through to a permissive generic policy so custom step_order sequences work. Only a name absent from your declared step_order is rejected.function and step fields do not match.attestation.witnessed_pack_id did not match the real prior receipt — missing, wrong, or unverifiable against the durable record.step_order sent differs from the one this sequence was opened with. The declared order is locked on the first permitted call, so a later call cannot shorten it to skip a required step. If the process genuinely changed, start a new sequence_id.sequence_id, step or action_type; a step_order that is not a non-empty list; a non-numeric ts_ms) is refused with invalid_request and a message naming the field, before evaluation, so no receipt is written.Every code above arrives as an entry in the reasons array of a DENY, and every DENY is written to a signed receipt. A request can also be refused before it reaches enforcement, in which case you get a HALT instead. Those are a different class, and they are listed next.
The HTTP status of a DENY. By default both ALLOW and DENY return HTTP 200, so read the decision field, which is what the SDKs and the MCP server do. A caller that reads only the status code — a shell script using curl --fail, a scheduler, a CI step, a no-code webhook step — would treat a DENY as success and run the next step. For those, add ?deny_status=409 to the URL: POST https://api.agenticrail.nz/v1/evaluate?deny_status=409. Every non-ALLOW decision then returns HTTP 409, so the flow stops on its own. The body, the receipt and the signature are the same either way. Any other value for deny_status is refused with a 400 rather than ignored. In n8n, put the flag in the HTTP Request node's URL; the node then fails on a refusal and the workflow stops, with the gate's full answer, next_expected_step included, in the error.
Refused at the door. No receipt.
HALT is not an enforcement decision. ALLOW and DENY are decisions: the gate evaluated your step against the sequence and reached a verdict, and either way a signed decision is issued before your action runs. HALT means the request was rejected at the boundary — oversized, or matching a prompt-injection pattern — and never reached the enforcement engine at all.
A HALT produces no receipt. Nothing was decided, so there is nothing to sign or store. If you are reconciling receipts against calls, HALTed calls will have no corresponding receipt, and that is correct behaviour, not a gap.
A HALT is returned with a non-2xx HTTP status and carries status rather than decision:
{
"status": "HALT",
"halt_gate_step": 1,
"reason_code": "SCHEMA_VIOLATION",
"reason_detail": "REJECT_ROLE_DIRECTIVES"
}
reason_code is the bucket; reason_detail, when present, is the specific trigger.
reason_detail carries the trigger: BODY_TOO_LARGE (413), BAD_JSON (400), BODY_READ_ERROR (400), or one of the injection patterns below (403). The wrapper re-sends your fields to the gate as JSON, so your own Content-Type never reaches this check, and a body that will not parse is refused earlier as invalid_json.attestation, which legitimately holds hashes and IDs.\n that appears in JSON-stringified bodies.attestation object exceeds its size cap.attestation object nests deeper than the allowed depth.404 not_found instead.404 not_found instead.Three rejections happen earlier still, at the public wrapper, and return a plain error field rather than a HALT envelope: invalid_api_key (401) when a real key prefix is presented with the wrong secret, invalid_json (400) when the body will not parse, and rate_limited (429) when you exceed your rate limit. Treat all of these the same way you treat a HALT: the call did not reach enforcement, and there is no receipt.
Sending no key is not one of them. An unrecognised credential — no header at all, a placeholder, or a key that was never issued — does not fail. The call runs on the public demo lane and the response says so, carrying lane, lane_reason and lane_notice. Only a recognised key presented wrongly is refused: a wrong secret returns 401, a revoked key returns 403. The trade is that a demo-lane sequence is prefixed demo- and its report can be read by anyone holding the sequence id, so nothing private belongs in attestation.
The rules the gate enforces.
These aren't soft guidelines. A violation on any one of them returns DENY immediately.
verify_identity → assess_risk → request_approval → execute_transfer. An out-of-order step returns SEQUENCE_VIOLATION.REPLAY_NONCE.function must match step. action_type must be in the allowed set for that function. Invalid combinations return ACTION_NOT_ALLOWED or FUNCTION_STEP_MISMATCH.settle on the built-in spine), the sequence locks. Any further request on that sequence_id returns SEALED_SEQUENCE. Start a new sequence with a fresh ID./v1/evaluate returns 404 not_found and reaches no enforcement.Embed evidence in the receipt.
Each step can carry an attestation object — arbitrary evidence that travels with the request and gets signed into the R2 receipt. Use it to record, at each step, what your system asserts: an AML check passed, a human approved the action, an external system returned a specific result. The gate signs what you send; it does not check that it is true.
The attestation is stored alongside the receipt and appears in every compliance report generated for that sequence. It's not a separate log entry — it's part of the signed receipt.
from agenticrail import RailClient import time, uuid client = RailClient(api_key="DEMO-AGENTICRAIL-PUBLIC-2026") seq = client.sequence(f"payment-run-{uuid.uuid4().hex[:12]}", [ "verify_identity", "assess_risk", "request_approval", "execute_transfer", "audit_ledger" ]) # Attach evidence at each step — signed into the receipt seq.next("verify_identity", attestation={ "kyc_provider": "acme-kyc", "result": "pass", "checked_at": int(time.time() * 1000), }) seq.next("assess_risk", attestation={ "risk_score": 23, "threshold": 50, "decision": "below_threshold", }) seq.next("request_approval", attestation={ "approved_by": "risk-committee-id-7f3a", "approval_ref": "APR-2026-00412", }) seq.next("execute_transfer") # attestation optional — omit if nothing to prove seq.next("audit_ledger") # seals sequence — all attestations locked in chain
import { RailClient } from "@agenticrail/core"; const client = new RailClient({ apiKey: "DEMO-AGENTICRAIL-PUBLIC-2026" }); const seq = client.sequence(`payment-run-${crypto.randomUUID()}`, [ "verify_identity", "assess_risk", "request_approval", "execute_transfer", "audit_ledger" ]); // Attach evidence at each step — signed into the receipt await seq.next("verify_identity", { attestation: { kyc_provider: "acme-kyc", result: "pass", checked_at: Date.now() } }); await seq.next("assess_risk", { attestation: { risk_score: 23, threshold: 50, decision: "below_threshold" } }); await seq.next("request_approval", { attestation: { approved_by: "risk-committee-id-7f3a", approval_ref: "APR-2026-00412" } }); await seq.next("execute_transfer"); // attestation optional await seq.next("audit_ledger"); // seals sequence — all attestations locked in chain
The attestation field accepts any plain JSON object. Values can include strings, numbers, and nested objects. Large binary blobs are not supported — store those in your own system and include a reference ID or hash here instead.
One command. Full provenance report.
The report worker reads every receipt for a sequence, verifies the cryptographic chain, and produces a human-readable compliance report. This is the deliverable your lawyer, auditor, or regulator asks for — proof of what was permitted, and in what order, verified independently of what the agent claims.
Not a log export. Chain-verified receipt evidence, generated on demand for any sequence.
Endpoint: POST https://api.agenticrail.nz/v1/report
Headers: Content-Type: application/json, Authorization: Bearer <key>
Body: { "sequence_id": "your-sequence", "format": "html"|"json" }
Your key reaches your own sequences here. The demo key is restricted to sequences prefixed demo-, and those also verify with no key at all at report.agenticrail.nz/report — that host serves public verification only and refuses a customer key.
Worker scans R2 for all receipts matching the sequence ID, verifies each pack ID hash (multi‑generation logic), validates the receipt chain, and composes a deterministic enforcement_summary from the resulting counts. No language model is involved anywhere in report generation — the same inputs always produce byte-identical text, so the summary reproduces like the rest of the document.
HTML: Full‑page report with cover, sequence summary, enforcement log, chain proof, and the deterministic enforcement summary.
JSON: Structured data containing all verified receipts and verification results.
Read‑only; no state mutation.
# Runs as-is. Swap in your own sequence_id and your own key.
curl -X POST https://api.agenticrail.nz/v1/report \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer DEMO-AGENTICRAIL-PUBLIC-2026' \
-d '{
"sequence_id": "demo-mcp-payment-workflow-001",
"format": "html"
}'
The enforcement summary is deterministic, and apart from the time the report was generated, the same receipts always yield the same report. Verification uses multi‑generation hash checking to handle Gen‑1 and Gen‑2 receipts.
What the receipt chain proves — and to whom.
Every gate call produces enforcement output (ALLOW/DENY). It also produces a compliance record — a signed, numbered, tamper-evident receipt of what the gate permitted and refused, and in what order.
These are the same facts. Different audience, different frame.
"Did the agent proceed correctly? Was the step blocked? Why?"
Answered by the gate decision: ALLOW, DENY, reason codes.
"Which steps was the agent permitted last Tuesday, in what order, and which was it refused — from a record the agent did not write?"
Answered by the receipts: signed, numbered, tamper-evident. They record the decisions, not that each action then ran.
Receipt field reference — what each field proves
| Receipt field | What it proves |
|---|---|
| pack_id | Unique identifier for this enforcement decision — the canonical reference for this chain link. |
| signature (Ed25519) | Tamper evidence. An Ed25519 signature (base64) over the canonical receipt, verifiable offline against the published public key (/spec/receipt-public-keys.json). If the receipt was altered after writing, verification fails. Legacy receipts before 2026-06-07 use HMAC-SHA256. |
| prev_receipt_id | pack_id of the previous receipt — an identifier reference establishing chain order. On its own, proves something with that identifier came before; see prev_receipt_hash below for content-tamper protection. |
| prev_receipt_hash | SHA-256 of the last permitted receipt's full canonical JSON, signature included (added 2026-07-08); the chain runs through ALLOWED receipts only, and decision_index numbers every decision so a removed refusal shows as a gap. An in-place edit to any earlier permitted receipt breaks this link even if prev_receipt_id references still match — this is what actually invalidates the report on tampering. Null for the chain's first receipt and for any link whose anchor predates this field. |
| ts_ms | The time the caller asserted for this step, in milliseconds. Signed, so it cannot be changed afterwards, but self-asserted: the gate refuses it only if it is more than 300 seconds from its own clock. If absent, the gate's clock is used. |
| payload_hash | SHA-256 of the raw request body — the fingerprint of your payload, not the payload itself. Your agent's input data never enters receipt storage. An auditor can verify the receipt is cryptographically bound to a specific payload without ever seeing that payload's contents. |
| attestation | Per-step evidence signed into the receipt — AML check results, human approval tokens, risk scores, KYC references. Records what your system asserted at this step; the gate signs it but does not check it. |
Logs are self-reported — the AI agent tells you what it did. Receipts are issued by the gate, signed before it answers, outside the agent's own reporting. A log has to be taken on trust; a receipt can be verified cryptographically against a published key. That difference is what makes AgenticRail evidence rather than documentation.
Calling the gate from an MCP client
AgenticRail runs a public Model Context Protocol server, so an agent can call the gate as a tool without installing an SDK. It is a thin adapter over the same public API described above: it holds no extra privileges, stores nothing of its own, and enforces nothing the gate does not already enforce.
| Endpoint | https://mcp.agenticrail.nz/ — JSON-RPC over POST /. A GET / returns the discovery card, which is also served at /.well-known/mcp.json; robots.txt and sitemap.xml are served too. Paths for protocols we do not implement — the oauth-* discovery endpoints, agent.json, ai-plugin.json — return 404, and that is the correct answer rather than a fault: a 404 on the OAuth paths is how an MCP server says no auth is required. |
| Transport | Streamable HTTP, stateless. No session to establish and nothing to keep open. |
| Protocol version | 2026-07-28 |
| Registry name | nz.agenticrail/gate, version 1.3.0 |
Two tools are published:
evaluate_step— ask the gate to ALLOW or DENY a single step before it runs. Calls/v1/evaluate. A DENY comes back with the remedy attached: the action types that step does permit, the step the sequence expected next, or the locked step order.verify_receipt— prove a sequence's receipt chain is intact. Calls the report worker, which resolves each receipt's predecessor by anchor and recomputes the verdict fresh.
# Add your own key once you have one. Without it you land on the public demo lane.
{
"mcpServers": {
"agenticrail": {
"type": "http",
"url": "https://mcp.agenticrail.nz/",
"headers": { "Authorization": "Bearer YOUR-KEY" }
}
}
}
Your key travels in Authorization: Bearer and is passed straight through. Send no key and the call still runs — on the public demo lane, and the response says so. Two consequences follow, and they are the reason the notice exists: a demo sequence's report needs no key, so anything placed in attestation is world-readable; and demo receipts are deleted 30 days after they are written. Neither applies once you send your own key.
One thing worth knowing before the first call: each step accepts only a subset of the eight action types, and a sequence that declares its own step_order is enforced against that order verbatim. A first call refused with ACTION_NOT_ALLOWED is usually an invented action_type, not a broken endpoint — the denial carries the list that would have worked. A refused call does not advance the sequence and locks nothing, so the run is not spoiled: the call was wrong, not the sequence. Correct the field the denial names and send the step again.
Ready to go further?
The demo key is open for testing, and you can drive the gate in the browser first — skip a step, replay one, and watch it get refused before you write any code. When you're ready for production — dedicated rate limits, private key, support, and integration help — get in touch.
Wiring this up from code, or pointing an agent at it? The machine-readable API description is published as OpenAPI 3.1 at agenticrail.nz/openapi.json — every endpoint, the full payload contract, all nine denial codes and the response schemas. It is linked as service-desc from /.well-known/api-catalog, so a client that follows RFC 9727 discovery will find it without being told. Framework integrations: the Python SDK ships LangGraph and CrewAI adapters, the JavaScript SDK documents a LangGraph.js example, and MCP clients can call the gate as a tool at mcp.agenticrail.nz.
Working out whether this fits at all, rather than how to call it? The product overview sets out what AgenticRail is in one page: what it enforces, what a receipt contains, what it is used for — EU AI Act Article 12 record-keeping, provable human oversight and sign-off, segregation of duties — how it sits alongside observability, guardrails and orchestration, and what it does not do.
Blunt questions — whether you can self-host, who holds the signing keys, and what a receipt does not prove — are answered on the FAQ.
Looking for the formal side instead? The enforcement specification (versioned, fingerprinted, frozen) and the published briefs — evidence completeness, provable safeguards for automated decisions, sector gap analyses for NZ health and NZ education — all live under /spec/.