SDK reference
Settlement
Issuing receipts, reading them, and verifying one produced by someone else.
issueReceipt(input)
Requires a credential.
const receipt = await path.issueReceipt({
issuedBy: 'payee',
request: '7fk2m9pq3vx8',
via: { rail: 'book', asset_type: 'fiat', asset_code: 'USD' },
received: { value: '5000', asset_type: 'fiat', asset_code: 'USD' },
railReferences: [{ type: 'book_entry', value: 'LE-8891-2231' }],
});
receipt.reference; // "rcpt_9a41c8f2b731"
receipt.signature;
receipt.kid;| Field | Notes |
|---|---|
issuedBy | 'payer' or 'payee' |
request | Optional — a spontaneous payment has no request. The public reference from the link, never an internal id |
via | The capability used |
railReferences | Typed by the rail. Required on 'settled' |
status | 'pending' or 'settled'. Defaults to 'settled' on a payee receipt |
attestations | Optional signed claims travelling with the receipt |
Separate from marking a request paid, because payment and proof are two events that can be seconds or hours apart. Conflating them forces every integration to pretend every rail settles as fast as a signature.
Issue 'pending' when the rail confirms later. It is the only route to a signed statement that a
payment failed — issue straight to 'settled' and the only document you can ever produce is one
asserting success.
transitionReceipt(reference, input)
Requires the issuer's credential.
// the rail came back
await path.transitionReceipt('rcpt_9a41c8f2b731', {
to: 'settled',
railReferences: [{ type: 'book_entry', value: 'E1234…' }],
});
// or it did not
await path.transitionReceipt('rcpt_9a41c8f2b731', {
to: 'failed',
reasonCode: 'rail_unavailable',
});Returns a new signed envelope. The one you were holding stays valid and stays readable.
supersedes is the digest of that envelope.
| Field | Notes |
|---|---|
to | 'settled' | 'failed' | 'reversed'. pending → settled | failed, settled → reversed |
reasonCode | Required on 'failed'. Closed list — see PATH SETTLEMENT |
reason | Free text. Switch on reasonCode, never on this |
'reversed' follows 'settled' only. A failure never moved value; a reversal moved it and moved it
back.
readReceiptChain(reference)
No credential.
const chain = await path.readReceiptChain('rcpt_9a41c8f2b731');
chain.status; // "failed"
chain.transitions.length; // 2
chain.transitions[1].reason_code; // "rail_unavailable"Each envelope in the chain verifies on its own. Worth reading rather than trusting the latest one
alone: the current state says the money is there, the chain says whether it ever left, came back, or
arrived on the second attempt.
readReceipt(reference)
No credential.
const receipt = await path.readReceipt('rcpt_9a41c8f2b731');Public because a receipt is only useful if a third party can check it — a supplier, an auditor, the payer's own institution. Behind a credential, the only people who could verify a settlement would be the two who already agree about it.
Verifying someone else's
import { verifyAgainstIssuer } from '@pathprotocol/sdk';
const { valid, reason } = await verifyAgainstIssuer(receipt, 'https://api.other-member.com');The key comes from the issuer's own discovery document. Not from whoever handed you the receipt, and not from a central registry: an operator vouching for another's key rebuilds the hierarchy the protocol avoids, and means compromising one operator compromises statements about others.
Offline
import { verifyEnvelope } from '@pathprotocol/sdk';
const valid = verifyEnvelope(receipt, cachedIssuerPublicKey);Once you hold the key, verification needs no network at all. That is what lets a receipt be shown as a QR code and checked by someone with no account anywhere — the concrete form of finality is a verifiable state, not a promise.
Unknown key id
const { valid, reason } = await verifyAgainstIssuer(receipt, issuerUrl);
// reason: 'Issuer publishes no key with kid "op_example_2026_02"'Reported distinctly from a bad signature, because it usually means a rotation you have not picked up. Refetch the issuer's document and try again before concluding anything.
The reserved field
receipt.route; // null — implementations still emit nullPIP-0005 defines the only shape route may take (legs, providers, eta_seconds).
This SDK types it as ReceiptRoute | null and emits null. Carry the field through. Do not strip
it, and do not reject an object because a counterpart populated it.