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:- Understand the AMB commerce loop
- Author and send an interactive flow
- Request Apple Pay in the thread
- Handle the result: token capture and the retry loop
- Run the worked example end to end
- Place AMB against the channel-agnostic guides
- Know the limits
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
receivedMessageandreplyMessageblocks 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
applePaycapability 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.
2. Author and send an interactive flow
An interactive AMB message travels as a normal send with amsgType 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.listPickerJsonmust parse as JSON and match the shape above — a send without it is rejected with422 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:
formJsonmust conform to Apple’s dynamic-form schema,richLinkJsonneeds at least{ url, title }, andtimePickerJsonneeds aneventwith at least one timeslot. - The agent’s capability list governs what it can carry — an agent registered without
interactive: trueshould 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 inmetadata (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 theapplePay 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:Native Apple Pay vs the deferred hosted link
Resolve the checkout:method: "native"— the agent hasapplePayenabled andmerchantIdentifierset. Present thenativeobject 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 againstreferenceId— which is the same id as the mintedpaymentRequest.method: "hosted"— any of:applePayEnabledunset, nomerchantIdentifier, or you deliberately send the link.nativeisnull; sendhosted.url(or the pre-renderedhosted.body) into the thread, and the customer completes on your hosted pay page.
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 (thePKPayment 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:
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
referenceIdstable while you invite the customer to retry the sheet; mint a freshcheckoutId(a newPOST /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
nativepayload always carriesfallbackUrl, and the response always carries the fullhostedblock — sendhosted.bodyinto the same thread and no state is lost: both paths reconcile to the samepaymentRequest, 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:- Register + enable. The onboarding guide registers the agent; register it with
capabilities: { "text": true, "interactive": true, "applePay": true }and poll untilstatus: "approved". Note the agent id (data.id). - Send the list picker.
POST /api/v1/messageswithchannel: "amb",metadata.businessId= your Applebusiness_id,metadata.conversationId= the thread,metadata.msgType: "interactive", andmetadata.listPickerJson= the picker JSON from §2. - Read the pick. The webhook delivers an inbound
interactivemessage; read the chosenidentifierout ofinteractiveData(here:SKU-1042). - Merge into the cart.
POST /api/v1/commerce/cartonce, then/cart/mergethe picked SKU with the server-side price. - Resolve the checkout.
POST /api/v1/commerce/amb-checkout— withapplePayEnabled: true+merchantIdentifierset, the response ismethod: "native"and thenativeblock carries the sheet payload keyed toreferenceId. - Request the sheet in-thread. Present the
nativeApple Pay request in the conversation; the customer authorizes with Face ID. - Forward the token, reconcile.
POST /api/v1/commerce/payment-request/reconcilewith the PSP transaction reference → the order service marks the orderpaid. - On a decline/cancel, fall back. Send
hosted.bodyinto the same thread — samepaymentRequest, 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.
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 apendingagent is a no-op. - Capability and identifier are both mandatory for native.
checkout…applePayEnabled: truewithout amerchantIdentifier, or vice versa, silently resolves tomethod: "hosted"— check both on the agent record before promising the sheet to the customer. - The provider shape is Apple-specific.
listPickerJson/formJson/richLinkJson/timePickerJsonare 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.
89for €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
422naming the metadata key; treat the send as rejected, fix the template, and resend — nothing queues.