Appearance
Client (Core)
Create one SwappedConnectClient with createSwappedConnectClient, then call loadSession() before any payment UI.
This SDK is browser only. createSwappedConnectClient throws BROWSER_REQUIRED on the server (Node.js, SSR, RSC). Create a sessionId on your backend — see Creating a session — then instantiate the client in the browser.
ts
import { createSwappedConnectClient } from '@swapped/connect-sdk';
const client = createSwappedConnectClient({
sessionId: 'your-session-id',
environment: 'staging', // omit or 'production' for live
});
await client.loadSession();Reuse this instance for the life of the payment UI. Call restartSession() for another payment — do not create a second client unless you are running more than one session on the same page. Call destroy() on teardown.
Config
| Option | Default | Notes |
|---|---|---|
sessionId | '' | Required before loadSession() / restartSession() (SESSION_ID_REQUIRED if missing) |
environment | 'production' | 'production' or 'staging'. Sets default API, WebSocket, and widget-gateway hosts |
apiBaseUrl | environment default | Overrides the API host. Allow-listed to the official production and staging hosts (INVALID_API_BASE_URL otherwise) |
widgetBaseUrl | environment default | Gateway host that serves /gateway. Localhost is allowed for widget development (INVALID_WIDGET_BASE_URL otherwise) |
modules | all four | Payment modules to construct. See Modules |
wallets.balancesCacheTtlMs | 600000 (10 min) | How long wallet balances stay cached. A manual refresh always loads fresh balances |
Environments
The client defaults to production. Pass environment: 'staging' to point the API, WebSocket, and widget gateway at staging. A staging sessionId will not load against production.
| Environment | Session endpoint | Client |
|---|---|---|
| Production (default) | POST https://connect-api.swapped.com/api/sessions | omit environment, or environment: 'production' |
| Staging | POST https://staging-api.swapped.app/api/sessions | environment: 'staging' |
If the session was created on staging and the client stays on production, loadSession() fails — and getSessionView() still reports active because no session is loaded, so payment UI can render over a session that does not exist.
Optional apiBaseUrl / widgetBaseUrl replace that environment's defaults. If you pass a custom apiBaseUrl, the client infers environment from it when environment is omitted.
Modules
All payment modules are enabled by default (exchangePay, cashApp, coinbase, wallets). Pass modules to construct only the ones you need.
ts
const client = createSwappedConnectClient({
sessionId: 'your-session-id',
modules: ['exchangePay'],
});wallets and coinbase mount a hidden /gateway iframe; exchangePay and cashApp do not. Sites with a Content-Security-Policy must allow that iframe and the API WebSocket — see Browser requirements.
Accessing a module that was not listed throws MODULE_NOT_ENABLED. paymentMethods.get() also hides methods for modules that are not enabled. Check with client.isModuleEnabled('wallets') or read client.modules.
Lifecycle
| Method | Purpose |
|---|---|
loadSession(sessionId?, options?) | Load (or reload) the session. A second call with the same id uses the cache unless { forceRefetch: true } |
restartSession(sessionId?) | Start a new payment for the same merchant flow; clears Exchange Pay state |
getSession() | Last loaded SessionData, or null |
getSessionView() | Classified UI view — see Session |
getSessionId() | Active session id |
getMaintenanceStatus() | Latest maintenance payload, or null |
getState() | Snapshot: sessionId, session, isLoading, isRestarting, error, maintenance |
subscribe(listener) | Public state changes; returns unsubscribe |
on / off | Lifecycle events — see Events |
destroy() | Tear down sockets and this client's listeners. The shared gateway iframe is removed only when the last client is destroyed |
Call loadSession() once at bootstrap. You almost never need forceRefetch — sockets and payment completion already refresh the session.
ts
await client.loadSession();
await client.restartSession(); // after completion / expiry / failure
client.destroy();getState() / subscribe are for session-level UI (loading, error, maintenance). For which screen to show, prefer getSessionView() and sessionView:changed.
Multiple clients on one page
You can create more than one client when each has its own sessionId. A second live client with the same sessionId throws SESSION_ALREADY_IN_USE. On the same page they share:
- Wallet connections (connect / disconnect /
switchChainon one client updates every client) - Coinbase login (the same OAuth token; each client still withdraws to its own session)
- One hidden
/gatewayiframe
Accepted limits:
- Only one WalletConnect pairing — and one QR — at a time. A second
connect({ transport: 'walletconnect' })withoutforce: truethrowsWALLET_PAIRING_IN_PROGRESS.cancelPairing()orforce: trueaborts the in-flight pairing and clears the previous QR. The pairing expires from the URI timestamp (WALLET_PAIRING_EXPIRED). - Only one OAuth or wallet popup at a time (
Login,swapped-wallet-popup). - The first client’s
widgetBaseUrl/ environment mounts the iframe. Do not mix staging and production gateway hosts on one page.
destroy() tears down that client. The shared iframe is removed only when the last gateway client (wallets or coinbase) is destroyed.
Errors
| Code | When | What to do |
|---|---|---|
BROWSER_REQUIRED | Client created outside the browser | Instantiate in a browser-only module |
SESSION_ID_REQUIRED | Missing id on load / restart | Pass sessionId to create or to the call |
SESSION_ALREADY_IN_USE | Another live client already has this sessionId | Use one client per session, or destroy() the other first |
INVALID_ENVIRONMENT | environment is not production or staging | Use one of those two values |
INVALID_API_BASE_URL | apiBaseUrl is not an official host | Use the production or staging API URL, or omit it |
INVALID_WIDGET_BASE_URL | widgetBaseUrl is not official and not localhost | Use the environment default, or a localhost widget host |
INVALID_MODULES | modules is not an array of known names | Pass exchangePay, cashApp, coinbase, and/or wallets |
MODULE_NOT_ENABLED | Called a module omitted from modules | Include it, or omit modules to enable all |
CLIENT_DESTROYED | Method called after destroy() | Create a new client |