Quickstart — First Trace in 60 Seconds
This page records one AI decision into Adjudon's tamper-evident
SHA-256 hash chain and verifies the row offline. Target: under
60 seconds from a clean install. You ship the trace, we ship the
chain row. The integration is one HTTPS call against the production
API at api.adjudon.com (Frankfurt eu-central-1); the rest of this
site is the operational answer to what happens after that call lands.
Prerequisites
- Python 3.10+ or Node 18+
- An Adjudon account (any plan; Sandbox works for this Quickstart)
- A workspace API key — create one at the dashboard's Settings → API Keys; see Authentication for the format
- A terminal that lets you set environment variables
Install
pip install adjudon
npm install @adjudon/node
curl --version # ensure curl is available
Get an API key
The key looks like adj_live_<64 hex characters>. Create one in the
dashboard, copy it from the modal — the raw value appears only
once and we cannot recover it — and export it as an environment
variable. Do not hardcode the key in source: the prefix is recognised
by GitHub's secret scanner and most IDE plugins, so an accidental
commit is detected, but the simpler protection is never to write the
raw value into a tracked file. See Authentication
for the full storage and rotation procedure.
export ADJUDON_API_KEY="adj_live_<your-64-hex-key>"
export ADJUDON_BASE_URL="https://api.adjudon.com"
The base URL is Frankfurt eu-central-1; do not point any other host at this. A quick liveness check before sending a real trace:
curl https://api.adjudon.com/health
{
"status": "ok",
"service": "adjudon-api",
"version": "1.0.0",
"timestamp": "2026-05-06T10:14:22.317Z",
"uptimeSeconds": 86400,
"checks": { "database": "connected" }
}
/health returns liveness only; it never carries customer data and
never requires authentication.
Record your first trace
The single core call: a POST /api/v1/traces with three required
fields — agentId, inputContext.prompt, and
outputDecision.action. Everything else (rationale, metadata,
triggering condition) is optional and passes through to the chain
row's payloadDigest. Send the request with an Idempotency-Key
header so a network retry does not produce a second trace; the server
returns the original response on a duplicate key for 24 hours.
curl -X POST https://api.adjudon.com/api/v1/traces \
-H "Authorization: Bearer $ADJUDON_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"agentId": "underwriter-v1",
"inputContext": {
"prompt": "Loan application: 50000 EUR, 36-month term"
},
"outputDecision": {
"action": "deny",
"rationale": "Income/debt ratio exceeds policy threshold"
}
}'
import os, requests, uuid
r = requests.post(
"https://api.adjudon.com/api/v1/traces",
headers={
"Authorization": f"Bearer {os.environ['ADJUDON_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"agentId": "underwriter-v1",
"inputContext": { "prompt": "Loan application: 50000 EUR" },
"outputDecision": { "action": "deny", "rationale": "..." },
},
)
r.raise_for_status()
print(r.json())
import { randomUUID } from "node:crypto";
const res = await fetch("https://api.adjudon.com/api/v1/traces", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ADJUDON_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify({
agentId: "underwriter-v1",
inputContext: { prompt: "Loan application: 50000 EUR" },
outputDecision: { action: "deny", rationale: "..." },
}),
});
console.log(await res.json());
The status code carries product semantics. A successful approve
returns 201 Created and the agent should proceed with the action.
A flag-for-review returns 202 Accepted and the agent should pause
while a human reviews the decision in the Review Queue. A policy
block returns 403 Forbidden with code ADJ_BLOCKED_BY_POLICY and
the agent must not proceed. The body in every case is the same shape
— the status code tells you which path the policy gate took:
{
"success": true,
"data": {
"traceId": "trace_aBcD1234",
"agentId": "underwriter-v1",
"status": "approved",
"confidenceScore": 0.83,
"tags": [],
"matchedPolicy": null,
"createdAt": "2026-05-06T10:14:22.317Z"
}
}
Copy the traceId. The hash-chain row is appended asynchronously (the
ingestion path is non-blocking); it is queryable within milliseconds
of the response.
Verify on the chain
The hash-chain row is the durable artefact a regulator will eventually
read. The trace POST returns synchronously; the chain append runs
fire-and-forget and lands within milliseconds. Pull the chain row for
the traceId you just received:
curl https://api.adjudon.com/api/v1/hash-chain/entry/trace_aBcD1234 \
-H "Authorization: Bearer $ADJUDON_API_KEY"
r = requests.get(
f"https://api.adjudon.com/api/v1/hash-chain/entry/{trace_id}",
headers={"Authorization": f"Bearer {os.environ['ADJUDON_API_KEY']}"},
)
entry = r.json()["data"]["entry"]
print(entry["sequence"], entry["chainHash"])
The response carries the row's sequence, prevHash, payloadDigest,
and chainHash — the four fields the published verify algorithm
needs. See Hash Chain API for the full
endpoint surface.
The chain is replay-verifiable offline. Run the algorithm yourself
against the bundle export at /api/v1/hash-chain/export and you have
a procurement-grade audit artefact, no Adjudon login required.
What just happened
Three things, in order. One, the trace was scrubbed of generic
PII (email, IBAN, credit-card, SSN, phone) before any storage; what
ingested into MongoDB Atlas Frankfurt was the redacted payload, not
the raw input. Two, the Confidence Engine triangulated against
three independent signals (base probability, variance across
ensembles, historical precedent) and produced confidenceScore plus
tags. Three, the Policy Engine evaluated the trace against your
organisation's active policies and decided 201 / 202 / 403. The
chain row anchored the whole sequence into a per-organisation SHA-256
chain that is replay-verifiable offline.
See Audit Log & Security for the chain formula and the four-step verify algorithm.
- Wrong base URL. Production lives at
https://api.adjudon.com. Pointing a key at any other host returns DNS or401. 401 INVALID_API_KEY_FORMAT. The key must matchadj_live_<64-hex>. A trimmed-down or wrapped value fails the prefix check.429 RATE_LIMIT_EXCEEDED. Per-agent and per-key limits apply — back off with exponential delay; see Rate Limits.- Idempotency-Key reused. A second POST with the same key returns the original response, not a new trace. That is by design.
Next steps
- Authentication — key formats, storage, rotation
- Hash Chain API — the full endpoint surface for verify and export
- Traces & Confidence — the three pillars in detail
- Idempotency — the
Idempotency-Keyheader in detail - Tracing a multi-step agent — cookbook recipe for the next integration shape
- Python SDK, Node SDK — the high-level client