Architecture

Every Frame integration involves three actors. Understanding what each one is allowed to do — and which credential it holds — is the load-bearing model for everything else in this SDK.

The three actors

Your frontend runs Frame.js in the browser. It holds your publishable key (pk_*) — safe to expose — and, for any session-scoped flow, a short-lived client_secret minted by your backend. It never sees your secret key. The frontend's job is to render UI: card input, wallet buttons, onboarding modal, Terms of Service. It collects sensitive values, encrypts them in the browser, and hands them to the Frame API directly so they never transit your server.

Your backend holds your secret key (sk_*) and owns everything that requires it. Creating accounts. Provisioning capabilities. Minting onboarding sessions. Confirming charge intents. Receiving webhooks. It can use a Frame server SDK or call the Frame API directly. It hands only the client_secret to your frontend — nothing more.

The Frame API is the source of truth. Your backend authenticates with the secret key. Your frontend authenticates with the publishable key plus a client_secret. The two authentication paths are intentionally separate so the secret key has no route to the browser.

How the credentials split

CredentialLives inUsed for
Secret key sk_*Backend only — environment variableAccount creation, session minting, confirming intents, webhook signature verification
Publishable key pk_*Frontend, embedded in the pageIdentifies your application to Frame; safe to expose
client_secretFrontend, passed in per sessionScopes the publishable key to one session (e.g. one onboarding flow, one charge intent)

The client_secret is the seam between the two halves. Your backend mints it after authorizing the operation; your frontend uses it to perform exactly that operation, nothing more. If a client_secret leaks, the blast radius is one session.

A worked example: embedded onboarding

  1. Frontend asks your backend to start an onboarding flow.
  2. Backend authenticates with sk_*, calls POST /v1/accounts and POST /v1/onboarding_sessions, and returns the session's client_secret to the frontend.
  3. Frontend calls onboarding.open({ clientSecret }). The Frame.js component uses the client_secret to authenticate directly with the Frame API and runs the modal flow end-to-end.
  4. Backend receives webhooks as the user completes each step. Webhooks are the authoritative signal — frontend callbacks are advisory.

The same shape repeats for card charges (mint a charge intent server-side, confirm it from the browser with client_secret) and for Terms of Service capture (the signed token from the browser gets verified server-side).

Element lifecycle

Frame elements are mounted asynchronously. They construct, fetch any needed config, and emit a ready event when they're available for interaction. A few rules apply across every element:

  • Construct, then mount. Frame.init() or a lightweight factory returns the element; element.mount(selector) attaches it to the DOM.
  • Listen before mount. Always wire on("ready"), on("change"), on("error") before the mount() call so the first event lands in your handler.
  • Trust webhooks for state changes. Browser events tell you what the user did in the moment; webhooks confirm what Frame accepted. Reconcile state from the webhook, not the event.