Skip to main content

AMB Commerce: Interactive Flows and Apple Pay Checkout

The onboarding guide gets an Apple Messages for Business (AMB) agent registered and approved. This guide picks up where onboarding stops: what you can build inside the thread once the agent is live. It walks the AMB-specific commerce loop end to end — interactive list-picker messages, the customer’s pick folding into your cart, the native Apple Pay request, and the checkout resolution route that anchors everything to a reconciled order. You will:
  1. Understand the AMB commerce loop
  2. Author and send an interactive flow
  3. Request Apple Pay in the thread
  4. Handle the result: token capture and the retry loop
  5. Run the worked example end to end
  6. Place AMB against the channel-agnostic guides
  7. Know the limits
All requests go to two base prefixes:
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. Prerequisite: the agent must be registered and approved — follow the onboarding guide first. Register it with the interactive capability set on the agent’s capabilities object, and with applePay: true when you intend to request Apple Pay.

1. The AMB commerce loop

AMB commerce is a conversation, not a page. Compared to a web checkout:
  • Interactive messages replace product grids. Instead of rendering a category page you send the customer an interactive message inside Messages: a list picker (sections of items, single or multiple selection), a time picker, a rich link with an inline preview, or a form (Apple’s dynamic-form JSON). The customer answers with a tap; the reply lands on your webhook as structured data.
  • Quick replies are plain taps. A list picker’s receivedMessage and replyMessage blocks define what bubbles the customer sees before and after they pick — the wire is provider-specific JSON, but the interaction model is the same one WhatsApp quick replies use.
  • Apple Pay replaces the hosted pay page where the business is eligible. When your AMB agent carries the applePay capability and you have a merchant identifier from Apple Business Register, the pay step surfaces as a payment sheet inside the thread — the customer authorizes with Face ID, no browser, no redirect.
  • The cart is one object across channels. AMB picks fold into the same persistent omni-cart a WhatsApp catalog order or an RCS carousel postback joins — Apple Pay in AMB is one of the folds that cart closes through.
The loop in one line: interactive message → customer taps → cart merges the pick → checkout resolves (native Apple Pay or hosted link) → payment reconciled server-side.

2. Author and send an interactive flow

An interactive AMB message travels as a normal send with a msgType and a provider-specific JSON blob in metadata. The shape is Apple’s Messages Business Chat envelope — author the JSON once, validate it against the shapes below, and reuse it.

The list-picker JSON

The workhorse shape for product selection is the list picker:

Validation gates

Catch these before production traffic does:
  • metadata.listPickerJson must parse as JSON and match the shape above — a send without it is rejected with 422 metadata.listPickerJson is required for AMB interactive messages. Store the JSON as a template string, not hand-pasted bodies per campaign.
  • The same gate applies to the other interactive kinds: formJson must conform to Apple’s dynamic-form schema, richLinkJson needs at least { url, title }, and timePickerJson needs an event with at least one timeslot.
  • The agent’s capability list governs what it can carry — an agent registered without interactive: true should not be targeted by an interactive template.

Save it as a template, assign it, send it

Keep the picker JSON next to your other channel templates (dashboard: Flows / Templates, or your own versioned template store) — treat it as campaign content, not code. Assign it to the campaign, journey step, or agent reply that starts the flow, exactly as you would assign a WhatsApp template. Send it over the unified messages surface, with the picker in metadata (values are strings — the JSON goes in serialized):
conversationId is the thread the customer opened inbound — AMB is customer-initiated, so the picker always answers a conversation that already exists; the customer-facing body is carried by receivedMessage.title, not a separate body field. The customer’s tap arrives on your registered AMB webhook as an inbound interactive message with a structured interactiveData payload carrying the picked item’s identifier. That identifier is the join key into everything below.

3. Request Apple Pay in the thread

Once the pick is in the cart, decide how the customer pays. The decision is per-business, not per-buyer — unlike WhatsApp Pay’s buyer-region rails, Apple Pay eligibility is a merchant capability: the business must carry the applePay capability AND have a merchant identifier configured.

The omni-cart fold

AMB picks do not live in an AMB-only state — they merge into the same cart object every other channel feeds. Create the cart once per customer, then merge the pick the webhook surfaced:
Merge the identifier the webhook returned — never a client-rendered label — and value prices from your catalog server-side, not from anything the customer device sent. Resolve the checkout:
The response decides the method you actually use:
Two paths out of one call:
  • method: "native" — the agent has applePay enabled and merchantIdentifier set. Present the native object to the customer as the Apple Pay request: the merchant identifier, line items, and the decimal total (Apple’s line items take major units, unlike Meta’s minor-unit WhatsApp payload). Apple renders the payment sheet inside the thread; the customer authorizes with Face ID. The sheet’s request and the PSP result both reconcile against referenceId — which is the same id as the minted paymentRequest.
  • method: "hosted" — any of: applePayEnabled unset, no merchantIdentifier, or you deliberately send the link. native is null; send hosted.url (or the pre-rendered hosted.body) into the thread, and the customer completes on your hosted pay page.
The returning-token vs deferred-authorization split is entirely on the Apple Pay path: a native request asks Apple for the payment sheet now, and the customer either authorizes (you receive a token — below) or cancels/retries; the hosted path defers authorization to the hosted page’s own flow, and you reconcile on paymentRequest.status transitions instead.

4. Handle the result

The native Apple Pay token

When the customer authorizes the sheet, Apple hands back a payment token (the PKPayment representation) addressed to your PSP. Forward it to the PSP exactly as you would a web Apple Pay token, then reconcile against the id the response minted:
Amount and currency must match the snapshot exactly — reconciliation refuses a forged subtotal, which is why totals are always re-derived server-side (see conversational checkout for the reconciliation semantics). Your order service flips the order to paid on the reconcile’s success surface, and the cart closes via POST /commerce/cart/transition.

The decline / retry / fallback loop

  • Authorization declined at the PSP → retry in-thread. Keep referenceId stable while you invite the customer to retry the sheet; mint a fresh checkoutId (a new POST /commerce/amb-checkout) only once the retry budget for that reference is spent — a new id is a new payment request, i.e. a clean after failure.
  • The customer cancels the sheet → offer the hosted fallback. The native payload always carries fallbackUrl, and the response always carries the full hosted block — send hosted.body into the same thread and no state is lost: both paths reconcile to the same paymentRequest, so which one the customer completed does not fork your order logic.
  • Publication failure of the native shape → degrade to hosted. If the interactive Apple Pay request fails to render on an older device, the same rule applies — the hosted link is never absent from a resolved checkout.

5. Worked example end to end

A reachable end-to-end sequence you can copy against a sandbox key:
  1. Register + enable. The onboarding guide registers the agent; register it with capabilities: { "text": true, "interactive": true, "applePay": true } and poll until status: "approved". Note the agent id (data.id).
  2. Send the list picker. POST /api/v1/messages with channel: "amb", metadata.businessId = your Apple business_id, metadata.conversationId = the thread, metadata.msgType: "interactive", and metadata.listPickerJson = the picker JSON from §2.
  3. Read the pick. The webhook delivers an inbound interactive message; read the chosen identifier out of interactiveData (here: SKU-1042).
  4. Merge into the cart. POST /api/v1/commerce/cart once, then /cart/merge the picked SKU with the server-side price.
  5. Resolve the checkout. POST /api/v1/commerce/amb-checkout — with applePayEnabled: true + merchantIdentifier set, the response is method: "native" and the native block carries the sheet payload keyed to referenceId.
  6. Request the sheet in-thread. Present the native Apple Pay request in the conversation; the customer authorizes with Face ID.
  7. Forward the token, reconcile. POST /api/v1/commerce/payment-request/reconcile with the PSP transaction reference → the order service marks the order paid.
  8. On a decline/cancel, fall back. Send hosted.body into the same thread — same paymentRequest, nothing re-created — and let the customer complete on the hosted link.

6. Where AMB fits next to the channel-agnostic guides

  • Conversational commerce rails covers the channel-agnostic loop: persistent cart, checkout-channel selection, reconciliation. This guide adds only the AMB-specific part on top — the interactive message envelope and the Apple Pay shortcut that lets an eligible business skip the hosted page.
  • Conversational commerce checkout walks the generic purchase (WhatsApp-led). AMB substitutes for the WhatsApp leg and adds one fold the generic guide leaves as theory: native Apple Pay where the business carries the capability.
  • The RCS checkout analogue is the hosted fallback — RCS has no native rail, so its checkout always mints the link AMB only falls back to.
Use this guide for the AMB-specific envelope + the Apple Pay branch; use the two channel-agnostic guides for everything cart/reconciliation semantics do not change per channel.

7. Limits

  • Onboarding first. Apple Pay only behaves after the agent is registered and approved — the onboarding guide is a hard prerequisite, and any attempt against a pending agent is a no-op.
  • Capability and identifier are both mandatory for native. checkout…applePayEnabled: true without a merchantIdentifier, or vice versa, silently resolves to method: "hosted" — check both on the agent record before promising the sheet to the customer.
  • The provider shape is Apple-specific. listPickerJson / formJson / richLinkJson / timePickerJson are Apple Messages Business Chat envelopes — they will NOT render on WhatsApp, RCS, or web chat; per-channel templates remain per-channel.
  • AMB is customer-initiated. You answer an open conversation; you cannot launch a picker into a thread that never started — entry points stay Apple-side (Maps, Spotlight, Safari, Siri, an untruncated link).
  • Line items are decimal amounts. Apple’s items/total take major units (e.g. 89 for €89), unlike the minor-unit WhatsApp order payload — do not feed one shape into the other’s builder.
  • Failed validations are loud. A missing/invalid interactive blob is a 422 naming the metadata key; treat the send as rejected, fix the template, and resend — nothing queues.