Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.algovoi.co.uk/llms.txt

Use this file to discover all available pages before exploring further.

The cancellation receipt records that a recurring-payment mandate (or any standing payer→payee authorisation) has been cancelled, by whom, for what reason, and with what effective date. It closes the lifecycle gap between recurring execution (settlement attestations) and post-cancellation refund (where owed). It is AlgoVoi-authored, specified in IETF Internet-Draft draft-hopley-x402-cancellation-receipt (POSTED on IETF datatracker 2026-05-25, Independent Submission, Informational), and published as a standalone reference implementation:
  • TypeScript: @algovoi/cancellation-receipt (live, v0.1.0)
  • Python: algovoi-cancellation-receipt (PyPI publish pending rate-limit clearance; npm package live)
Both packages depend on algovoi-substrate / @algovoi/substrate for the JCS canonicalisation primitive. Apache 2.0.

Lifecycle position

admission       settlement       cancellation     refund (if owed)
compliance  --> settlement   --> cancellation --> refund
receipt         attestation      receipt          receipt
All four formats anchor to the same canonicalisation discipline (urn:x402:canonicalisation:jcs-rfc8785-v1). A verifier walking the audit chain confirms admission → recurring execution → termination → (optional) refund under one byte-deterministic pin.

Why a four-state enumeration

Mandate termination is genuinely four-state. The regulatorily-load-bearing distinctions are:
  • Payer revocation under PSD2 (Directive 2015/2366) Article 64 + UK Consumer Rights Act 2015 — MAY trigger refund obligations on debits already settled prior to the effective date.
  • Payee termination under PSD2 Article 72 + contractual terms — does NOT trigger consumer-revocation refund-window obligations on already-settled debits.
  • Operator/compliance-forced termination (sanctions, KYC, AML, court order) — anchors POCA s.330 / AML 5+6 evidence chain back to the originating compliance event.
  • Time-based expiry — mandate’s own terms terminated it. Standard record-keeping only.
A collapse to three-state (e.g. PARTY_REQUESTED + AUTO_TERMINATED) loses the payer-vs-payee distinction that drives the PSD2 Article 64 refund-window obligation.

Receipt shape

A cancellation receipt is a seven-field JSON object canonicalised under RFC 8785 (JCS). Field names are sorted lexicographically by JCS during canonicalisation.
{
  "canon_version": "jcs-rfc8785-v1",
  "cancellation_provider_did": "did:web:api.algovoi.co.uk",
  "cancellation_reason": "USER_REQUESTED",
  "cancellation_timestamp_ms": 1716494400000,
  "effective_from_ms": 1716537600000,
  "jurisdiction_flags": ["UK", "EU"],
  "mandate_ref": "sha256:0dd5d0b76c9b9281fdeb2509ad38ab132b16a17385ca01d976ff9e6e12563a0f"
}
FieldTypeDescription
canon_versionstringIn-band canonicalisation pin. Fixed jcs-rfc8785-v1.
cancellation_provider_didstringDID URI of the issuing party.
cancellation_reasonstring (closed enum)USER_REQUESTED / MERCHANT_REQUESTED / COMPLIANCE_TERMINATED / EXPIRED.
cancellation_timestamp_msintegerEpoch ms when the cancellation event was recorded. Substrate Rule 2.
effective_from_msintegerEpoch ms when the cancellation takes legal effect. MUST be >= cancellation_timestamp_ms.
jurisdiction_flagsordered arrayISO-3166-1 codes. Order significant under RFC 8785.
mandate_refstringsha256:{hex} reference to mandate setup record.

The closed enumeration: cancellation_reason

ValueInitiatorRegulatory significance
USER_REQUESTEDPayerPSD2 Article 64 right of revocation. UK Consumer Rights Act 2015. May trigger refund obligation under Article 64 if recent debits already settled.
MERCHANT_REQUESTEDPayeePSD2 Article 72 + contractual terms. Does NOT trigger consumer-revocation refund-window obligations.
COMPLIANCE_TERMINATEDOperatorSanctions / KYC / AML / court order. Triggers POCA s.330 / AML 5+6 evidence chain.
EXPIREDNone (time-based)Mandate’s own end-state. Standard record-keeping only.
Each value produces a byte-distinct content_hash. Free-form “reason” strings or operator-internal codes are not acceptable substitutes.

Two timestamps

The cancellation receipt records two epoch-millisecond integer timestamps independently:
  • cancellation_timestamp_ms — when the cancellation event was observed and recorded by the issuing provider.
  • effective_from_ms — when the cancellation takes legal effect.
For most cancellations these are equal. For PSD2 Article 64(3)(a) direct-debit revocations, the effective time is typically end-of-business-day prior to the next scheduled execution — distinct from the moment the revocation was recorded. The independent encoding supports that operational separation. The format pins the invariant effective_from_ms >= cancellation_timestamp_ms. Implementations MUST reject receipts where the effective time precedes the recording time.

Composition

A cancellation receipt mandate_ref MAY reference a compliance receipt content_hash (the receipt that admitted the mandate). A USER_REQUESTED cancellation may chain forward to a refund receipt if PSD2 Article 64 refund is owed on a recently-settled debit:
compliance receipt (ALLOW for mandate setup)
    |
    v   (settled_payment_ref or operator-layer link)
settlement attestation (recurring execution N)
    |
    v   (chain via prev_hash)
cancellation receipt (USER_REQUESTED, effective T+1 day)
    |
    v   (original_payment_ref references settlement)
refund receipt (FULL, if Article 64 refund owed)
A verifier walking this chain confirms the full mandate lifecycle under one canonicalisation pin.

Conformance vectors

8 byte-level reference vectors + 7 pair invariants + 3 chain invariants at vectors/cancellation_receipt_v1/. The vector set pins:
  1. Closed four-element cancellation_reason enumeration — four byte-distinct content_hash values.
  2. jurisdiction_flags array order (RFC 8785 §3.2.3).
  3. canon_version in-band pin.
  4. effective_from_ms >= cancellation_timestamp_ms invariant.
  5. mandate_ref content-addressing (sha256: prefix retained in canonical bytes).
  6. Audit chain row linkage via prev_hash.

Quick start

TypeScript

import { buildCancellationReceipt } from "@algovoi/cancellation-receipt";
import { sha256Jcs } from "@algovoi/substrate";

const r = buildCancellationReceipt({
  cancellation_reason: "USER_REQUESTED",
  cancellation_timestamp_ms: 1716494400000,
  effective_from_ms: 1716537600000,
  cancellation_provider_did: "did:web:api.algovoi.co.uk",
  mandate_ref:
    "sha256:0dd5d0b76c9b9281fdeb2509ad38ab132b16a17385ca01d976ff9e6e12563a0f",
  jurisdiction_flags: ["UK", "EU"],
});
console.log(sha256Jcs(r));
// e50f8d4f9fcd8b0cf142bd65528a90d8814dd71127d4529ca77137b4b41ad407

What this is NOT

  • Not a refund receipt. Cancellation records the termination of a mandate; refund records the reversal of a settled payment. When a USER_REQUESTED cancellation triggers a refund, both receipts are emitted and chained via the audit-chain.
  • Not a dispute receipt. Disputes are state machines over multiple parties; cancellation is a single state transition.
  • Not an attestation of mandate validity. The receipt records the termination of a previously-valid mandate; the mandate itself is specified elsewhere.

Companion IETF Internet-Draft

draft-hopley-x402-cancellation-receipt (Independent Submission, Informational). AlgoVoi-authored. Normatively references draft-hopley-x402-canonicalisation-jcs-v1. Welcomes downstream-adopter contributions per the established Appendix C “Known Adopters” pattern.

See also