API reference
Receipts
Issuing, reading and verifying settlement receipts — the one format that makes a mobile-money transfer as provable as an on-chain one.
Issuing needs a member credential. Reading and verifying do not, and that is the point: a receipt is only useful if a third party can check it.
POST /receipts
{
"issued_by": "payee",
"request": "7fk2m9pq3vx8",
"via": { "rail": "book", "asset_type": "fiat", "asset_code": "USD" },
"received": { "value": "5000", "asset_type": "fiat", "asset_code": "USD" },
"rail_references": [{ "type": "book_entry", "value": "LE-8891-2231" }]
}{
"type": "path.receipt",
"reference": "rcpt_9a41c8f2b731",
"issuer": "example-member",
"issued_by": "payee",
"payer": { "member": "member-b" },
"payee": { "member": "example-member" },
"request": "7fk2m9pq3vx8",
"instruction": null,
"via": { "rail": "book", "asset_type": "fiat", "asset_code": "USD" },
"sent": null,
"received": { "value": "5000", "asset_type": "fiat", "asset_code": "USD" },
"fx": null,
"fees": [],
"rail_references": [{ "type": "book_entry", "value": "LE-8891-2231" }],
"attestations": [],
"status": "settled",
"reason_code": null,
"supersedes": null,
"route": null,
"grammar": 2,
"protocol_version": "0.2.0",
"signed_at": "2026-09-09T10:04:12Z",
"kid": "op_example_2026_01",
"signature": "…"
}| Field | Notes |
|---|---|
issued_by | payer or payee. A payee receipt may state settled. A payer receipt is pending or failed |
request | Optional — a spontaneous payment has no request. The public reference from the link, never an internal id |
instruction | The public reference of the payment instruction, when one was used |
via | The capability used |
sent / received | Amounts. received is required on a settled payee receipt |
rail_references | Typed by the rail. Required on settled. A pending receipt may omit them — the rail has not named the transaction yet |
status | pending or settled. Absent means settled on a payee receipt |
supersedes | null at issuance. Set on every transition to the digest of the envelope it replaces |
route | Always null. PIP-0005 is the only shape it may take |
The reserved field
route is present on every receipt. PIP-0005 specifies the only shape it may take
(legs, providers, eta_seconds). Implementations MUST emit null until a later PIP names who
may fill it.
POST /receipts/{reference}/transitions
Issuer's credential. Moves the receipt and returns a new signed envelope.
{ "to": "failed", "reason_code": "rail_unavailable" }{
"type": "path.receipt",
"reference": "rcpt_9a41c8f2b731",
"status": "failed",
"reason_code": "rail_unavailable",
"supersedes": "4c1e77b9…",
"via": { "rail": "pix", "asset_type": "fiat", "asset_code": "BRL" },
"rail_references": [],
"route": null,
"grammar": 2,
"protocol_version": "0.2.0",
"signed_at": "2026-09-21T10:06:44Z",
"kid": "op_example_2026_01",
"signature": "…"
}| Field | Notes |
|---|---|
to | settled, failed or reversed. pending → settled | failed, settled → reversed, and nothing else — anything else answers path.settlement.transition_invalid |
reason_code | Required on failed. Closed list: rail_unavailable, rail_rejected, destination_unknown, destination_closed, limit_exceeded, compliance_blocked, insufficient_funds, expired |
reason | Free text, never normative. A reader switches on reason_code |
rail_references | Supplied here when the rail only names the transaction once it has gone through |
The envelope already handed out stays as signed. A transition returns a new statement. supersedes
is the digest of the previous envelope. Both remain readable and both remain valid.
Re-stating the state a receipt already holds returns the current envelope unchanged: a duplicate rail callback is ordinary and must not append a second statement.
GET /receipts/{reference}
Public. A supplier, an auditor, a customs officer, the payer's own institution.
Putting this behind a credential would mean the only people who can verify a settlement are the two who already agree about it.
Returns the current envelope — the most recent statement in the chain.
GET /receipts/{reference}/transitions
Public. Every statement ever signed about this settlement, oldest first.
{
"reference": "rcpt_9a41c8f2b731",
"status": "failed",
"transitions": [
{
"from": null,
"to": "pending",
"reason_code": null,
"occurred_at": "2026-09-21T10:04:12Z",
"envelope": { "…": "signed at issuance" }
},
{
"from": "pending",
"to": "failed",
"reason_code": "rail_unavailable",
"occurred_at": "2026-09-21T10:06:44Z",
"envelope": { "…": "signed at the transition" }
}
]
}Each envelope verifies on its own. A reader holding only the current one can check a signature and
still not know whether the money ever moved, or moved and came back — which is most of what a
third party came to find out.
POST /receipts/verify
{
"envelope": { "type": "path.receipt", "…": "…" },
"issuer_public_key": "d75a980182b10ab7…"
}{ "valid": true }{ "valid": false, "reason": "Signature does not verify against the supplied key" }You supply the key. This endpoint does not fetch it for you, deliberately: an operator vouching for another operator's key rebuilds the trust hierarchy the protocol avoids, and would mean compromising one operator compromises statements about others.
Fetch it from the issuer's own discovery document. The SDK does both steps:
import { verifyAgainstIssuer } from '@pathprotocol/sdk';
const { valid, reason } = await verifyAgainstIssuer(receipt, 'https://api.other-member.com');Verifying offline
A receipt verifies with nothing but the envelope and the issuer's public key — no API call at all, once you hold the key.
That is what makes a receipt presentable as a QR code, scanned by someone with no account anywhere, and it is the concrete form of finality is a verifiable state, not a promise.
Reconciliation
RESERVED — the shape below is specified and
no endpoint writes it. PATH-SETTLE.Reconciliation is a separate profile for exactly this reason,
and this implementation does not declare it. What follows describes where it is going, not what it
does.
Three views of one event, which must agree:
{
"receipt": "rcpt_9a41c8f2b731",
"observations": [
{ "source": "provider", "observed": "5000", "observed_at": "2026-09-09T10:04:12Z" },
{ "source": "statement", "observed": "4975", "observed_at": "2026-09-10T02:00:00Z", "discrepancy": "25" }
]
}Discrepancies are recorded, not silently corrected. A system that quietly reconciles differences away is one where nobody notices the fee that was never disclosed, or the rail that rounds in one direction.