Appearance
Example
End-to-end Coinbase OAuth withdraw with client.coinbase: connect, pick balance / network, withdraw (with 2FA when required), show summary.
Route here when the payment method has provider: coinbase and type: exchange_oauth. Session must be active before startWithdrawal. After a completed payment, call restartSession() before starting again.
1. Set up the client
ts
import {
createSwappedConnectClient,
IntegrationProvider,
IntegrationProviderType,
} from '@swapped/connect-sdk';
const client = createSwappedConnectClient({
sessionId: 'your-session-id', // from your backend
});
await client.loadSession();2. Confirm Coinbase is available
ts
const methods = await client.paymentMethods.get({
type: IntegrationProviderType.ExchangeOauth,
});
const coinbaseMethod = methods.find(
method =>
method.provider === IntegrationProvider.Coinbase &&
method.type === IntegrationProviderType.ExchangeOauth,
);
if (!coinbaseMethod) {
throw new Error('Coinbase is not available for this session');
}3. Connect (OAuth popup)
Wait for token restore, then connect. Revalidate with ensureSession() when returning to the flow.
ts
await client.coinbase.ready(); // finishes restoring any stored token
if (!client.coinbase.isConnected()) {
const connected = await client.coinbase.connect();
if (!connected) {
// User closed the popup or auth failed — show retry
// Watch for OAUTH_POPUP_BLOCKED / OAUTH_POPUP_CLOSED
}
}
const stillValid = await client.coinbase.ensureSession();
if (!stillValid) {
// Token expired — emit coinbase:sessionExpired; call connect() again
await client.coinbase.connect();
}
client.coinbase.onSessionExpired(() => {
// Send user back to the Connect step
});4. Load balances and let the user pick one
Do not hardcode a currency. Show a picker from getBalances() and continue with the selected row.
ts
const balances = await client.coinbase.getBalances();
// Render a picker — prefer eligible rows; optionally show ineligible with a hint
const balance = await pickBalance(balances); // your UI: user selects a CoinbaseBalance
if (!balance.eligible) {
throw new Error('Selected balance is not eligible');
}5. Optional funding tokens
After a balance is selected, optionally let the user include other balances that can fund the withdraw. Funding helpers take balance.raw rows.
ts
const raw = balances.map(item => item.raw);
const candidates = client.coinbase.getFundingTokens(raw, balance.currency);
const defaults = client.coinbase.getDefaultFundingTokens(raw, balance.currency);
// Render toggles from candidates; start from defaults or let the user choose
const fundingTokens = await pickFundingTokens(candidates, defaults); // your UI
const selection = client.coinbase.getSelectionAggregatedBalance({
balances: raw,
currency: balance.currency,
fundingTokens,
});6. Load networks and let the user pick one
ts
const networks = await client.coinbase.getNetworks({
currency: balance.currency,
accountName: balance.id,
});
// Render a picker — prefer eligible networks
const network = await pickNetwork(networks); // your UI: user selects a CoinbaseNetworkItem
if (!network.eligible) {
throw new Error('Selected network is not eligible');
}7. Amount + limits
Collect the amount from the user. Use min / max for the field UI; optional validateWithdrawalAmount for inline errors. Viable above_spendable amounts are auto-adjusted by startWithdrawal (see withdrawal).
ts
const minAmount = await client.coinbase.getMinWithdrawalAmount({
symbol: balance.currency,
exchangeRate: balance.exchangeRate,
network: network.id,
});
const maxAmount = client.coinbase.getMaxWithdrawableAmount({
balance: selection.balance,
exchangeRate: balance.exchangeRate,
symbol: balance.currency,
});
const amount = await askAmount({ minAmount, maxAmount }); // your UI
const validation = await client.coinbase.validateWithdrawalAmount({
amount,
balance: selection.balance,
exchangeRate: balance.exchangeRate,
symbol: balance.currency,
network: network.id,
});
if (!validation.ok && validation.reason !== 'above_spendable') {
// Show validation.reason on the amount field
}8. Start withdrawal
Respect cooldown on Start (default ~30s). Confirm is not blocked by cooldown. When the amount is above the fee-reserve max, the SDK caps it and awaits onAmountAdjusted if provided.
ts
const { isInCooldown, remainingMs } = client.coinbase.getCooldown();
if (isInCooldown) {
// Disable Start — show remainingMs
}
const startResult = await client.coinbase.startWithdrawal(
{
amount,
currency: balance.currency,
name: balance.name, // CoinbaseBalance.name — required
network: network.id,
fundingTokens, // from the funding step
},
{
onAmountAdjusted: ({ formatted }) => {
// Toast / banner — withdrawal continues with the adjusted amount.
showToast(
`Amount reduced to ${formatted.adjustedAmount} for the $${formatted.feeReserveFiat} network fee reserve.`,
)
},
},
);
if (startResult.status === 'idle') {
// Cancelled via onAmountAdjusted returning false (if you used a confirm dialog)
}
if (startResult.status === 'requires2fa') {
// go to 2FA step
}9. Confirm 2FA
The 2FA code can come from SMS, email, or an authenticator app and is typically 6–7 characters — do not restrict the input to exactly 6 digits. Wrong 2FA often returns requires2fa again. Passkeys are not supported → COINBASE_PASSKEY_NOT_SUPPORTED.
ts
const code = await askTwoFactorCode(); // your UI
const confirmResult = await client.coinbase.confirmWithdrawal(code);
if (confirmResult.status === 'requires2fa') {
// Wrong code — keep 2FA UI and show feedback
}
// 'completed' → done; thrown errors → surface and retry when allowed
// User abandons 2FA (clears local pending only — not Coinbase-side)
// client.coinbase.cancelWithdrawal()10. Summary and restart
ts
client.coinbase.onWithdrawalCompleted(() => {
const summary = client.coinbase.getCompletedTransactionSummary();
console.log(summary);
// Show success UI
});
// Before another payment on this session:
await client.restartSession();Next
| Topic | Docs |
|---|---|
OAuth / ensureSession | Connection |
| Balance list | Balances |
| Funding selection | Funding tokens |
| Network picker | Networks |
| Min / max validation | Limits |
| Start / confirm / cancel | Withdrawal |
| Start-button lockout | Cooldown |
| Success screen data | Summary |
| Lifecycle listeners | Events |
| Error codes | Errors |