Request / Response standard, and the browser widget only needs a
mounted DOM container. The route example uses Next.js App Router wiring, but the
same handler works in Fetch-compatible backends such as Hono, Cloudflare
Workers, Bun, Deno, and modern Node. For Express or Fastify hosts, see
Customize session minting.
Prerequisites
Before you begin, ensure that you’ve:- Installed Node.js v22+.
- Set up an existing web app with a server route and a browser page.
- Installed the App Kit SDK.
- Obtained an API key from the Circle Console.
- Obtained a wallet address on Arc where users will receive their stablecoins.
Step 1. Set your environment variables
Add your API key to your server environment. Never inline it in client code or commit it to source control..env
Step 2. Expose a session route on your server
Create a route that exchanges your API key for a short-lived session token. The App Kit SDK shipscreateSessionRouteHandler as a drop-in Request → Response
handler. The following example uses the Next.js App Router:
app/api/onramp/sessions/route.ts
POST requests, validates the body, calls
server.onramp.createSession(), and returns the session as JSON. It maps thrown
errors to the appropriate HTTP status codes and sets Cache-Control: no-store
so session tokens are never cached.
This route doesn’t perform authentication. Before deploying, add an
authorize
callback that checks the user’s session. See
Customize session minting.Step 3. Add a container element to your page
The Onramp widget renders inside an<iframe> that fills its container. The
container must already be attached to the DOM and must have an explicit,
non-zero height, or the iframe collapses to 0px.
The snippet shows only the container markup. Step 4 extends this file with the
full mounting logic.
Step 4. Mint a session and mount the widget
In your browser code, mint the session by sending aPOST request to the route
you created in step 2, then mount the widget into the container element.
onramp.ts
USER_WALLET_ADDRESS with the signed-in user’s Arc wallet address
before running the flow.
Keep the widget controller returned by mountIframe. Call widget.close() when
the user leaves this view, closes the modal, or starts a new widget session. In
a framework, call stopOnramp() from that framework’s cleanup lifecycle.
Step 5. Allow the widget origin in your Content Security Policy (CSP)
If your site sends a CSP header, the iframe fails to load silently unless you allow the widget and API origins:Step 6. Set up a webhook receiver
Browser events such asDEPOSIT_SETTLED are best-effort UX signals only. If a
user closes the tab between submitting a deposit and settlement, the browser
never receives the settlement event even though the deposit succeeds. Reconcile
final deposit state from webhook events delivered to your backend.