<!--
Sitemap:
- [Tempo Accounts SDK - Getting Started](/docs/): Set up the Tempo Accounts SDK to create, manage, and interact with accounts on Tempo.
- [Deploying to Production](/docs/production): Things to consider before deploying your application with the Tempo Accounts SDK to production.
- [FAQ](/docs/faq): Frequently asked questions about the Tempo Accounts SDK.
- [Connect Accounts](/docs/guides/connect-accounts): Connect a Tempo account in your application.
- [Authentication](/docs/guides/authentication): Authenticate connected accounts against your own server with a signed SIWE challenge.
- [Identity](/docs/guides/identity): Request and verify identity claims about a connected account, starting with a verified email.
- [Transfers](/docs/guides/transfers): Send stablecoin transfers from a connected Tempo account, initiated either by the user or by your server.
- [Spend Permissions](/docs/guides/spend-permissions): Authorize spend limits, call scopes, and expiries so repeat transfers can be signed without a confirmation prompt.
- [React Native](/docs/guides/react-native): Set up Tempo Accounts in a React Native app.
- [Subscriptions](/docs/guides/subscriptions): Charge recurring payments from a connected Tempo account.
- [Fee Sponsorship](/docs/guides/fee-sponsorship): Sponsor transaction fees from a server-controlled policy.
- [Deposits](/docs/guides/deposits): Open the Tempo deposit flow from a connected account and let the user choose deposit details.
- [Swaps](/docs/guides/swaps): Open the Tempo swap flow from a connected account with optional pre-filled intent fields.
- [Theming](/docs/guides/theming): Match embedded account surfaces to your product.
- [CLI](/docs/guides/cli): Authorize and use Tempo accounts from command-line tools.
- [Adapters](/docs/adapters/): Choose the signing adapter for your Tempo Accounts SDK integration.
- [Tempo Wallet Adapter](/docs/adapters/tempo-wallet): Use Tempo Wallet as the hosted universal wallet adapter.
- [WebAuthn Adapter](/docs/adapters/webauthn): Use domain-bound passkeys as the account signing adapter.
- [Turnkey Adapter](/docs/adapters/turnkey): Use Turnkey-managed wallet accounts as the account signing adapter.
- [Privy Adapter](/docs/adapters/privy): Use Privy embedded wallets as the account signing adapter.
- [Private Key Adapter](/docs/adapters/private-key): Sign in-process with a `secp256k1` private key.
- [Custom Adapter](/docs/adapters/custom): Author your own adapter with the `Adapter.define` API.
- [Adapters](/docs/api/adapters): Pluggable adapters for the Tempo Accounts SDK Provider.
- [dialog](/docs/api/dialog): Adapter for the Tempo Wallet dialog, an embedded iframe or popup for account management.
- [local](/docs/api/local): Key-agnostic adapter for defining arbitrary account types and signing mechanisms.
- [mobileWebAuth](/docs/api/mobileWebAuth): Mobile web auth adapter for browser-session wallet requests.
- [postMessage](/docs/api/postMessage): Connect to wallet from anywhere on the web
- [privy](/docs/api/privy): React adapter backed by Privy sessions and embedded Ethereum wallets.
- [secp256k1](/docs/api/secp256k1): Adapter that signs in-process with a `secp256k1` private key.
- [turnkey](/docs/api/turnkey): Adapter backed by Turnkey client sessions.
- [webAuthn](/docs/api/webAuthn): Adapter for passkey-based accounts using WebAuthn registration and authentication.
- [Dialog](/docs/api/dialogs): Dialog modes for embedding the Tempo Wallet.
- [Dialog.iframe](/docs/api/dialog.iframe): Embed the Tempo Wallet auth UI in an iframe dialog element.
- [Dialog.popup](/docs/api/dialog.popup): Open the Tempo Wallet auth UI in a popup window.
- [Expiry](/docs/api/expiry): Utility functions for computing access key expiry timestamps.
- [Provider](/docs/api/provider): Create an EIP-1193 provider for managing accounts on Tempo.
- [Rpc](/docs/api/rpc): Per-method Zod schemas and shared building blocks for the Accounts JSON-RPC surface.
- [Schema](/docs/api/schema): Zod-based JSON-RPC schema definitions for the Accounts provider.
- [TrustedHosts](/docs/api/trustedHosts): Trusted host mappings and matching helpers for dialog adapters.
- [WebAuthnCeremony](/docs/api/webauthnceremony): Pluggable strategy for WebAuthn registration and authentication ceremonies.
- [WebAuthnCeremony.from](/docs/api/webauthnceremony.from): Create a WebAuthnCeremony from a custom implementation.
- [WebAuthnCeremony.server](/docs/api/webauthnceremony.server): Server-backed WebAuthn ceremony that delegates to a remote handler.
- [CLI](/docs/cli/provider): Create a Provider for CLI environments.
- [eth_accounts](/docs/rpc/eth_accounts): Get the addresses of the currently connected accounts.
- [eth_chainId](/docs/rpc/eth_chainId): Get the chain ID of the currently active chain.
- [eth_fillTransaction](/docs/rpc/eth_fillTransaction): Fills missing transaction fields and returns wallet-aware metadata.
- [eth_requestAccounts](/docs/rpc/eth_requestAccounts): Request access to user accounts, prompting the user to connect if needed.
- [eth_sendTransaction](/docs/rpc/eth_sendTransaction): Send a transaction from the connected account.
- [eth_sendTransactionSync](/docs/rpc/eth_sendTransactionSync): Send a transaction and wait for the receipt.
- [eth_signTransaction](/docs/rpc/eth_signTransaction): Sign a transaction without broadcasting it.
- [eth_signTypedData_v4](/docs/rpc/eth_signTypedData_v4): Sign EIP-712 typed structured data with the connected account.
- [personal_sign](/docs/rpc/personal_sign): Sign a message with the connected account.
- [wallet_authorizeAccessKey](/docs/rpc/wallet_authorizeAccessKey): Authorize an access key for delegated transaction signing.
- [wallet_connect](/docs/rpc/wallet_connect): Connect account(s) with optional capabilities like access key authorization.
- [wallet_deposit](/docs/rpc/wallet_deposit): Open the wallet deposit flow with optional pre-filled fields.
- [wallet_depositZone](/docs/rpc/wallet_depositZone): Open the wallet zone-deposit flow with optional pre-filled fields.
- [wallet_disconnect](/docs/rpc/wallet_disconnect): Disconnect the connected account(s).
- [wallet_getBalances](/docs/rpc/wallet_getBalances): Get token balances for an account.
- [wallet_getCallsStatus](/docs/rpc/wallet_getCallsStatus): Get the status of a batch of calls sent via wallet_sendCalls.
- [wallet_getCapabilities](/docs/rpc/wallet_getCapabilities): Get account capabilities for specified chains.
- [wallet_revokeAccessKey](/docs/rpc/wallet_revokeAccessKey): Revoke a previously authorized access key.
- [wallet_send](/docs/rpc/wallet_send): Open the wallet send-token flow with optional pre-filled fields.
- [wallet_sendCalls](/docs/rpc/wallet_sendCalls): Send a batch of calls from the connected account.
- [wallet_swap](/docs/rpc/wallet_swap): Open the wallet swap flow with optional pre-filled swap intent fields.
- [wallet_switchEthereumChain](/docs/rpc/wallet_switchEthereumChain): Switch the provider's active chain.
- [wallet_withdrawZone](/docs/rpc/wallet_withdrawZone): Open the wallet zone-withdraw flow with optional pre-filled fields.
- [Remote](/docs/api/remote): Bridge that runs inside the wallet's iframe/popup and serves RPC requests from the host SDK.
- [Remote.create](/docs/api/remote.create): Create a remote context bound to a Messenger and Provider.
- [Remote.useEnsureVisibility](/docs/api/remote.useEnsureVisibility): React hook that monitors iframe visibility and falls back to a popup when occluded.
- [Remote.useState](/docs/api/remote.useState): React hook to subscribe to a remote context's state store.
- [Remote.useTheme](/docs/api/remote.useTheme): React hook that applies theme overrides from URL search params and live messenger updates.
- [Remote.validateSearch](/docs/api/remote.validateSearch): Validate an RPC request payload from URL search params.
- [Tempo Accounts Server Handlers](/docs/server/): Configure server-side Tempo Accounts SDK handlers for relaying wallet RPC requests, composing backends, and managing WebAuthn ceremonies.
- [Handler.auth](/docs/server/handler.auth): Server handler that issues SIWE-based authentication challenges and sessions.
- [Handler.codeAuth](/docs/server/handler.codeAuth): Server handler for the device-code (PKCE) access-key bootstrap flow.
- [Handler.compose](/docs/server/handler.compose): Compose multiple server handlers into a single handler.
- [Handler.exchange](/docs/server/handler.exchange): Server handler that returns Stablecoin DEX quotes and ready-to-submit calls.
- [Handler.relay](/docs/server/handler.relay): Server handler that proxies certain RPC requests with wallet-aware enrichment.
- [Handler.webAuthn](/docs/server/handler.webAuthn): Server-side WebAuthn ceremony handler for registration and authentication.
- [hc](/docs/server/hc): Typed RPC client for handlers built with the Tempo Accounts SDK.
- [Identity.verify](/docs/server/identity.verify): Verify a wallet-issued identity token (verified email) against an issuer's JWKS.
- [Kv](/docs/server/kv): Key-value store adapters for server-side persistence.
- [Kv.cloudflare](/docs/server/kv.cloudflare): Kv adapter backed by a Cloudflare Workers KV namespace.
- [Kv.durableObject](/docs/server/kv.durableObject): Kv adapter backed by a Cloudflare Durable Object with atomic take and create.
- [Kv.from](/docs/server/kv.from): Wrap a custom Kv-shaped object so the SDK accepts it as a Kv.
- [Kv.memory](/docs/server/kv.memory): In-memory Kv adapter for tests and single-process deployments.
- [Keystore](/docs/api/keystore): Pluggable backends for key material.
- [Keystore.p256](/docs/api/keystore.p256): Pure-JS P-256 keystore.
- [Keystore.secp256k1](/docs/api/keystore.secp256k1): Pure-JS secp256k1 keystore.
- [Keystore.webCryptoP256](/docs/api/keystore.webCryptoP256): WebCrypto P-256 keystore.
- [Storage](/docs/api/storage): Pluggable storage adapters for persisting provider state.
- [Storage.combine](/docs/api/storage.combine): Combine multiple Storage adapters into one.
- [Storage.cookie](/docs/api/storage.cookie): Cookie-backed Storage adapter.
- [Storage.from](/docs/api/storage.from): Create a Storage adapter from a custom implementation.
- [Storage.idb](/docs/api/storage.idb): IndexedDB-backed Storage adapter.
- [Storage.localStorage](/docs/api/storage.localStorage): localStorage-backed Storage adapter.
- [Storage.memory](/docs/api/storage.memory): In-memory Storage adapter.
- [asyncStorage](/docs/api/storage.asyncStorage): React Native Storage adapter backed by AsyncStorage.
- [secureMmkv](/docs/api/storage.secureMmkv): Encrypted React Native Storage adapter backed by MMKV.
- [Tempo Accounts SDK](/index): The fastest way to add stablecoins to your application.
- [Secp256k1 Adapter](/docs/adapters/secp256k1): Sign in-process with a `secp256k1` private key.
- [Bring Your Auth](/docs/enterprise/bring-your-auth/): Connect enterprise auth and signing systems to Tempo accounts.
- [Hosted Universal Wallets](/docs/enterprise/hosted-universal-wallets): Stub for hosting a universal wallet on your own domain.
- [Handler.feePayer (Deprecated)](/docs/server/handler.feePayer): Deprecated — use Handler.relay with feePayer option instead.
- [tempoWallet](/docs/wagmi/tempoWallet): Wagmi connector for the Tempo Wallet dialog.
- [webAuthn](/docs/wagmi/webAuthn): Wagmi connector for passkey-based WebAuthn accounts.
- [AWS KMS](/docs/enterprise/bring-your-auth/aws-kms): Stub for integrating AWS KMS-backed signing with the Tempo Accounts SDK.
- [Custom Auth](/docs/enterprise/bring-your-auth/custom): Stub for first-party enterprise auth and signing integrations.
- [Privy](/docs/enterprise/bring-your-auth/privy): Enterprise notes for integrating Privy-backed auth with the Tempo Accounts SDK.
- [Turnkey](/docs/enterprise/bring-your-auth/turnkey): Stub for integrating Turnkey-backed signing with the Tempo Accounts SDK.
-->

# Transfers

Send stablecoin transfers from a connected Tempo account using the [Tempo Accounts SDK](/docs).

There are two common ways to move value out of a connected account:

* **User-initiated transfers**: your UI calls `wallet_transfer` and the connected account signs and broadcasts a stablecoin transfer.
* **Server-initiated transfers**: your server responds with HTTP `402 Payment Required` using the [Machine Payments Protocol (MPP)](https://mpp.dev) [charge method](https://mpp.dev/payment-methods/tempo/charge), describing the amount, currency, and recipient. The Tempo Accounts SDK auto-fulfills the challenge from the connected account and retries the request, so your client code stays a plain `fetch`.

<Tabs stateKey="transfer-mode">
  <Tab title="User-initiated Transfers" className="pt-8!">
    ## Demo

    By the end of this section you'll have a button that sends pathUSD from the connected account to a recipient.

    <Demo title="User-initiated Transfer" githubUrl="https://github.com/tempoxyz/accounts/tree/main/examples/transfers" headerAction={<DemoReset />} className="flex flex-col gap-3">
      <Steps.Step label="Create an account, or use an existing one." action={<ConnectAccount />} />

      <SendPayment />
    </Demo>

    ## Walkthrough

    ::::steps

    ### Connect an Account

    Follow the [Connect Accounts](/docs/guides/connect-accounts) guide to set up Wagmi with a Tempo signing adapter and let the user sign in. This guide will still function regardless of adapter.

    ### Send the Transfer

    Call [`Hooks.wallet.useTransfer`](https://wagmi.sh/tempo/hooks/wallet.useTransfer) from `wagmi/tempo` and pass the amount, recipient, and token.

    ```tsx twoslash [Transfer.tsx]
    // @noErrors
    import { Hooks } from 'wagmi/tempo'

    export function Transfer() {
      const transfer = Hooks.wallet.useTransfer()

      return (
        <button
          onClick={() =>
            transfer.mutate({
              amount: '1',
              to: '0x0000000000000000000000000000000000000001',
              token: 'pathusd',
            })
          }
        >
          Pay $1
        </button>
      )
    }
    ```

    ::::

    ## Best Practices

    ### Loading State

    Use `transfer.isPending` to disable the button and show a pending label while the transfer is in flight.

    ```tsx twoslash [Transfer.tsx]
    // @noErrors
    import { Hooks } from 'wagmi/tempo'

    export function Transfer() {
      const transfer = Hooks.wallet.useTransfer()

      return (
        <button
          disabled={transfer.isPending} // [!code ++]
          onClick={() =>
            transfer.mutate({
              amount: '1',
              to: '0x0000000000000000000000000000000000000001',
              token: 'pathusd',
            })
          }
        >
          {transfer.isPending ? 'Sending...' : 'Pay $1'} {/* [!code ++] */}
        </button>
      )
    }
    ```

    ### Error Handling

    If the transfer fails — for example, the user rejects the request, or the access key has insufficient allowance — surface the error from `transfer.error` to the user.

    ```tsx twoslash [Transfer.tsx]
    // @noErrors
    import { Hooks } from 'wagmi/tempo'

    export function Transfer() {
      const transfer = Hooks.wallet.useTransfer()

      return (
        <div>
          <button onClick={() => transfer.mutate({ /* ... */ })}>Pay $1</button>
          {transfer.error && ( // [!code ++]
            <pre>{`${transfer.error.name}: ${transfer.error.message}`}</pre> /* [!code ++] */
          )} {/* [!code ++] */}
        </div>
      )
    }
    ```

    ### Success State

    When the transfer succeeds, `transfer.data.receipt.transactionHash` is the on-chain transaction hash. Link to the block explorer, refresh balances, or surface a success state to the user.

    ```tsx twoslash [Transfer.tsx]
    // @noErrors
    import { tempoModerato } from 'wagmi/chains' // [!code ++]
    import { Hooks } from 'wagmi/tempo'

    export function Transfer() {
      const transfer = Hooks.wallet.useTransfer()

      return (
        <div>
          <button onClick={() => transfer.mutate({ /* ... */ })}>Pay $1</button>
          {transfer.isSuccess && ( // [!code ++]
            <a /* [!code ++] */
              href={`${tempoModerato.blockExplorers.default.url}/tx/${transfer.data.receipt.transactionHash}`} /* [!code ++] */
              target="_blank" /* [!code ++] */
              rel="noreferrer" /* [!code ++] */
            > {/* [!code ++] */}
              See receipt {/* [!code ++] */}
            </a> /* [!code ++] */
          )} {/* [!code ++] */}
        </div>
      )
    }
    ```
  </Tab>

  <Tab title="Server-initiated Transfers" className="pt-8!">
    ## Demo

    By the end of this section you'll have an HTTP endpoint that charges $100 in pathUSD per call before returning its payload. The button below hits a real `/api/transfer` endpoint hosted on this docs site — the connected account auto-fulfills the `402 Payment Required` challenge and the request resolves only after settlement.

    <Demo title="Server-initiated Transfer" githubUrl="https://github.com/tempoxyz/accounts/tree/main/examples/transfers" headerAction={<DemoReset />} className="flex flex-col gap-3">
      <Steps.Step label="Create an account, or use an existing one." action={<ConnectAccount />} />

      <ServerTransfer />
    </Demo>

    ## Walkthrough

    ::::steps

    ### Install the Server Dependencies

    Install [`mppx`](https://github.com/tempoxyz/mppx) for serving and verifying HTTP `402` payment challenges. This guide uses [Hono](https://hono.dev) on Cloudflare Workers; the same handler shape works on Node.js, Bun, Deno, Express, and Next.js.

    :::code-group

    ```bash [npm]
    npm i mppx hono
    ```

    ```bash [pnpm]
    pnpm i mppx hono
    ```

    ```bash [bun]
    bun i mppx hono
    ```

    :::

    ### Define a Charge

    Gate the endpoint behind a stablecoin transfer. If the client hasn't paid, return the payment challenge. Once paid, return your payload with a verifying receipt.

    ```ts twoslash [worker/index.ts]
    // @noErrors
    import { Hono } from 'hono'
    import { Mppx, tempo } from 'mppx/server'

    const pathUsd = '0x20c0000000000000000000000000000000000000'

    const mppx = Mppx.create({
      methods: [tempo.charge({ testnet: true })],
    })

    const app = new Hono()

    app.get('/api/transfer', async (c) => {
      const result = await mppx.charge({
        amount: '100',
        currency: pathUsd,
        recipient: '0x0000000000000000000000000000000000000001',
      })(c.req.raw)

      if (result.status === 402) return result.challenge

      return result.withReceipt(
        Response.json({
          message: 'Thanks for your $100.',
        }),
      )
    })

    export default app
    ```

    ### Call It from the Client

    No extra client code is required. Use `fetch` (or any HTTP client) the same way you would for any other endpoint. The Tempo Accounts SDK transparently handles the `402`, signs a transfer with the connected account, and replays the request.

    ```tsx twoslash [Charge.tsx]
    // @noErrors
    import { useState } from 'react'

    export function Charge() {
      const [data, setData] = useState<unknown>()
      const [isPending, setPending] = useState(false)

      return (
        <div>
          <button
            type="button"
            disabled={isPending}
            onClick={async () => {
              setPending(true)
              try {
                const res = await fetch('/api/transfer')
                setData(await res.json())
              } finally {
                setPending(false)
              }
            }}
          >
            {isPending ? 'Transferring...' : 'GET /api/transfer'}
          </button>
          {data !== undefined && <pre>{JSON.stringify(data, null, 2)}</pre>}
        </div>
      )
    }
    ```

    ::::

    ## Best Practices

    ### React Query for Mutations

    Reach for `useMutation` from `@tanstack/react-query` instead of hand-rolling `useState` for `data`, `error`, and `isPending`. You get retries, cache invalidation, and consistent loading/error semantics for free, and the rest of your app can react to the latest charge result.

    ```tsx twoslash [Charge.tsx]
    // @noErrors
    import { useMutation } from '@tanstack/react-query'

    export function Charge() {
      const charge = useMutation({
        mutationFn: async () => {
          const res = await fetch('/api/transfer')
          if (!res.ok) throw new Error(await res.text())
          return res.json()
        },
      })

      return (
        <div>
          <button disabled={charge.isPending} onClick={() => charge.mutate()}>
            {charge.isPending ? 'Transferring...' : 'GET /api/transfer'}
          </button>
          {charge.error && <pre>{charge.error.message}</pre>}
          {charge.data && <pre>{JSON.stringify(charge.data, null, 2)}</pre>}
        </div>
      )
    }
    ```

    ### Identify the Payer

    After `mppx.charge` verifies payment, the `Authorization` header is still on the request. Decode it with `Credential.deserialize` to read the payer's DID (e.g. `did:pkh:eip155:1:0x...`). Use it for per-payer rate limiting, analytics, or audit logs.

    ```ts twoslash [worker/index.ts]
    // @noErrors
    import { Credential } from 'mppx'

    app.get('/api/transfer', async (c) => {
      const result = await mppx.charge({ /* ... */ })(c.req.raw)
      if (result.status === 402) return result.challenge

      const credential = Credential.deserialize(c.req.header('Authorization')!) // [!code ++]
      const payer = credential.source // "did:pkh:eip155:1:0x..." // [!code ++]

      return result.withReceipt(Response.json({ payer }))
    })
    ```

    ### Reuse Credentials with `Expires`

    By default, each request requires a fresh on-chain transfer. For tightly coupled flows (e.g. a paid endpoint that the user hits repeatedly within a short window), set `expires` so a paid credential is reusable for that duration. The user pays once and reuses the credential until it expires.

    ```ts twoslash [worker/index.ts]
    // @noErrors
    import { Expires } from 'mppx'

    const result = await mppx.charge({
      amount: '100',
      currency: pathUsd,
      recipient: '0x0000000000000000000000000000000000000001',
      expires: Expires.minutes(5), // [!code ++]
    })(c.req.raw)
    ```
  </Tab>
</Tabs>

## Next Steps

<Cards>
  <Card description="Authorize spend limits, scopes, and durations so transfers can be signed without a confirmation prompt." to="/docs/guides/spend-permissions" icon="lucide:shield" title="Spend Permissions" />

  <Card description="Sponsor transaction fees for your users so transfers are feeless from their perspective." to="/docs/guides/fee-sponsorship" icon="lucide:badge-dollar-sign" title="Fee Sponsorship" />

  <Card description="Charge users on a recurring schedule using server-initiated transfers." to="/docs/guides/subscriptions" icon="lucide:repeat" title="Subscriptions" />
</Cards>
