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
| Credential | Lives in | Used for |
|---|---|---|
Secret key sk_* | Backend only — environment variable | Account creation, session minting, confirming intents, webhook signature verification |
Publishable key pk_* | Frontend, embedded in the page | Identifies your application to Frame; safe to expose |
client_secret | Frontend, passed in per session | Scopes 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
- Frontend asks your backend to start an onboarding flow.
- Backend authenticates with
sk_*, callsPOST /v1/accountsandPOST /v1/onboarding_sessions, and returns the session'sclient_secretto the frontend. - Frontend calls
onboarding.open({ clientSecret }). The Frame.js component uses theclient_secretto authenticate directly with the Frame API and runs the modal flow end-to-end. - 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:
Register event listeners before calling mount(). The ready event fires once and won't replay — listeners attached after mount can miss it.
- 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 themount()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.