Skip to main content

Conversational Commerce Rails

The checkout guide covers one purchase flow end to end. This guide covers the five rails that flow can ride on, one worked snippet each, from first config call to a terminal state: agent payment mandates, direct carrier billing (DCB), hosted pay links, the agent-facing storefront, and recurring subscriptions. You will:
  1. Reach each rail through the PAY-API
  2. Watch the rails in the console
  3. Handle inbound routing and lifecycle events
  4. Diagnose the common failure modes
  5. Simulate in sandbox, then cut over to live
All requests go to a single base URL:
Every request carries your key in the X-API-Key header (or a session JWT). Use a sandbox key (dv_test_sk_…) while you build; swap in a live key (dv_live_sk_…) for production. Every rail round-trips a serializable snapshot — the API computes the next valid state; you persist the returned snapshot verbatim.

1. PAY-API ergonomics

A mandate is a scoped, revocable, spend-capped consent token that lets an AI agent complete a purchase on a customer’s behalf without re-prompting. Issue it once:
Then follow the lifecycle: dry-run a charge with /agent-mandate/authorize (returns the decision without advancing spend), commit it with /agent-mandate/charge (advances spend and returns the updated snapshot + authorization + an AP2 verifiable credential), check integrity with /agent-mandate/verify (recomputes the consent digest — tamper evidence for auditors), and withdraw consent with /agent-mandate/revoke (terminal). To settle on a card network’s agentic program instead of a hosted link, present the authorized charge to /agent-mandate/network-token (Mastercard Agent Pay or Visa Trusted Agent Protocol).
A charge outside the scope rejects with VALIDATION_ERROR; a successful authorize/charge response carries reason: "ok" and, when settlement runs, an x402 settlement receipt with network + transaction hash.

1.2 DCB — confirm code relay, capture, settlement

Direct carrier billing charges the customer’s mobile operator bill instead of a card. Onboard the merchant first — settlement currency, a digital-goods category, a per-transaction cap, and the tenant-level coverage map of country+operator pairs (live, pilot, unsupported — only live pairs may be charged):
Initiate a charge (validates coverage + cap — the buyer MSISDN is masked on the snapshot), then relay the operator confirm-code request the buyer’s carrier sends:
Build the operator request body with /dcb/operator-request (passes the raw MSISDN — it never lands on the snapshot) and submit it to the carrier-billing route. When the buyer replies with the confirm code, capture with /dcb/charge/capture (partial capture honoured); on decline, mark /dcb/charge/fail. Refund with /dcb/charge/refund (partial → partially_refunded, full headroom → refunded); record an operator-initiated reversal with /dcb/charge/chargeback. Close the batch with /dcb/reconcile, which matched/mismatched/missing/unexpected-flags the operator settlement records and advances matched charges to their settled form. A hosted pay-by-link is the channel-agnostic fallback every checkout guide reaches when no native wallet exists. Mint it, render it for the buyer’s channel, drive its lifecycle (MARK_VIEWED / MARK_PAID / EXPIRE / CANCEL), and reconcile the captured PSP payment:
expiresAt is the TTL you set — past it, /payment-request/transition with EXPIRE moves the request to expired (terminal). Hand the rendered content to your per-channel send call; the Commerce API produces link content only. When the PSP webhook reports the capture, close the request out:
An exact amount+currency match flips the snapshot to paid — the terminal state.

1.4 Storefront — publish the agent-facing catalog wiring

The storefront is the tenant-owned surface a third-party AI shopping agent (ChatGPT Instant Checkout, Perplexity, AP2) discovers and drives with no Orbit credential. Publish (or replace) the stored config once — merchant name, currency, categories, and a bounded catalog (max 250 products):
The response carries the storefrontId, the publicUrl you hand to a shopping agent, and the normalized discovery manifest. From then on the public edge serves the manifest + product feed with no further calls from you; read the stored config back with GET /commerce/agentic/storefront. A shopping agent opens a checkout (/agentic/checkout), prices it server-side against your catalog, and completes it under a signed mandate (/agentic/checkout/complete) — your tenant-owned controls govern the catalog, the mandate scope, and whether the storefront is published at all. (A UCP-native agent uses the same primitives on the /ucp/* paths; that adapter is a design-partner pilot gated per tenant.)

1.5 Subscriptions — renewal cadence and dunning

A subscription reuses the consent the customer already gave as a mandate, so a renewal collects no new consent. Start it off an existing cart with an interval (weekly, biweekly, monthly, quarterly) and a channel (whatsapp or rcs):
Use /subscriptions/list to annotate and sort a set by next-charge date (soonest first). When a cycle falls due, run the dunning cadence — the renewal re-authorizes against the stored mandate and mints the reorder’s checkout:
The response carries the advanced schedule, the updated mandate, the reorder checkout payload, and the webhookEvent your subscriber consumes (event, cycleNumber, chargeReference, nextChargeAt). A declined charge returns VALIDATION_ERROR — the dunning loop retries the renewal against a re-issued mandate instead of advancing the schedule. Pause/resume/cancel move the lifecycle; a nextChargeAt that fell in the past while paused is re-anchored one interval out on resume, so resuming never fires a backlog.

2. Console walkthrough

In the dashboard, open Messages → Commerce (/messages/commerce). The hub page links the five rail consoles — Agent mandate, DCB, Pay links, Storefront, Subscriptions — and the message side rail lets you jump from any conversation thread into the rail page the thread’s charge rides on. Use the consoles to watch what the API calls above mutate:
  • Agent mandate — the consent ledger: scope, spend against caps, active/exhausted/revoked status, and the network-token mints a charge produced.
  • DCB — operator-billed charges by country+operator, their lifecycle state (initiatedcaptured/failedrefunded/charged_back), and the settlement reconciliation result (matched/mismatched/missing/unexpected).
  • Pay links — every minted link with its rendered channel, TTL expiry, and the history view of each charge’s status (pendingviewedpaid/expired/cancelled) plus the PSP reconciliation reference.
  • Storefront — the published config a shopping agent sees: merchant details, categories, the catalog the public edge serves, and the public URL.
  • Subscriptions — the schedule with next-charge dates and the renewal ledger (cyclesCompleted, the last renewal reference, pause/resume/cancel state).
The history view on each rail page is where a charge’s configured terminal state is visible: for a mandate, the settled authorization; for DCB, the reconciled settlement; for a pay link, the paid snapshot with the PSP reference; for a subscription, the last successful renewal cycle.

3. Inbound routing and lifecycle events

  • DCB confirm-code reach — the operator sends the buyer an SMS with a confirmation code; the buyer’s MO reply routes through Orbit’s inbound messaging and your application submits the confirming dip to the carrier-billing route before you /dcb/charge/capture. An unanswered confirm code leaves the charge initiated until you /dcb/charge/fail it.
  • Subscription opt-out on cancel — when a buyer texts a stop keyword or cancels in-thread, cancel the schedule with /subscriptions/cancel (terminal) and let your normal opt-out suppression take the number — the rail stops renewing and the consent surface (the mandate) should be revoked with /agent-mandate/revoke too.
  • Charge-refund webhook events — for pay links the PSP webhook closes the request via /payment-request/reconcile; for subscriptions each renewal emits the webhookEvent object above; for DCB a refund or chargeback the operator reports is recorded with /dcb/charge/refund or /dcb/charge/chargeback so the settlement report flags it.

4. Common failure modes

  • Carrier rejects a DCB charge — the merchant coverage map lists the country+operator pair as pilot or unsupported, or the amount exceeds maxChargeAmount. /dcb/charge rejects with VALIDATION_ERROR; either route the buyer to a pay link or update the merchant coverage to a live pair. A charge the operator declined mid-flight is terminal failed via /dcb/charge/fail.
  • Mandate token expiry — a charge against an expired (or cap-exhausted) mandate returns authorized: false with the reason. Re-issue a fresh mandate off the customer’s new consent, or fall back to a hosted pay link. For a card-network fallback the /agent-mandate/network-token route returns 503 NETWORK_TOKEN_NOT_CONFIGURED when the program is not provisioned — fall back to /agent-mandate/charge.
  • Pay-link TTL — once expiresAt passes, lifecycle transitions other than EXPIRE reject. Mint a fresh link; never resurrect an expired one (the history view shows it terminal).
  • Storefront schema validation — a PUT /agentic/storefront body missing a required product key (productId, title, price, currency) or over the 250-product bound rejects with VALIDATION_ERROR. A checkout completion whose re-priced total exceeds the mandate scope is rejected so an agent can never spend past signed consent.
  • Subscription renewal declined/subscriptions/renew validates the schedule is active and due and the mandate authorizes the charge; a mandate decline or a not-due schedule returns VALIDATION_ERROR. Re-issue consent, then retry — do not advance the schedule on a declined cycle.

5. Sandbox and live cutover

Build each rail against a sandbox key (dv_test_sk_…) end to end:
  1. Mandate — issue, dry-run authorize, commit a charge, verify the digest, revoke. The terminal state is a settled authorization with an AP2 credential.
  2. DCB — configure the merchant with a live coverage pair, initiate a charge, build the operator request, capture it, then reconcile it against a synthetic settlement record. Terminal: a reconciled report with reconciled: true.
  3. Pay link — mint, render for a test channel, mark viewed, reconcile a synthetic PSP capture. Terminal: paid.
  4. Storefront — publish the config, fetch the public URL, run a sandbox shopping-agent session against the manifest + feed, complete the checkout under a test mandate. Terminal: a completed checkout session.
  5. Subscription — start, list, fire one renewal cycle, cancel. Terminal: the canceled schedule with the final webhookEvent recorded.
For cutover, swap the key to dv_live_sk_…, point hostedBaseUrl at the production payment host, and for DCB replace the synthetic coverage map with the operator pairs your carrier agreement actually provisions (tenant-level, per country+operator — there is no global gate). Sandbox snapshots are yours to discard; live snapshots are the record you persist.

See also