SDK reference
Payment requests
Issuing a request, reading one as any wallet, and verifying who signed it.
createRequest(input, idempotencyKey?)
Requires a credential.
const request = await path.createRequest(
{
amount: { value: '5000', asset_type: 'fiat', asset_code: 'USD' },
accepts: [
{
rail: 'book',
asset_type: 'fiat',
asset_code: 'USD',
fees: [{ kind: 'operator', amount: { value: '25', asset_type: 'fiat', asset_code: 'USD' }, paid_by: 'payer' }],
},
],
orderReference: 'ord_10482',
expiresAt: new Date(Date.now() + 86_400_000).toISOString(),
},
`ord_10482`,
);
request.reference; // "7fk2m9pq3vx8"
request.url; // "https://api.example.com/path/7fk2m9pq3vx8"Open amounts
await path.createRequest({
amount: null,
accepts: [{ rail: 'book', asset_type: 'fiat', asset_code: 'USD' }],
});Omit amount and the payer chooses. A first-class case — a tip, a donation, an open invoice — not a
missing field.
Bound what the payer may enter with limits on each entry, in that entry's asset
(PIP-0015):
await path.createRequest({
amount: null,
accepts: [
{
rail: 'book',
asset_type: 'fiat',
asset_code: 'XOF',
limits: {
min_single: { value: '1000', asset_type: 'fiat', asset_code: 'XOF' },
max_single: { value: '50000', asset_type: 'fiat', asset_code: 'XOF' },
},
},
],
});Issued against an address, the limits sit inside the standing intent's; a bound left out is copied
from it. The payment instruction for an open request carries the same limits.
Display rules
State how the request is shown (PIP-0016). A checkout on it is shown the same way.
await path.createRequest({
amount: { value: '12500', asset_type: 'fiat', asset_code: 'XOF' },
accepts,
display: { primary: 'amount', settlement: 'hidden', equivalence: 'EUR' },
});On the paying side, resolve the rules for the screen. funding is the asset of the payer's account:
it applies the floor.
import { resolveDisplay } from '@pathprotocol/sdk';
const rules = resolveDisplay(req, { via: chosenEntry, funding: payerAccountAsset });
rules.primary; // 'amount' | 'quote'
rules.settlement; // 'hidden' | 'asset' | 'asset_and_chain'
rules.equivalence; // 'EUR' — at the wallet's own rate, labelled indicativeWhatever the rules say, show what leaves the payer's account, fees included, and the payee's verified name.
Idempotency
Use your own order id as the key. On the retry after a timeout, when you do not know whether the first call landed, a replay returns the original request instead of creating a second one.
Fees
Each accepts entry carries its fees. They travel inside the signature, so they cannot differ
from what the payer was shown. A price disclosed after the decision is not a disclosure.
readRequest(referenceOrUrl, opts?)
No credential. Accepts a reference or a full URL, so a scanned QR code goes straight in.
const req = await path.readRequest('https://pay.example.com/path/7fk2m9pq3vx8');
req.amount; // { value: "5000", asset_type: "fiat", asset_code: "USD" } — or null
req.issuer; // "example-member"
req.accepts; // capabilities, with fees on each entry
req.status; // "created"Verify before displaying
const req = await path.readRequest(url, { verifyWith: issuerPublicKeyHex });
if (!req.verified) throw new Error('Refusing to display an unverified request');or, fetching the key for you:
import { verifyAgainstIssuer } from '@pathprotocol/sdk';
const { valid, reason } = await verifyAgainstIssuer(req, 'https://api.example.com');The domain says WHERE TO FETCH
The signature says WHO IS RESPONSIBLEThen show the issuer's verified name, never the raw URL. A payer confronted with an unfamiliar hostname closes the app — which is why a card terminal shows a merchant name rather than an acquirer's.
Expiry and revocation
try {
const req = await path.readRequest(reference);
} catch (err) {
if (err instanceof PathApiError) {
switch (err.code) {
case 'path.request.expired': // too late — ask for a new link
case 'path.request.revoked': // the issuer withdrew it
case 'path.core.not_found': // wrong reference entirely
}
}
}The three are distinguished on purpose. A payer told "not found" when a link merely expired will retype it, blame themselves, and call support.
revokeRequest(reference)
Requires a credential. Only the issuer may revoke.
await path.revokeRequest('7fk2m9pq3vx8');Irreversible. A payer opening the link afterwards is told it was revoked, not that it never existed.
markPaid(reference, reason?)
Requires a credential. Separate from issuing the receipt.
A grammar-2 request moves to paid on its own once settled receipts cover it. This call checks
the coverage and answers path.request.not_covered when it falls short.
await path.markPaid('7fk2m9pq3vx8');Checkout — the request plus the return URLs — is its own page.