Skip to content

Session (React)

A Swapped Connect session is one payment run identified by sessionId. Load it once, run Wallets, Exchange Pay, or Coinbase while it is active, then call restartSession() before another payment.

For UI, prefer useSessionView() over reading raw session.status. The view classifies API status, maintenance, and runtime rejection into screens you can switch on.

HookUse for
useSessionView()Which screen to render
useSession()Session payload, isLoading, isRestarting, error
useRestartSession()restartSession() + isRestarting / error for buttons
useMaintenance()Maintenance enabled / message (when you need the message)

Do not dig into client.getState() for internal fields. Public state is limited to session id, session payload, load/restart flags, error, and maintenance.

Status vs view

LayerTypeUse for
API statusSessionStatus on session.statusDebugging / logging
UI viewSessionView from useSessionView()Which screen to render

Only initiated sessions can start a new payment (isSessionActiveForPayments). After completion (or most other terminal views), call restartSession() or the next Wallets / Coinbase / Exchange Pay call hits SESSION_NOT_ACTIVE.

API statuses (SessionStatus)

StatusMeaning
initiatedReady for payment — maps to view active
awaitingConfirmationPayment in progress — waiting screen, or completed if transactionData is already present
awaitingEmailConfirmationUser must confirm email
completedPayment finished — success / restart
failedPayment failed
cancelledSession cancelled — treat as expired in UI
rejectedRegion / geo rejection (view.country, view.rejectReason)

UI views (SessionViewType)

Returned by useSessionView() (and client.getSessionView()).

ViewWhenSuggested UI
activeNo session yet, or status initiatedYour normal app (payment methods, Wallets, Coinbase, Exchange Pay)
completedStatus completed, or awaitingConfirmation with transactionDataSuccess summary + Start new session (restartSession)
awaitingConfirmationStatus awaitingConfirmation without transaction data yetWaiting / “payment processing”
awaitingEmailConfirmationStatus awaitingEmailConfirmationAsk the user to confirm email
expiredStatus cancelledSession expired + restart
failedStatus failedFailure message + restart
rejectedRegionStatus rejectedRegion not supported — prefer view.rejectReason (may contain \n; use white-space: pre-line), fall back to view.country
rejectedComplianceRuntime session:rejected (compliance)Compliance blocked — no restart of the same session
maintenanceMaintenance flag enabled (wins over status)Maintenance / try later

completed may include optional view.transaction when the SDK can show a completed-transaction screen. For success details, also use useWalletCompletedTransactionSummary, useCoinbaseCompletedTransactionSummary, or useExchangePayCompletedTransactionSummary.

This is the Swapped session. Coinbase OAuth JWT expiry is separate (COINBASE_SESSION_EXPIRED / useOnCoinbaseSessionExpired) — reconnect Coinbase, do not confuse it with expired above.

Render by view

Wrap your payment routes in a gate that switches on view.type:

tsx
import { SessionViewType } from '@swapped/connect-sdk'
import {
  useCoinbaseCompletedTransactionSummary,
  useExchangePayCompletedTransactionSummary,
  useSessionView,
  useSwappedConnectClient,
  useWalletCompletedTransactionSummary,
} from '@swapped/connect-sdk/react'
import type { ReactNode } from 'react'

function SessionViewGate({ children }: { children: ReactNode }) {
  const view = useSessionView()
  const client = useSwappedConnectClient()
  const { summary: walletsSummary } = useWalletCompletedTransactionSummary()
  const coinbaseSummary = useCoinbaseCompletedTransactionSummary()
  const exchangePaySummary = useExchangePayCompletedTransactionSummary()

  const restart = () => {
    void client.restartSession()
  }

  switch (view.type) {
    case SessionViewType.Active:
      return children

    case SessionViewType.Completed:
      if (walletsSummary) {
        return (
          <div>
            <p>
              {walletsSummary.receive.formatted.amount}{' '}
              {walletsSummary.receive.currency}
            </p>
            <button type="button" onClick={restart}>
              Start new session
            </button>
          </div>
        )
      }
      if (coinbaseSummary) {
        return (
          <div>
            <p>
              Sent {coinbaseSummary.amount.amount}{' '}
              {coinbaseSummary.amount.currency}
            </p>
            <button type="button" onClick={restart}>
              Start new session
            </button>
          </div>
        )
      }
      if (exchangePaySummary) {
        return (
          <div>
            <p>
              Paid {exchangePaySummary.amount.amount}{' '}
              {exchangePaySummary.amount.currency}
            </p>
            <button type="button" onClick={restart}>
              Start new session
            </button>
          </div>
        )
      }
      return (
        <div>
          <p>Transaction completed</p>
          <button type="button" onClick={restart}>
            Start new session
          </button>
        </div>
      )

    case SessionViewType.RejectedRegion:
      return (
        <p style={{ whiteSpace: 'pre-line' }}>
          {view.rejectReason ||
            `Not available in ${view.country || 'your region'}`}
        </p>
      )

    case SessionViewType.RejectedCompliance:
      return <p>This payment cannot continue (compliance).</p>

    case SessionViewType.Expired:
      return (
        <div>
          <p>Session expired</p>
          <button type="button" onClick={restart}>
            Start new session
          </button>
        </div>
      )

    case SessionViewType.Failed:
      return (
        <div>
          <p>Transaction failed</p>
          <button type="button" onClick={restart}>
            Start new session
          </button>
        </div>
      )

    case SessionViewType.AwaitingConfirmation:
      return <p>Waiting for confirmation…</p>

    case SessionViewType.AwaitingEmailConfirmation:
      return <p>Confirm your email to continue</p>

    case SessionViewType.Maintenance:
      return <p>Temporarily unavailable. Try again later.</p>

    default:
      return children
  }
}

Use useSessionView() for this branching. Use event hooks (useOnSessionViewChanged, useOnSessionRejected, useOnMaintenanceChanged) only for side effects (analytics, toast, navigate).

Lifecycle

  1. createSwappedConnectClient({ sessionId })loadSession() → wrap with SwappedConnectProvider
  2. While view is active, show payment methods and run Wallets, Exchange Pay, or Coinbase
  3. When the view leaves active, show the matching screen above
  4. restartSession() before another payment (new active session)
  5. destroy() when tearing down the client
tsx
import { useRestartSession } from '@swapped/connect-sdk/react'

function RestartButton() {
  const { restartSession, isRestarting, error } = useRestartSession()

  return (
    <div>
      <button
        type="button"
        disabled={isRestarting}
        onClick={() => void restartSession()}
      >
        {isRestarting ? 'Starting…' : 'Start new session'}
      </button>
      {error ? <p>{error.message}</p> : null}
    </div>
  )
}

Errors

CodeWhenWhat to do
SESSION_NOT_ACTIVEStart payment when status ≠ initiatedShow completed / terminal UI; restartSession()
SESSION_REQUIREDAction needs a loaded session (connect, withdraw, create order, …)loadSession() first
SESSION_ID_REQUIREDMissing id on load / restartPass a valid sessionId

Wallets / Coinbase / Exchange Pay guides call out SESSION_NOT_ACTIVE on their start APIs — same rule: only active / initiated can pay.

Session data and maintenance

tsx
import { useMaintenance, useSession } from '@swapped/connect-sdk/react'

function SessionHeader() {
  const { session, isLoading, error } = useSession()
  const maintenance = useMaintenance()

  if (isLoading) return <p>Loading…</p>
  if (error) return <p>{error}</p>
  if (!session) return null

  return (
    <div>
      <p>{session.merchant.name}</p>
      {maintenance.enabled ? <p>{maintenance.message}</p> : null}
    </div>
  )
}