GitHub
PATH REQUEST

API reference

Checkout sessions

A payment request plus the hand-back — the only part of checkout that crosses between two organisations.

LIVE

A session is a payment request plus the two things a merchant flow needs and a bare link does not: where to send the payer back, and where to notify the merchant.


POST /checkout/sessions

Requires a member credential.

{
  "request_reference": "7fk2m9pq3vx8",
  "success_url": "https://store.example.com/thanks?order=10482",
  "cancel_url": "https://store.example.com/cart",
  "webhook_url": "https://store.example.com/hooks/path",
  "expires_at": "2026-09-09T10:30:00Z"
}
{
  "id": "cs_4b81f2c7",
  "reference": "7q2mfk9pv3x8dn4wjb",
  "url": "https://pay.example.com/path/7q2mfk9pv3x8dn4wjb",
  "status": "open",
  "request_reference": "7fk2m9pq3vx8",
  "success_url": "https://store.example.com/thanks?order=10482",
  "cancel_url": "https://store.example.com/cart",
  "expires_at": "2026-09-09T10:30:00Z"
}

Only the issuer of the request may open a session on it, and only while the request is created.

The url is an ordinary PATH link: a wallet scans it, reads a signed path.checkout object and never learns the id. Which is why the two payer-facing routes below take the reference, not the id.


GET /checkout/sessions/{id}

Requires a member credential, and only the issuer's. This is the merchant's own view, and it carries fields a payer never sees. A session that belongs to someone else answers 404, not 403: confirming that it exists is already the leak.

The payer-facing read is GET /path/{reference}, which returns the signed object.

{
  "id": "cs_4b81f2c7",
  "reference": "7q2mfk9pv3x8dn4wjb",
  "status": "open",
  "success_url": "https://store.example.com/thanks?order=10482",
  "cancel_url": "https://store.example.com/cart",
  "payer_member": null,
  "attempts": 0,
  "expires_at": "2026-09-09T10:30:00Z",
  "completed_at": null
}

payer_member

The wallet completing the session — and where this stops being theoretical.

Today a checkout completes only inside the wallet its provider chose. Under PATH it may be a wallet at another member entirely, and the merchant integrates nothing extra. That field being populated with a name the merchant has never heard of is the protocol working.


POST /checkout/sessions/{reference}/attempt

Requires a member credential. Called by the payer's wallet when it starts. No body.

{ "reference": "7q2mfk9pv3x8dn4wjb", "attempts": 1 }

payer_member is taken from the calling credential and is not a field you send. It is the claim that a wallet at another member served this checkout — self-declared, it would be worth nothing, and anyone could pin a merchant's failed attempts on a member that had done nothing.

Attempts are counted, never overwritten. A payer who tries three wallets before one works is a normal story, and a merchant investigating a complaint needs all three.


POST /checkout/sessions/{reference}/revoke

Requires the local member credential. The merchant withdraws an open session. No body. open becomes revoked.

{
  "reference": "7q2mfk9pv3x8dn4wjb",
  "status": "revoked",
  "revoked_at": "2026-09-09T10:14:22Z"
}

POST /checkout/sessions/{reference}/complete

Requires a member credential. The hand-back: the wallet is done and the customer can go back to the merchant. No body.

{
  "reference": "7q2mfk9pv3x8dn4wjb",
  "status": "completed",
  "completed_at": "2026-09-09T10:14:22Z",
  "success_url": "https://store.example.com/thanks?order=10482"
}

Only an open session can be completed, and the condition is evaluated in the write itself, so two wallets finishing at the same instant cannot both close it.

This does not mark the underlying request paid. A payer declaring a session complete is not evidence that money moved, and a demand must not settle on the debtor's own word. The request is settled by its issuer, or proved by a receipt. The session tracks the flow; the request tracks the debt.


Lifecycle

Fig. 01 — Lifecycle
open
completed
expired
revoked

What is not here, and why

Partial payments, several concurrent attempts against one session, and refunds tied to an order are RESERVED for a later version.

The line that keeps this pillar small: PATH specifies what crosses between the merchant's provider and the payer's wallet. The basket, tax, shipping, inventory, promotion codes and the payment page belong to whoever built the store — which is where products should differ.

If the session later acquires a lifecycle of its own — partial settlement, dispute tied to delivery — that is the signal to reconsider whether the demand deserves richer treatment. It is a decision to take with evidence, not in advance.


Webhooks

DRAFT
EventWhen
checkout.session.completedThe session completed
checkout.session.expiredIt expired unpaid
request.paidThe underlying request was marked paid
receipt.issuedA settlement receipt was issued

Deliveries are signed with the operator's key, same envelope as everything else, so a receiver verifies them the way it verifies anything.

Treat delivery as at-least-once. Duplicates happen; make handlers idempotent on the event id. Re-marking a paid request as paid is deliberately not an error, for exactly this reason.

On this page