Skip to content

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 ​

modeWhat 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:

FieldMeaning
addressAddress to verify (required)
network'evm' | 'solana' | 'bitcoin'
customerIdYour 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, or expired
  • Throws WALLET_VERIFICATION_NOT_STARTED if you have not called startVerification() (or the last run already finished)
  • Throws WALLET_POPUP_BLOCKED if 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:

  1. The argument, if you pass one
  2. ?verificationId= on the current URL
  3. The pending id persisted in this tab (persistVerification, default true)

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 ​

CodeWhenWhat to do
WALLET_VERIFICATION_REDIRECT_URL_REQUIREDmode: 'redirect' without redirectUrlPass the landing-page URL
WALLET_VERIFICATION_NOT_STARTEDopenVerificationTab() with nothing pendingCall startVerification() first
WALLET_POPUP_BLOCKEDBrowser blocked the Coinbase tabAsk to allow popups; retry from a click
WALLET_VERIFICATION_CANCELLEDcancelVerification() or the user closed the tab after the grace periodOffer start again
WALLET_VERIFICATION_NOT_FOUNDLanding page has no idPass verificationId or keep persistVerification on
WALLET_VERIFICATION_IN_PROGRESSAnother verify / Coinbase run is already in flightWait, or cancelVerification()
WALLET_GATEWAY_NOT_READYHidden /gateway iframe blocked or timed out on completeVerification()Allow widgetBaseUrl in frame-src - Browser requirements

See Errors for the full list. Walkthrough: Coinbase example.