Receipts, or it didn't happen.
Sentinel turns every action — human or AI — into a hash-chained, tamper-evident receipt. There are only two verbs to learn: seal an action, and verify the chain. Everything else is detail.
Seal an action
Append a receipt to the ledger. Who acted, in what lane, on what, and the result — locked to the receipt before it.
Receipts API →Verify the chain
Re-check every link. Returns PASS, or the exact receipt where integrity broke. No key required — anyone can verify.
Verify endpoint →The verify endpoint is public and keyless. Sealing receipts needs a workspace key — mint one in the console. Base URL throughout these docs: https://sentinel.matchain.io/v1.
01 Quickstart
From a key to your first sealed receipt in five minutes.
- Get a key. Mint a workspace key in the console. Set it in your environment as
SENTINEL_KEY. - Seal your first action. One POST. Humans and agents use the same call.
- Verify. Re-check the chain — no key needed.
# seal an action
curl -X POST https://sentinel.matchain.io/v1/receipts \
-H "Authorization: Bearer $SENTINEL_KEY" \
-d '{"operator":"agent:planner-7","role":"planner",
"action":"deploy","target":"svc/checkout@1.9.2","scope":"ok"}'
→ { "seq":1069, "hash":"a1f0…e9", "prev":"9c4b…2d" }
# verify the whole chain — no key required
curl https://sentinel.matchain.io/v1/verify
→ { "ok":true, "links":1069, "root":"0x9f4c…e21" }
# vendor sdk/python — not yet on PyPI
from sentinel import Sentinel
s = Sentinel(api_key=os.environ["SENTINEL_KEY"])
r = s.receipts.seal(
operator="agent:planner-7", role="planner",
action="deploy", target="svc/checkout@1.9.2", scope="ok",
)
print(r.seq, r.hash)
v = s.verify()
assert v.ok, f"broken at #{v.broken_at}"
// vendor sdk/js — not yet on npm
import { Sentinel } from "./sdk/js/src/index.js";
const s = new Sentinel({ apiKey: process.env.SENTINEL_KEY });
const r = await s.receipts.seal({
operator: "agent:planner-7", role: "planner",
action: "deploy", target: "svc/checkout@1.9.2", scope: "ok",
});
console.log(r.seq, r.hash);
const v = await s.verify();
if (!v.ok) throw new Error(`broken at #${v.brokenAt}`);
02 Authentication
Sealing receipts is authenticated with a workspace key. Verifying is not — integrity is public by design.
- Bearer key. Send your key as
Authorization: Bearer SENTINEL_KEYon every write. Keys are workspace-scoped; one key can emit for many operators and agents. - Keyless verify.
GET /v1/verifyneeds no credentials. A regulator, a partner, or a stranger can re-check your chain at any time — that is the point. - Rotate freely. Issue and revoke keys per workspace. A revoked key cannot seal new receipts; receipts it already sealed stay valid and attributable.
- Never the content. A key authorizes sealing metadata, never reading your underlying data. Sentinel sells proof of integrity, not access.
03 Receipts API
Two endpoints carry the whole surface. Everything is JSON over HTTPS.
/v1/receipts
Seal one action into the ledger.
Request body fields:
| Field | Type | Required | Description |
|---|---|---|---|
operator | string | yes | Who acted — a human handle or agent:<id>. |
role | string | yes | The lane the operator acted in (e.g. planner, coder, reviewer). |
action | string | yes | What was done (e.g. deploy, payment, read, signoff). |
target | string | yes | What it was done to — a stable identifier. |
scope | string | no | Firewall verdict: ok or denied. Defaults to ok. |
cost_cents | number | no | Metered cost of the action, if you track it. |
meta | object | no | Free-form key/values you want sealed alongside the action. |
/v1/verify
Re-check the chain. Optional ?from=&to= range.
Returns { "ok": true, "links": N, "root": "0x…" } on a clean chain, or { "ok": false, "broken_at": N } pointing at the first receipt whose hash no longer matches. The root is the Merkle root of the chain. On-chain anchoring of that root is built but not yet enabled — see Anchoring below.
04 Receipt schema
What you get back. The last three fields are what make it tamper-evident.
| Field | Type | Description |
|---|---|---|
seq | integer | Monotonic position in the ledger. |
operator · role · action · target · scope | string | The action, exactly as sealed. |
cost_cents | number | Metered cost, if supplied. |
ts | string | RFC-3339 timestamp the receipt was sealed. |
prev | string | Hash of the previous receipt — the link backward. |
hash | string | This receipt's fingerprint (SHA-256 over its fields + prev). Change any field and it no longer matches. |
sig | string | Operator signature over hash. |
05 Errors & rate limits
| Status | Meaning | What to do |
|---|---|---|
401 | Missing or invalid key | Check the Authorization header. Verify needs no key. |
403 | Scope denied | The action is out of the key's lane — recorded, not silent. |
422 | Malformed receipt | A required field is missing or the wrong type. |
429 | Rate limited | Back off; the Retry-After header tells you how long. Quotas scale by plan. |
5xx | Sentinel error | Safe to retry — sealing is idempotent on a client-supplied idempotency_key. |
Free tier is 1,000 reads/day. Paid plans raise the ceiling; the Regulated band is bespoke. Regulator access is unlimited and free, forever.
06 SDKs
Thin wrappers over the same two calls. No SDK required — raw HTTP works from anywhere.
Status: the Python and Node SDKs are built and used in production, but are not yet published to PyPI or npm. Vendor the source from sdk/, or call the HTTP API directly — it is the same two calls.
Python
# vendor sdk/python — not yet on PyPI
from sentinel import Sentinel
s = Sentinel(api_key=KEY)
s.receipts.seal(operator="u:ana", role="ops",
action="payout", target="inv:4821")
Node / TypeScript
// vendor sdk/js — not yet on npm
import { Sentinel } from "./sdk/js/src/index.js";
const s = new Sentinel({ apiKey: KEY });
await s.receipts.seal({ operator: "u:ana",
role: "ops", action: "payout", target: "inv:4821" });
07 Concepts
The architecture, in plain terms — the public half of the whitepaper. No secret sauce, just the load-bearing ideas.
Hash-chain
Each receipt's fingerprint is computed over its own fields plus the previous receipt's hash. Edit any past entry and its hash changes, which breaks the next link, and the next — the verifier rejects everything downstream. History becomes append-only without a database that promises it; the math promises it.
On-chain anchoring
Receipts are hash-chained and tamper-evident today: any edit to sealed history is detectable by anyone, with no account and no trust in us. Anchoring — periodically stamping the chain's current Merkle root onto a public chain, so that not even Sentinel could rewrite history behind it — is built and not yet enabled. Until it is, we say tamper-evident. We do not say tamper-proof.
Firewall-as-code
Scope is a denylist enforced mechanically, not a convention people remember. An out-of-lane action is blocked and recorded as a denied receipt — attributable, never silent. The boundary is in code, so it cannot be quietly skipped.
Consent-gated reads
Reputation derived from receipts is aggregate-only and consent-gated by architecture. No tier, anywhere, unmasks an individual. Sentinel sells verifiability — never the underlying identity.
The full technical whitepaper — threat model, signature scheme, anchoring cadence, and the competency-ladder rollout — is shared with partners and reviewers under request. Ask for a copy →