API reference

One primitive. Three adoption surfaces. The receipt format is a public spec — anyone can implement or verify without trusting the hosted service.

1Bootstrap a tenant§

Returns a tenant_id, an api_key (shown once — store it now), and the tenant's public_key_b64 (used for offline verification).

curl -X POST https://humanaccepted.ola-turmo.workers.dev/bootstrap \
  -H "Content-Type: application/json" \
  -d '{"name":"My Co","domain":"my.co","plan":"free"}'

# 201 Created
# {
#   "tenant_id": "tn_01HXY3K8M2Q9",
#   "api_key":   "***",
#   "public_key_b64": "8Uq4A4zK…",
#   "algorithm": "ed25519",
#   "plan": "free"
# }

2Record an acceptance§

The primitive. One call, one signed receipt. The server hashes both payloads, stores them to R2, signs the canonical form with the tenant's Ed25519 key, and returns the receipt.

curl -X POST https://humanaccepted.ola-turmo.workers.dev/v1/accept \
  -H "Authorization: Bearer $HUMANACCEPTED_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "human":  { "id": "u_olav", "email": "olav@my.co" },
    "ai":     { "provider": "openai", "model": "gpt-5.5",
                "draft": "This is the AI draft..." },
    "output": { "final": "This is the final, edited version." },
    "context": {
      "purpose": "marketing-email-draft",
      "ai_act_class": "limited_risk",
      "tools_used": ["gpt-5.5"],
      "policy_version": "p_2026.06"
    }
  }'

# 201 Created
# {
#   "id": "rcp_01HXY3K8M2Q9F4W7TA5V6BPE0N",
#   "version": 1,
#   "issued_at": "2026-06-09T16:30:00.000Z",
#   "tenant": { "id": "tn_…", "name": "My Co", "domain": "my.co" },
#   "human": { "id": "u_olav", "email_hash": "sha256:…" },
#   "ai":    { "provider": "openai", "model": "gpt-5.5",
#              "draft_hash": "sha256:…",
#              "draft_ref":  "r2://…" },
#   "output":{ "final_hash": "sha256:…",
#              "final_ref":  "r2://…" },
#   "context": { "purpose": "marketing-email-draft", "ai_act_class": "limited_risk" },
#   "signatures": { "tenant_ed25519": "ed25519:…", "cf_attestation": null }
# }

3Verify a receipt (public, no auth)§

Anyone can verify. The hosted endpoint reads the tenant's public key from its Durable Object and checks the Ed25519 signature.

curl https://humanaccepted.ola-turmo.workers.dev/verify/<tenant_id>/<receipt_id>

# 200 OK (valid)
# { "valid": true, "receipt_id": "…", "tenant_id": "…", "issued_at": "…",
#   "purpose": "…", "human_id": "…", "ai_act_class": "…" }

# 200 OK (invalid)
# { "valid": false, "reason": "tenant signature did not verify" }

# 404
# { "valid": false, "reason": "not_found" }

For offline verification (no API call), the receipt format is a public spec with 5 reference verifiers (Python, Go, TypeScript, Rust, Elixir — all 4/4 conformant with byte-exact canonical form). See the spec →

4List receipts (auth)§

curl "https://humanaccepted.ola-turmo.workers.dev/v1/receipts?limit=50&ai_act_class=limited_risk" \
  -H "Authorization: Bearer $HUMANACCEPTED_KEY"

# 200 OK
# { "receipts": [{...}, {...}], "next_cursor": "rcp_…" }

5OpenAI-style tool call§

Drop this into any OpenAI / Anthropic / Vercel AI SDK / Cloudflare Agents tool-call definition. The framework calls human_accept_this when AI output needs human approval, and the SDK ships this exact schema.

{
  "type": "function",
  "function": {
    "name": "human_accept_this",
    "description": "Records a signed receipt that a human reviewed and approved AI-generated work. Returns a receipt id with the Ed25519-signed canonical payload. Useful for your own AI Act Art. 14 (human oversight) and Art. 12 (logging) workflows — HumanAccepted is the evidence layer, not a compliance platform. Pass the `Idempotency-Key` header (Stripe-style) to make retries safe."
    "parameters": {
      "type": "object",
      "properties": {
        "ai_draft":      { "type": "string", "description": "The raw AI output (before human edits)." },
        "final_output":  { "type": "string", "description": "The human-accepted final output." },
        "human_id":      { "type": "string", "description": "Tenant-scoped user id." },
        "purpose":       { "type": "string", "description": "Free-text purpose. e.g. 'marketing-email-draft'." },
        "ai_act_class":  { "type": "string", "enum": ["limited_risk", "high_risk", "minimal_risk", "unclassified"] },
        "tools_used":    { "type": "array",  "items": { "type": "string" } }
      },
      "required": ["ai_draft", "final_output", "human_id", "purpose"]
    }
  }
}

6Webhooks§

Subscribe to receipt.created and receive a signed POST on every approval. The body is the full signed receipt; the signature is HMAC-SHA256 over the JSON body, prefixed with sha256=.

# Register a webhook
curl -X POST https://humanaccepted.ola-turmo.workers.dev/v1/webhooks \
  -H "Authorization: Bearer $HUMANACCEPTED_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your.app/humanaccepted-webhook","events":["receipt.created"]}'

# 201 Created
# { "id": "whk_…", "url": "https://your.app/humanaccepted-webhook",
#   "events": ["receipt.created"], "secret": "***", "active": true }

# The delivery (one POST per receipt.create)
# POST https://your.app/humanaccepted-webhook
# Headers:
#   X-Accepted-Event: receipt.created
#   X-Accepted-Delivery: whk_…
#   X-Accepted-Signature: sha256=9058e6db653bbf0d…
#   User-Agent: humanaccepted-webhook/0.1
# Body:
#   { "event": "receipt.created", "ts": 1781030584536, "data": { ...receipt... } }

SDKs§

# Python (3.9+)
pip install humanaccepted
from humanaccepted import Accepted
client = Accepted(api_key="sk_…")
receipt = client.accept(
    human_id="u_olav",
    ai_draft="…",
    final="…",
    purpose="marketing-email-draft",
)

# TypeScript / Node 18+
npm install @humanaccepted/sdk
import { Accepted } from "@humanaccepted/sdk";
const client = new Accepted({ apiKey: "sk_…" });
const receipt = await client.accept({
  humanId: "u_olav",
  aiDraft: "…",
  final: "…",
  purpose: "marketing-email-draft",
});

Errors§

All errors are JSON. error is a stable code, message is human-readable.

StatusCodeMeaning
400validationA required field is missing or wrong type.
401missing_authorizationNo Authorization: Bearer … header.
401invalid_api_keyAPI key not found.
401api_key_revokedAPI key was explicitly revoked.
404not_foundReceipt or tenant doesn't exist.
429rate_limitedTenant exceeded per-minute or per-day limit. Retry with backoff.
500tenant_key_missingTenant Durable Object lost its signing key. Re-bootstrap.

Receipt format spec§

The full receipt format is a public spec, Apache-2.0 on the verifier, CC-BY-4.0 on the prose. Read it in the repo: docs/receipt-format.md →

Implementations exist in TypeScript, Python, and the Worker itself. The canonical form (recursive-sorted-keys, keep nulls, drop only undefined) is byte-exact between implementations.

AI Act oversight mapping§

HumanAccepted is not a compliance platform. It is the evidence layer. The receipt includes the fields a compliance team typically uses to document human review and AI oversight — use them as part of your own AI Act (or any other regulatory) workflow, alongside your policy, risk, and consent systems.

A receipt of this shape is useful evidence for four articles of the EU AI Act. The receipt is not itself a complete compliance pack; it is the audit-trail primitive that lets the rest of the pack be machine-verifiable.

ArticleWhat's coveredReceipt field(s)
Art. 12 — LoggingImmutable log record of the AI output + human actionai.draft_hash + output.final_hash + human.id + issued_at
Art. 14 — Human oversightProof a human reviewed the AI outputhuman.id + human.auth_method + human.approver_session
Art. 9 — Risk managementRisk class + versioned policy at time of approvalcontext.ai_act_class + context.policy_version
Art. 13 — TransparencyDocumented AI system usedai.provider + ai.model + context.tools_used