Skip to main content
Follow a checkout from its returned runId until it reaches a terminal status. The run exposes current status and requiredAction; its messages expose conversation, progress, activity, input requests, and results.

Prerequisites

  • A checkout started through the quickstart
  • A production API key with agent-checkouts.read and agent-checkouts.update; add agent-checkouts.cancel to cancel runs
  • The same buyer identity used to create the run

Authenticate Each Request

For a backend integration, keep the server key outside client bundles and send the buyer’s identifier on every call:
For browser or native calls, use a production client-side key and Authorization: Bearer YOUR_USER_JWT instead of x-crossmint-user-id. Register your JWT issuer under 3P Auth providers in the Crossmint Console and allow your app’s origins. The request bodies are the same.

Observe and Recover

1

Choose the observation strategy

Crossmint recommends streaming for live production integrations. The quickstart uses polling to keep its example small.
2

Open the message stream

The stream is an HTTP response with content type text/event-stream. It carries both run snapshots and message changes:
Store each event’s id as the resume cursor. Keep the greatest run revision, and the greatest message revision for each message ID. Replayed or stale snapshots must not replace newer state. Revisions can skip; they order snapshots, while the cursor identifies the stream replay position.Handle open requests once by requestId, whether they arrive in requiredAction or a message part. Dispatch interaction.kind: "form" to Handle Buyer Inputs, and "payment" to Authorize Payments.Reconnect with the cursor in the last-event-id header or after query parameter. If the cursor is rejected or recovery needs a fresh snapshot, use the next step.
3

Bootstrap or resynchronize from history

List all message pages before opening a stream for an existing run. The final page’s streamCursor provides the handoff to live updates. Read the current run after history so you also discover a pending action.
Render the recovered messages and current run. If run.status is "awaiting_input" and its request has not been answered, handle run.requiredAction before connecting to streamUrl with the same authentication. Retain your request deduplication state across reconnects. See List Messages and Stream Messages for pagination and replay details.
4

Poll when streaming is unavailable

Read GET /{runId} with the same headers and cache: "no-store". Poll every 2 seconds while active, increase the interval while nothing changes, and reset it after a status change or response. Stop at a terminal status.Retry transient read failures with bounded backoff. For 429, respect the response’s retryAfterSeconds. This read helper illustrates the retry behavior:
A poll immediately after a successful response can still contain the same action. Track answered request IDs so you do not submit again. For resumed integrations, recover message history rather than relying only on an in-memory set.

Process Message Parts

A message can contain several parts. Store it by id, retain its greatest revision, and use role to identify the speaker. Your UI can also display the read-only checkout browser in an iframe using run.browser.embedUrl. browser is null until a session exists; displaying it is optional.

Respond or Steer

Submit the form answer or payment authorization to POST /{runId}/messages. Generate the message id once and retain its exact body until acknowledged. If acknowledgement is lost, retry the same ID and body. After a definitive validation rejection, correct the response with a new ID. Do not reuse an ID for changed content. Respond before the request’s expiresAt. If it closes or expires, load the current request. After a response, continue observing: the agent may ask several questions during a purchase. To choose another path, send an alternative instead of the requested answer:
Use action: "decline" without text to refuse a request. To steer without answering a particular request, send a text part:
A 202 Accepted confirms message acceptance, not that the agent consumed it. Follow its delivery and subsequent run updates.

Finish or Cancel

For a succeeded run with result.purchase.kind: "receipt_captured", read result.purchase.receipt for the total and merchant order ID. The result message supplies the user-facing summary. Cancel an active run with POST /{runId}/cancel, using the same authentication. It returns 202 Accepted; keep observing until the run reaches a terminal status. The stream closes after terminal updates.

Connect Your User Channel

Your application associates each handoff with the buyer, run, and request. See Handle Buyer Inputs for combining answers collected across channels.

Next Steps

Handle Buyer Inputs

Render and submit ordinary and protected answers

Authorize Payments

Turn a payment request into an order intent response