<!-- Generated raw Markdown for /reference/payment-history; product pin 85fd50ac23a6ba222641a90f954e3a6f87efb85b. -->

SubEtha can expose payment history through the **Facilitator** for a configured payer
and provider. This is a participant-scoped API: a caller is not authorized merely
because it knows a tenant or actor identifier. The record must bind that actor to the
requested participant role, and the request must carry an authenticated principal.

The history API is an optional capability of a Facilitator deployment. It is not
available merely because the standard `/verify` or `/settle` endpoints are running.

## HTTP endpoints

When the history API is registered, the Facilitator exposes these routes. The
ordinary list/read routes require the complete history environment. The proof
routes are attached as a group only when all proof issuer, trust-anchor,
manifest, and durable artifact settings are present; partial proof configuration
fails closed at startup.

| Method | Path | Purpose |
|---|---|---|
| `POST` | `/history/auth/challenge` | Create a wallet-login challenge for `payer` or `provider` |
| `POST` | `/history/auth/verify` | Verify the participant's wallet signature and issue a short-lived identity token |
| `GET` | `/history/payments` | List payments visible to the authenticated participant |
| `GET` | `/history/payments/:paymentId` | Read one visible payment |
| `POST` | `/history/proofs/issue` | Issue a proof when complete proof configuration is enabled |
| `POST` | `/history/proofs/verify` | Verify a proof when complete proof configuration is enabled |
| `POST` | `/history/proofs/revoke` | Revoke a proof when complete proof configuration is enabled |

The proof routes are unavailable unless the complete proof configuration
(issuer, trust anchor, manifest, and durable artifact settings) is enabled;
there is no verify-only or revoke-only deployment
mode. With complete configuration, the official demo and standalone Facilitator
daemon wire issue, offline-capable verification, and revoke through the same
HTTP routes. No-proof-config deployments retain ordinary history list/read capability
without proof publication. Deployments with no proof configuration should be
described as no-proof-config deployments, not as deployments without History.

List/read requests carry the short-lived identity token as an
`Authorization: Bearer` token. `GET /history/payments` returns `{ payments: [...] }`;
pagination is
not part of this verified API surface, so consumers must not infer cursor,
limit, or ordering semantics.

The three proof routes also require an `Authorization: Bearer` token and resolve
the same authenticated participant identity. The route passes that identity and
the requested payment/proof identifier to the History API; authorization for
issuance, verification, and revocation is then enforced by the configured API
policy. The HTTP verify route is an online API call, even though the underlying
verifier can also be used locally with retained artifacts.

## How offline verification works

This is the short version of the flow; the next section defines the verification states and boundaries precisely.

1. **Issue a signed proof.** After a persisted payment reaches `accepted` or `finalized`, the configured issuer creates a proof from that record and signs it. These states are not interchangeable: `accepted` means the request path accepted the payment, while `finalized` means the later proof-gated settlement completed.
2. **Carry the issuer's public key safely.** The issuer publishes a signed key manifest. The verifier checks that manifest against an out-of-band bootstrap trust anchor before using the listed public key to check the proof signature.
3. **Keep the artifacts together.** An operator-managed filesystem export can contain the proof, manifest, and signed status artifacts (for example, `proof.json`, `manifest.json`, and a status chain). The current implementation does not provide a hosted registry or universal availability guarantee.
4. **Verify locally.** Once those artifacts and the trust anchor have been obtained, the verifier checks signatures, the proof's internal payment-binding fields, validity times, and—when supplied—the signed status chain without calling the Facilitator or issuer API. Proof-only verification does not receive the canonical persisted record or salt, so it does not recompute the record commitment. Issuance itself may still require the configured Facilitator or another trusted issuer.
5. **Represent revocation separately.** Revocation appends a signed status to the chain; it does not modify or delete the original proof bytes. The proof remains available as an issuance-time claim while the later status changes its current-status result.

This flow does not promise automatic long-term retention, replication, or protection against later copying of a bearer proof. Operators are responsible for protecting, backing up, rotating, and deleting filesystem exports according to their deployment requirements.

## Offline verification after issuance

An issued proof can be verified without calling the Facilitator or issuer API when the
verifier has the signed proof and the issuer's published key manifest. The manifest is
validated against an out-of-band bootstrap trust anchor and supplies the issuer key
registry used to verify the proof signature.

This is the outage-resilience boundary: if a payer or provider has already received
and retained the signed proof, the manifest, and the bootstrap trust anchor, that
party can later present and verify the issuer's payment attestation without the
Facilitator being online. The attestation can establish the payment lifecycle state
recorded by the issuer—`accepted` for request-path acceptance or `finalized` for the
later proof-gated settlement—but it is not an unconditional replacement for chain
or operator evidence. Current-status claims additionally require the applicable
signed status artifact. Proofs and artifacts must be distributed and retained by
the participants or their operators in advance; the current implementation does
not provide automatic participant delivery, a hosted registry, universal
availability, or automatic retention.

The verification states are deliberately separate:

- **`VALID_AT_ISSUANCE`** — the proof structure, signature, and issuance-time key
  validity verify; the proof carries a signed record-commitment value, but proof-only
  offline verification does not receive the canonical persisted record or salt and
  therefore cannot recompute that commitment.
- **`VALID_NOW`** — the proof is valid, the verified active status artifact is effective at the verification time, and the signing key is not revoked at that time.
- **`REVOKED`** — the proof is structurally valid and either a verified signed revocation
  artifact applies, or an otherwise valid active status is evaluated after the
  manifest's issuer key `revoked_at` has taken effect. Later key metadata alone does
  not change the proof-only result.
- **`STATUS_UNKNOWN`** — a current-status artifact was supplied, but its provenance,
  signature, chain, or effective time cannot be verified. For an unexpired valid proof
  with no status artifact, the verifier returns `VALID_AT_ISSUANCE` and makes no
  current-status claim; an expired proof returns `EXPIRED`.
- **`EXPIRED`** — the proof's validity window has ended.
- **`INVALID`** — the proof structure, signature, or manifest trust cannot be verified.

Evaluation is ordered by validity first: malformed or untrusted proof data is
`INVALID`; an expired proof is `EXPIRED`; an unexpired proof without a
status artifact is `VALID_AT_ISSUANCE`, while a supplied but unverifiable status
artifact is `STATUS_UNKNOWN`; `VALID_NOW` or `REVOKED` requires a verified
effective status or key-revocation condition.

Status artifacts are signed append-only publications bound to the proof, payment,
issuer, and key. They change the status assertion, not the held proof itself. The
verification path is offline after the artifacts have been obtained; proof issuance
may still involve the configured Facilitator or another trusted issuer.

New lifecycle proofs are bound to a canonical persisted payment record through a
per-record commitment. Caller-supplied commitments or evidence are not authoritative
for this path. An issued proof remains a bearer artifact: participant authorization
protects the online history API, but does not prevent later copying of an offline proof.

## Wallet-bound authentication

The configured participant wallet signs the exact newline-delimited challenge
message returned by `/history/auth/challenge` using an EIP-191 personal-sign
(`viem.verifyMessage`) signature. The message contains:

- the history authentication purpose and domain;
- `tenantId`, `actorId`, and the participant subject (`payer` or `provider`);
- a random nonce; and
- the challenge expiry.

The verified implementation uses these limits:

- challenge lifetime: **300 seconds**;
- issued identity-token lifetime: at most **3600 seconds**;
- per-client-IP-plus-subject challenge issuance limit: **10 per minute**;
- per-client-IP-plus-subject verification limit: **20 per minute**;
- pending challenge capacity: **10,000**.

The rate-limit key uses the client IP derived from Express `req.ip` together with the
requested subject; deployments using a trusted proxy should configure that proxy
behavior deliberately because it affects the client-IP component of the key.

A challenge is single-use. A signature for another wallet, another subject, an expired
challenge, or a replayed nonce is rejected. The issued token carries the authenticated
participant identity; spoofed `x-history-*` headers are not an identity source in
wallet-token mode. A deployment may explicitly wire the legacy resolver, which
reads `x-history-tenant` and `x-history-actor`; that compatibility path is not
wallet authentication and should be limited to a trusted network.

## Participant scope and projections

A configured history record binds the payer and provider independently, for example:

```text
payer:    tenant-a / payer-1
provider: tenant-b / provider-1
```

The payer can read the record only with the payer subject and matching payer binding.
The provider can read it only with the provider subject and matching provider binding.
An unrelated actor, a subject mismatch, or a record without a required participant
binding fails closed in the configured History API, including the standalone path
when its complete history environment is present.

The projections are role-specific:

- **Payer:** settlement linkage and operational secrets such as mint, treasury, and
  linkage fields are redacted. The payer view does not expose the real recipient
  destination through this history surface.
- **Provider:** the provider projection may include provider-side settlement evidence,
  subject to the deployment's record and authorization configuration.

Authorization and participant binding are separate checks. A tenant grant alone does
not establish that the actor participated in a particular payment.

## Lifecycle and persistence

The Facilitator lifecycle adapter records an intent before an irreversible burn, then
records accepted and finalized transitions as the settlement progresses. A failed
history-intent write rolls back the in-memory reservation so a payment can be retried;
it does not silently accept a payment without its required history intent.

The official demo attaches durable JSONL payment history to settlement when
`PAYMENT_HISTORY_PATH` is set. The standalone facilitator constructs and attaches
that persistence only when the complete History environment is present, including
`PAYMENT_HISTORY_PATH`, the identity secret, and both payer/provider tenant and
actor bindings. Neither path exposes the participant-aware HTTP API from the path
variable alone; the complete HTTP History configuration is required for that:

```bash
export PAYMENT_HISTORY_PATH=./payment-history.jsonl
export HISTORY_IDENTITY_SECRET='[REDACTED]'
export HISTORY_PAYER_TENANT=tenant-a
export HISTORY_PAYER_ACTOR=payer-1
export HISTORY_PROVIDER_TENANT=tenant-b
export HISTORY_PROVIDER_ACTOR=provider-1
```

Wallet-bound HTTP login additionally requires `HISTORY_PAYER_ADDRESS` and
`HISTORY_PROVIDER_ADDRESS`. Keep the identity secret and wallet/private-key material
in the environment or a secret manager; never put real values in source, shell
history, or public documentation.

The verified persistence implementation is append-oriented and does not define an
automatic record-retention period. Operators should choose file protection, backup,
rotation, and deletion policies appropriate to the payment data they retain.

## Relationship to payment settlement

History is an audit/read surface, not a replacement for the payment protocol:

- `accepted` means the payment was accepted on the request path;
- `finalized` means the later proof-gated settlement completed;
- a history record does not make `/settle` synchronous with finalization.

See [accepted vs finalized](/concepts/accepted-vs-finalized/) and the
[Facilitator quickstart](/quickstarts/facilitator/) for the settlement flow.
