Verifying a Sequesign receipt.
A Sequesign receipt is designed to be verified offline by anyone who has it. This post walks through what verification actually does, what the receipt package looks like, and what a minimal verifier needs to check.
What a receipt package contains
A Sequesign receipt is a directory or zip file with a small, fixed structure:
receipt.sequesign/
receipt.json receipt envelope: metadata + agent/witness/approval/counterparty attestations
actions.jsonl one action record per line
evidence/ hashed evidence content
act_001_task_created.json
act_002_policy_checked.json
...
attestations.jsonl deferred approval/counterparty satellites (optional)
keys/ public keys for verification
agent.pub.pem
witness.pub.pem
verification-report.json output of the last verifier run (optional)
The receipt envelope at the top is a single JSON object that names the chain, declares the receipt mode (freeform, schema-validated, or profile-constrained), references the schemas and profile if applicable, and lists the final chain state. Everything else in the package is content that the envelope references by hash.
What the verifier checks
Verification is deterministic. Given the same receipt and the same public keys, every verifier in the world produces the same report. There is no fuzziness, no model inference, no scoring. The verifier returns a structured report with flags for each property it checked.
The checks, in order:
1. Chain integrity. The verifier walks the chain from the initial state forward. For each action, it computes the action record hash from the canonical action record, computes the next chain state from the previous state and the action record hash using the chain extension function, and confirms the new state matches what is recorded. Any mismatch fails the receipt with reason chain_state_mismatch.
2. Evidence hashes. For each action, the verifier canonicalizes the corresponding evidence file, hashes it, and confirms the hash matches what the action committed to. Any mismatch fails with reason evidence_hash_mismatch.
3. Agent signatures. Each action carries an agent signature over a canonical message that includes the action record hash, the previous chain state, and the new chain state. The verifier confirms the signature verifies under the agent's declared public key. Failure here means the agent did not actually sign this action, or the message has been altered.
4. Witness signatures. Each chain extension carries a witness attestation. The verifier confirms the witness signature verifies under the witness's published public key. The witness key is discovered via a well-known endpoint, or for offline verification, it is included in the receipt's keys directory.
5. Schema validation, if requested. If the receipt mode is schema-validated or profile-constrained, the verifier loads each declared schema, confirms its hash matches what the receipt committed to, and validates each evidence object against its schema. Failure here means the evidence does not match the shape it claimed to follow.
6. Workflow profile, if requested. If the receipt is profile-constrained, the verifier loads the declared profile and confirms the action sequence satisfies the profile's rules. This catches missing required actions, prohibited transitions, and other workflow-level errors.
7. Approval attestations. If the receipt carries approvals (human or agent), the verifier confirms each is signature-valid under the approver's public key, bound to this receipt's task and a recorded action, and signed by a key distinct from the executing agent. Valid approvals surface as the independent approval badge (present_unverified) and are enumerated in the report; they do not change the base level.
8. Counterparty attestations. If the receipt carries counterparty confirmations, the verifier confirms each signature under the counterparty's embedded public key and that it is bound to the confirmed action's evidence. Valid confirmations surface as the independent counterparty badge (present_unverified) and are enumerated.
9. Witness inclusion and completeness. When inclusion proofs are bundled (or the witness log is reachable), the verifier checks each witnessed entry's proof and, given a completeness checker, that no chain entries are missing. These record how strongly the transparency log backs the receipt; on their own they do not fail an otherwise-valid one.
What the report looks like
The verifier's output is structured for both human and machine reading:
{
"valid": true,
"verification_level": "L3_POLICY_BOUND",
"receipt_mode": "profile_constrained",
"trust_anchor_mode": "external",
"agent_identity": {
"kind": "registered",
"key_fingerprint": "sha256:a3f7b8c2e91d4f6a2c8e1f3b9d6c5e7fa3f7b8c2e91d4f6a2c8e1f3b9d6c5e7f",
"registered_at": "2026-05-02T18:30:00Z"
},
"profile": {
"profile_id": "sequesign.invoice_payment.v0.1",
"profile_hash_verified": true
},
"flags": {
"hash_integrity": true,
"sequence_integrity": true,
"schema_valid": true,
"workflow_profile_valid": true,
"witnessed": true,
"agent_identity_bound": true,
"policy_bound": true,
"approval": "present_unverified",
"counterparty": "absent",
"completeness_verified": null,
"inclusion_proofs_verified": "passed"
},
"approvals": [
{
"approval_id": "appr_3c2e91d7b6a8",
"approver_id": "cfo@acme.com",
"approver_key_fingerprint": "sha256:1a4f7c9d6b2e8a3c5f9b2d8a4e6c1b7e1a4f7c9d6b2e8a3c5f9b2d8a4e6c1b7e",
"party_type": "human",
"approved_action_type": "invoice_payment_approved",
"approved_at": "2026-05-14T16:48:12Z"
}
],
"counterparties": [],
"reason": null
}
The flags are the truth. verification_level is the identity-anchored base — one of L0_INTEGRITY_ONLY … L3_POLICY_BOUND — and it is not inflated by who else signed off. Approval and counterparty are independent badges (absent / present_unverified / present_verified): here the human approval is signature-valid and bound to the receipt, so it reads present_unverified and is enumerated under approvals (the present_verified tier, which adds registered-identity vouching, is a later release). A null flag means the property was not checked, not that it failed — completeness_verified is null here because confirming no entries are missing requires a witness-log check the offline run does not perform; pass a completeness checker to populate it.
When verification fails, the report includes a specific reason and pinpoints the affected action:
{
"valid": false,
"reason": "evidence_hash_mismatch",
"action": {
"sequence": 3,
"action_type": "llm_invoice_reviewed"
},
"expected_evidence_hash": "a3f7b8c2e91d4f6a2c8e1f3b9d6c5e7f",
"computed_evidence_hash": "b2e8f4a7c93d5a1e3b7c9f2d8a4e6c1b"
}
Running the verifier yourself
The reference implementation includes a verifier you can run locally on any receipt. The verifier is offline by design; it reads the receipt package and the public keys it needs, and it produces a report without contacting any external service. This is intentional. A receipt is supposed to be verifiable by anyone who has it, including someone with no relationship to Sequesign and no internet connection.
The web demo at sequesign.com runs this same verifier server-side and renders the report in the browser. There is no model inference, no heuristic, no magic. What you see in the demo is exactly what a local verifier produces from the same inputs.
The SDK, which is in progress, will expose the verifier as a one-line import so any system can verify receipts as part of its own workflow. Until then, the reference implementation is the canonical source.