Docs
Quickstart
From nothing to a payment request another wallet can read, in about fifteen minutes.
This walks through the smallest useful integration: issue a payment request, read it back the way a foreign wallet would, and verify who signed it.
Nothing here requires network membership.
1. Install
npm install @pathprotocol/sdk2. Read an operator's discovery document
Always the first call. It tells you what an operator speaks, what it claims, and — the part worth reading before anything else — how its directory behaves.
import { PathClient } from '@pathprotocol/sdk';
const path = new PathClient({
baseUrl: 'https://api.example.com',
apiVersion: '2026-10-04.genesis',
});
const config = await path.discovery();
console.log(config.protocol_versions); // ["0.2.0", "0.1.0"]
console.log(config.conformance); // ["PATH-ID.Core", "PATH-ADDR.Format", …]
console.log(config.finder.hashing); // "server_side" | "oblivious"finder.hashing says whether the network sees the identifiers being searched. server_side means
it does. You are entitled to know that before joining, and to decline. See Privacy.
3. Issue a payment request
This needs a member credential, since it creates an object signed in your name.
const path = new PathClient({
baseUrl: 'https://api.example.com',
apiVersion: '2026-10-04.genesis',
credential: {
kid: 'member_2026_01',
privateKeyHex: process.env.PATH_MEMBER_KEY!,
memberSlug: 'your-member',
},
});
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(),
});
console.log(request.url); // https://api.example.com/path/7fk2m9pq3vx8
console.log(request.reference); // 7fk2m9pq3vx8Pass amount: null and the payer chooses — a tip, a donation, an open invoice.
Fees sit on each accepts entry and travel with the terms, shown before the payer commits. A price
disclosed after the decision is not a disclosure.
4. Read it as another wallet would
No credential. This is the path a wallet at a different institution takes.
const anyWallet = new PathClient({ baseUrl: 'https://api.example.com' });
const req = await anyWallet.readRequest(request.url);
console.log(req.amount); // { value: "5000", asset_type: "fiat", asset_code: "USD" }
console.log(req.status); // "created"
console.log(req.issuer); // "your-member"
console.log(req.fees); // { network_fee: "0", … }5. Verify the signature
The domain that served the link says where it was fetched from. The signature says who is responsible for it — and with delegated hosting those are routinely different parties.
import { verifyAgainstIssuer } from '@pathprotocol/sdk';
const { valid, reason } = await verifyAgainstIssuer(req, 'https://api.example.com');
if (!valid) throw new Error(`Refusing to display: ${reason}`);Do this before showing a payment screen. Then show the issuer's verified name, never the raw URL — the same reason a card terminal shows a merchant name rather than an acquirer's hostname.
6. Close the loop
When the payment lands, mark the request paid and issue a receipt.
await path.markPaid(request.reference);
await path.issueReceipt({
issuedBy: 'payee',
request: request.reference,
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' }],
});railReferences is required on a settled receipt. A receipt nobody can trace back to a movement
on a rail is an assertion, and the point of a receipt is that a stranger can go and check.
The receipt is signed and publicly readable, so a third party — an auditor, a supplier, the payer's own institution — can verify it without an account anywhere.
What you just did without joining anything
Read an operator's terms, issued a signed request, read it as a stranger, and verified its origin.
That is the relationship profile, and it is a complete path.
The one thing it does not include is finding someone you have no prior relationship with. That needs a directory, and directories belong to networks.