# Funding

Funding lets a user add crypto to their Openfort wallet from **any chain, any token, or a centralized exchange** — and have it arrive as the token you want on the chain you want. Under the hood it mints a single deposit address per route and bridges/swaps the inbound funds to the destination, so your users never have to think about networks or wrapping.

It is the same primitive whether a human taps a button or an agent calls an endpoint: one **funding session** with a destination and a source.

:::info
Funding settles into the user's Openfort **embedded wallet** (or a **backend wallet** for treasury/agent flows). The destination is always a wallet you control through Openfort, so you can act on the funds the moment they land.
:::

## How a session works

A funding session is one deposit attempt against a destination.

:::steps

### Create a session

Specify where the funds should land — a `target` of chain (CAIP‑2), token, and wallet address. You get back a session `id` and a `clientSecret`.

### Set a payment method

Commit the source the user will send from (an EVM or Solana wallet, or an exchange). This mints a **deposit address** and returns a QR + wallet deeplinks.

### Poll until settled

The session moves through `requires_payment_method → waiting_payment → processing → succeeded` (or `bounced` / `expired`). Read it back, or subscribe to [webhooks](/docs/configuration/funding/webhooks).

:::

```ts
type FundingTarget = { chain: string; currency: string; address: string } // CAIP-2 destination
type FundingSource = { chain: string; currency: string; amount: string }  // where the user sends from
type FundingStatus =
  | 'requires_payment_method'
  | 'waiting_payment'
  | 'processing'
  | 'succeeded'
  | 'bounced'
  | 'expired'
```

### Session statuses

A session moves forward through these states and stops at one of three terminal ones. Read the current status from `get`, or subscribe to [webhooks](/docs/configuration/funding/webhooks).

| Status | What it means | Terminal |
| --- | --- | --- |
| `requires_payment_method` | Session created; no source committed yet. | |
| `waiting_payment` | Deposit address minted; waiting for the inbound transfer. | |
| `processing` | Deposit detected; bridging/swapping to the destination. | |
| `succeeded` | Funds delivered to the destination wallet. | ✓ |
| `bounced` | Delivery failed; funds refunded on the source chain. | ✓ |
| `expired` | No deposit arrived before the session expired. | ✓ |

## Payment methods

A user can fund a wallet four ways. The first two are **transfers** that run through the session lifecycle above; the last two **buy** crypto on an external rail and settle straight to the wallet.

| Method | How the user pays | Rail |
| --- | --- | --- |
| **Self-custody wallet** | Sends from MetaMask, Phantom, Coinbase Wallet… — deeplink or QR | Funding session — `paymentMethod` `evm` / `solana` |
| **Exchange withdrawal** | Withdraws from Binance / Coinbase to the deposit address (guided network, min, memo) | Funding session — `paymentMethod` `cex` |
| **Exchange on-ramp** | Buys on an exchange on-ramp (Coinbase; Binance soon); delivered to the wallet | `pay_link` helper (not a session) |
| **Card / Apple Pay** | Fiat purchase | Existing [Buy](/docs/products/embedded-wallet/react/ui) on-ramp |

:::note
Only the two **transfer** methods emit session status — they mint a deposit address and bridge to the destination. The on-ramp and fiat rails settle directly to the wallet and don't run through the session lifecycle. **Card / Apple Pay (fiat) is available in the React modal only — not yet via the headless API or SDK.**
:::

## Credentials

Every funding call uses two credentials:

| Credential | Where it lives | Scope |
| --- | --- | --- |
| **Publishable key** (`pk_…`) | `Authorization: Bearer` header, client or server | Identifies your project. Safe to ship to the browser. |
| **`clientSecret`** | Returned by `create`; held by the client driving the session | Scopes reads and writes to that one session. Treat it like a one-time token — never log it or reuse it across sessions. |

:::note
A secret-key **server flow** — create a session for any user, list sessions across your project — is planned. Until then, both browser and server integrations authenticate with the publishable key plus the session `clientSecret`.
:::

## Ways to integrate

Pick the surface that fits your app. All three are clients over the same session API, so you can mix them.

<HoverCardLayout>
  <HoverCardLink description="Drop-in Deposit flow in the @openfort/react wallet modal, or the useFunding hook for a custom UI." href="/configuration/funding/react" title="React modal" icon={LayoutTemplateIcon} />

  <HoverCardLink description="The deposit send page the modal links to on mobile — hosted by Openfort or self-hosted." href="/configuration/funding/webview" title="WebView" icon={SmartphoneIcon} />

  <HoverCardLink description="Call the funding endpoints directly from any backend or agent — no UI required." href="/configuration/funding/headless" title="Headless / API" icon={TerminalIcon} />

  <HoverCardLink description="Get notified on every session status change instead of polling." href="/configuration/funding/webhooks" title="Webhooks" icon={WebhookIcon} />
</HoverCardLayout>

## What each surface is for

| Surface | Best for | You ship |
| --- | --- | --- |
| **React modal** | Web apps already using `@openfort/react` | A `Deposit` button — the modal handles the rest |
| **WebView** | Mobile deposits / a self-hosted deposit UI | A link to the deposit send page (Openfort-hosted or your own) |
| **Headless / API** | Backends, agents, custom UIs | Calls to `openfort.funding.*` (or the REST endpoints) |

:::tip
Building an agent or automated treasury? The headless API is agent‑native: give it a destination and it returns a deposit address that settles token X on chain Y into the wallet — no human in the loop. See [Headless / API](/docs/configuration/funding/headless).
:::
