GitHub
PATH REQUEST

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.

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 RESPONSIBLE

Then 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.

On this page