Skip to main content
A money question with no connection is not an error. run() answers needs_connection, holds the question, and includes the URL to offer:
Render the button from that result:

The click, start to finish

  1. <ConnectButton> opens Payman’s consent page in a popup. It renders a real link as the blocked-popup fallback and retires itself once connected.
  2. The customer signs in, picks a provider, and approves access to their Payman account.
  3. Payman redirects to the mounted callback, which verifies state, exchanges the code, and stores the connection.
  4. Before redirecting back, the callback starts the held question server-side. The reply is already being produced while the browser navigates.
  5. The page lands with ?payman=connected (plus resume=1 when a replay started). <PaymanProvider autoResume> opens one stream on {basePath}/resume/stream and the answer arrives with nothing retyped.
While the connection holds (90 days), the same call is an ordinary turn and the button does not render. A denial lands with ?payman=denied. Treat it as an answer, not an error, and offer again when a later turn needs it.
Ask at the moment of need, not at onboarding. Offer the button in the turn that actually needed money or account data; the question is already held, so the answer follows the consent straight back. Your agent is what decides: give it the Connect tool and let it pick the turns that need the connection.

Watching the replay

Progress frames carry a phase and the platform’s own label, nothing else. Show progress.label while the turn runs rather than the raw liveText; <ResumeNotice> does exactly that and hides once output lands.

Refresh and multiple instances

The resume stream attaches instead of starting. A refresh mid-replay catches up on buffered frames and follows live, and the held question can never run twice. When the callback landed on another instance, the stream falls back to the shared store, so multi-instance deployments need the shared store they already chose (see any framework). Off the browser there is no page to attach. After handling your callback, call resumePending() to run the held question yourself.
Record the resumed turn by having your server re-read the SDK’s record of it, never by trusting what the page posted. A browser that can write “the payment went through” into your transcript can lie about money.