Appearance
Coinbase
client.coinbase proves the user controls an address that lives on Coinbase. The user signs in; Swapped confirms the address. There is no wallet connect and no signature step.
Do not reuse Connect client.coinbase (that module withdraws to a session and throws SESSION_REQUIRED here).
Modes
mode | What happens |
|---|---|
'new-tab' (default) | startVerification() creates the record only. From a click, call openVerificationTab() to open Coinbase and wait on this page |
'redirect' | startVerification() creates the record and navigates this tab to Coinbase. On the landing page, call completeVerification() |
If that customerId + address is already verified, startVerification() returns status: 'verified' immediately - no tab and no redirect.
startVerification
ts
const created = await client.coinbase.startVerification({
address: '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B',
network: 'evm',
customerId: 'user-1234',
})StartCoinbaseVerificationRequest:
| Field | Meaning |
|---|---|
address | Address to verify (required) |
network | 'evm' | 'solana' | 'bitcoin' |
customerId | Your id for the end user (account id, uuid - not a Swapped id). Combined with address, this is the reuse key |
mode? | 'new-tab' (default) or 'redirect' |
redirectUrl? | Required for redirect. Optional for new-tab - omit to land on the Swapped “you can close this tab” page |
orderId? | Merchant order reference (metadata only) |
customerEmail? | Optional end-user email. Forwarded to the API on create |
webhookUrl? | Optional override of the partner default webhook |
redirect without redirectUrl throws WALLET_VERIFICATION_REDIRECT_URL_REQUIRED.
Only one verification may run on this client at a time (WALLET_VERIFICATION_IN_PROGRESS).
New tab
Split create and open so the Coinbase tab comes from a user gesture (otherwise the browser blocks it).
ts
await client.coinbase.startVerification({
address,
network: 'evm',
customerId: 'user-1234',
// mode defaults to 'new-tab'
})
// inside a click handler
const verification = await client.coinbase.openVerificationTab()openVerificationTab():
- Opens Coinbase in a new tab and waits on this page until
verified,failed, orexpired - Throws
WALLET_VERIFICATION_NOT_STARTEDif you have not calledstartVerification()(or the last run already finished) - Throws
WALLET_POPUP_BLOCKEDif the browser blocked the tab - If the user closes the Coinbase tab, waits a short grace period for a terminal status, then cancels (
WALLET_VERIFICATION_CANCELLED) so you can start again
cancelVerification() aborts the wait, closes the tab, and releases the in-flight lock:
ts
client.coinbase.cancelVerification()A pending new-tab verification is kept in memory on this client. It is not written to storage.
Same-tab redirect
Host a landing page at redirectUrl. Coinbase sends the browser back with ?verificationId= and a hint ?status=. Treat status as a hint only - always wait for the real record.
ts
// page A - start (navigates away)
await client.coinbase.startVerification({
address,
network: 'evm',
customerId: 'user-1234',
mode: 'redirect',
redirectUrl: 'https://merchant.example.com/wallet-verified',
})
// page B - landing
const verification = await client.coinbase.completeVerification()completeVerification(verificationId?) resolves the id from:
- The argument, if you pass one
?verificationId=on the current URL- The pending id persisted in this tab (
persistVerification, defaulttrue)
Throws WALLET_VERIFICATION_NOT_FOUND when none of those are available.
Same-tab persist is how completeVerification() recovers after Coinbase navigates back. Set persistVerification: false on createWalletVerification if you will always pass the id yourself.
Errors
| Code | When | What to do |
|---|---|---|
WALLET_VERIFICATION_REDIRECT_URL_REQUIRED | mode: 'redirect' without redirectUrl | Pass the landing-page URL |
WALLET_VERIFICATION_NOT_STARTED | openVerificationTab() with nothing pending | Call startVerification() first |
WALLET_POPUP_BLOCKED | Browser blocked the Coinbase tab | Ask to allow popups; retry from a click |
WALLET_VERIFICATION_CANCELLED | cancelVerification() or the user closed the tab after the grace period | Offer start again |
WALLET_VERIFICATION_NOT_FOUND | Landing page has no id | Pass verificationId or keep persistVerification on |
WALLET_VERIFICATION_IN_PROGRESS | Another verify / Coinbase run is already in flight | Wait, or cancelVerification() |
WALLET_GATEWAY_NOT_READY | Hidden /gateway iframe blocked or timed out on completeVerification() | Allow widgetBaseUrl in frame-src - Browser requirements |
See Errors for the full list. Walkthrough: Coinbase example.