Skip to main content
The Onramp widget renders inside a cross-origin iframe or popup served from a hosted origin on Arc. Configure your host page to meet the requirements on this page before deploying. For an end-to-end walkthrough that uses these settings, see the embed widget quickstart.

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

Both createAppServerKit 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:
Client side:
The server and client values must match. If they differ, 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 blocks localhost, so a production widget won’t load from a local dev server. To target sandbox, override two config values on the server and one on the client. API keys are environment-bound. A sandbox API key won’t work against production, and a production API key won’t work against sandbox.

Example .env

.env
The 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.

Content security policy

If your site sends a CSP header, you must allow the widget and API origins for the environment you target. Without these directives, the iframe silently fails to load. Sandbox:
Production:

Container element

Two container-side preconditions apply when calling mountIframe:

The container must be attached to the document

The App Kit SDK appends the iframe synchronously and installs a MutationObserver 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 DOMContentLoaded or insert the container yourself before calling.

The container must have an explicit, non-zero height

The iframe renders at width: 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.
Any of these resolve the height correctly:
  • A fixed height (such as 720px).
  • A min-height.
  • A flex child inside a parent with a sized cross-axis.
When you use openWindow instead of mountIframe, the host environment matters:
  • Mobile browsers open a new tab, not a sized popup. The width and height features are ignored.
  • In-app browsers (Instagram, Facebook, TikTok, LinkedIn, WeChat, LINE) block popups. openWindow returns { status: 'blocked', reason: 'in_app_browser' } so you can fall back to mountIframe.
  • Installed web apps in standalone mode also block popups. openWindow returns { status: 'blocked', reason: 'pwa_standalone' }.
See Choose iframe or popup mode for the full fallback flow.

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, use openWindow mode on that platform.

Webhooks are the source of truth

Browser lifecycle events such as DEPOSIT_SUBMITTED and DEPOSIT_SETTLED are best-effort UX signals. A user can close the tab between submitting a deposit and settlement, and the browser will never deliver the settlement event even though the deposit completes server-side.
Do not treat the absence of DEPOSIT_SETTLED as “no deposit.” Reconcile final state from webhook events delivered to your backend. Use the in-browser events only to drive UI.

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

Each mountIframe() 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.