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
{
"request_id": "7fk2m9pq3vx8",
"payer_member": "member-b",
"amount": "5000",
"currency": "XOF",
"rail": "mobile_money",
"source_reference": "MM-8891-2231"
}{
"kind": "settlement_receipt",
"reference": "rcpt_9a41c8f2b731",
"request": "7fk2m9pq3vx8",
"payer_member": "member-b",
"payee_member": "example-member",
"amount": "5000",
"currency": "XOF",
"rail": "mobile_money",
"source_tx_hash": null,
"source_reference": "MM-8891-2231",
"attestations": [],
"route": null,
"protocol_version": "0.1.0",
"signed_at": "2026-09-09T10:04:12Z",
"kid": "op_example_2026_01",
"signature": "…"
}| Field | Notes |
|---|---|
request_id | Optional — a spontaneous payment has no request, and that is normal |
source_tx_hash | For rails that have one |
source_reference | For rails that do not. At least one of the two must be present |
route | Always null in v0.1. Reserved for liquidity routing |
The reserved field
route is declared on every receipt and never populated. When routing is specified it will hold the
legs, providers, shares and expected duration.
It exists now so that arrives as an extension. Added later, it would be a migration for everyone who had already shipped — and that is the cheapest insurance in the specification.
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.
POST /receipts/verify
{
"envelope": { "kind": "settlement_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
DRAFTThree 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.