Endpoints
Onramp exposes two environments. Use sandbox for local development and testing. Use production for real transactions.
Your API key authenticates your server. Keep it
server-side only, since it grants full access to your Onramp integration.
Widget origin
BothcreateAppServerKit on your server and new AppKit on your client accept
a widgetBaseUrl option that points at the widget origin. The default is the
production widget URL, so production integrations don’t need to set it. To
target sandbox, pass the sandbox widget origin on both sides.
Server side:
mountIframe and
openWindow throw an
INPUT_WIDGET_URL_ORIGIN_MISMATCH
error (code 1910).
Sandbox
Sandbox is a testing environment for developing your Onramp integration without real money movement. It exposes the same API surface as production. Only the endpoints and the API key binding differ. Use sandbox for local development. The production widget’s Content Security Policy blockshttp://localhost, so a production widget won’t load from a local
dev server over HTTP. Sandbox allows http://localhost:*.
To target sandbox, override two config values on the server and one on the
client. API keys are environment bound: use a sandbox API key with sandbox and a
production API key with production.
Example .env
.env
widgetBaseUrl value is safe to expose to the browser, so a NEXT_PUBLIC_
(or your framework’s equivalent) prefix lets the client read it. The API key is
the only real secret and never gets a public prefix.
Testing behavior
Sandbox doesn’t move real money. It sends testnet tokens instead. Use dummy data only. Never enter real PII.
The identity form’s required fields vary by region:
Content security policy
If your site sends a CSP header, you must allow the widget origin for the environment you target. Without this directive, the iframe silently fails to load. Sandbox:connect-src for Onramp. Other App Kit capabilities (Bridge, Swap, Earn,
Borrow, Unified Balance) need connect-src https://api.circle.com for error
reporting if used in the browser.
Container element
Two container-side preconditions apply when callingmountIframe:
The container must be attached to the document
The App Kit SDK appends the iframe synchronously and installs aMutationObserver that auto-disposes the widget if the container later
detaches. The container must be in the DOM when you call mountIframe:
- In React, mount from a
useEffect(not during render) so the ref is populated before the call. - In other frameworks, use that framework’s mounted lifecycle.
- In plain browser code, wait for
DOMContentLoadedor insert the container yourself before calling.
The container must have an explicit, non-zero height
The iframe renders atwidth: 100%; height: 100%. A cross-origin iframe cannot
size itself to its content, so without a resolved height the iframe collapses to
0px and the user sees nothing.
- A fixed
height(such as720px). - A
min-height. - A flex child inside a parent with a sized cross-axis.
Popup mode constraints
When you useopenWindow instead of mountIframe, the host environment
matters:
- Mobile browsers open a new tab, not a sized popup. The
widthandheightfeatures are ignored. - In-app browsers (Instagram, Facebook, TikTok, LinkedIn, WeChat, LINE)
block popups.
openWindowreturns{ status: 'blocked', reason: 'in_app_browser' }so you can fall back tomountIframe. - Installed web apps in standalone mode also block popups.
openWindowreturns{ status: 'blocked', reason: 'pwa_standalone' }.
Event delivery requires embedding
BothmountIframe and openWindow deliver widget events to the page that
called them. If you open the widget URL as a top-level page (for example, from a
native app in Safari View Controller or Chrome Custom Tabs), nothing receives
the events. Host the widget on your own page using one of these two modes and
open that page instead.
Third-party storage in iOS Safari
iOS Safari’s Intelligent Tracking Prevention (ITP) can restrict the embedded app’s access to its own storage inside an iframe. If you see KYC or session issues that only occur on iOS Safari, useopenWindow mode on that platform.
Session lifetime
Onramp sessions are valid for 30 minutes from creation. If your app pre-fetches or reuses a session, mint a fresh one once more than 30 minutes have passed to avoid a stale-session error.Close unused widgets
EachmountIframe() call, and each successful openWindow() call, returns a
widget controller. Keep that controller and call widget.close() before
starting over or when the user leaves the view.