Skip to main content
POST
Create a funding session

How it works

Ask for a funding session for an amount; we return a checkout_url. There are two kinds, picked with link_type:
  • hosted (default): an Agentcard-hosted payment page. Render it anywhere — an “Add funds” button, a QR code, a chat message — and the user opens it and pays with Apple Pay, Google Pay, or card. Safe to relay: the underlying payment order is created only when the user opens the page. Creating a hosted session is free; an unopened link costs nothing.
  • embedded: an in-app payment link, minted immediately. Load checkout_url in a WKWebView (iOS) or Android WebView and the user pays without leaving your app.
When the payment completes, the funds land in the user’s wallet. Poll GET /api/v2/wallet/fund/{session_id} to know when — that endpoint is the source of truth for money; treat any client-side signal as advisory.

Fees: send the exact amount

Agentcard covers the payment provider’s fee. Send amount_cents for exactly what the user should receive — the wallet is credited the full amount. Do not gross up the charge to compensate for fees; that just over-charges your user.
  • fee_cents on the create and status responses is the provider’s fee (which Agentcard absorbs) when known, or null while it isn’t yet — null means “unknown”, never “free”.
  • fees_covered tells you whether the fee is on us: true (it is — the user receives the full amount_cents), false (an anomalous fee we won’t absorb — the wallet receives the net amount), or null (fee not known yet). false is rare; treat it as net delivery rather than failing the flow.

Requirements

  • The user must be identity-verified. Funding reuses the user’s completed identity verification — there is no separate verification step inside checkout. If the user isn’t verified, create-session returns 422 kyc_required; run identity verification first, then retry. Other verification-related 422s:
    • funding_profile_required — a one-time funding profile is missing (collect it, then retry).
    • kyc_transfer_required — the user’s existing verification needs a one-time carry-over before funding (a single extra step, no re-verification).
    • unsupported — the user’s verification can’t be used for funding (for example, a non-US user).
  • Amount bounds come from the API. If the amount is out of range you get 422 amount_out_of_range with min_amount_cents and max_amount_cents in the response — read those rather than hardcoding limits.
  • Link lifetime. hosted and embedded links both stay openable for the session’s 30-minute window.

Embedded: in-app checkout

Create the session with link_type: "embedded", hand checkout_url to your app, and load it in a WebView:
  • Apple Pay renders inside a WKWebView on iOS 16+ (Apple Pay on the Web is supported in WKWebView, but not in SFSafariViewController — use a WKWebView). On older iOS the user pays with Google Pay or card. No domain verification is required on your side; the checkout runs on our Apple-registered domain and pops the native Apple Pay sheet in-app.
  • Google Pay works in a modern Android WebView; card is always available as a fallback.
  • Completion: the page navigates to /fund/success when the payment completes — observe that navigation to close your WebView — and the status endpoint is the authoritative confirmation.
  • Wallet-only sheet: append &only=apple_pay (or &only=google_pay) to the checkout_url fragment to present just that wallet button with no card form.

Mint on demand, not per render

Every embedded session creates a real payment order the moment you call the endpoint. Mint one only when the user has actually initiated payment (tapped “Add funds”), render it immediately, and create a fresh session for the next attempt if the link lapses. Don’t mint on page render or in retry loops — the API refuses excess pending sessions for the same user with too_many_pending_sessions.
  • Never relay an embedded link through chat or email; link unfurlers can consume it and leave the user a dead page. Server → WebView, immediately, is the only safe path.
  • The embedded checkout_url appears only on the create response; the status endpoint never re-serves it. If it lapsed, create a new session.
  • Test with sandbox-mode credentials — sandbox clients create TEST orders (never charged), so you can exercise the full WebView flow end to end before switching to live keys.
  • On a physical device, cards must be in the platform wallet; simulators cannot show the Apple Pay sheet.

Authorizations

Authorization
string
header
required

A platform access token. Get one on the Create an access token endpoint by exchanging your client_id + client_secret, then send it as Authorization: Bearer <token>. Tokens live one hour.

Body

application/json
user_id
string
required

The connected user's id.

amount_cents
integer
required

Amount to fund, in USD cents.

payment_method
enum<string>
default:apple_pay

google_pay with link_type "embedded" is accepted when the user's rail returns the web checkout style; on web rails embedded is Apple Pay only (422 payment_method_not_supported).

Available options:
apple_pay,
google_pay

hosted returns an Agentcard-hosted payment page, safe to relay anywhere (chat, email, QR); the underlying payment order is created only when the user opens it, so unopened hosted sessions cost nothing. embedded creates a REAL payment order immediately for rendering inside your own app; mint it only when the user initiates payment, render it immediately, and never relay it through chat (link unfurlers consume it). Embedded responses carry checkout_style (crossmint_sdk | cb_onramp | web) — branch your rendering on it: crossmint_sdk boots Crossmint's native mobile SDK from the response's crossmint object (native Apple Pay / Google Pay sheet in-app), cb_onramp loads checkout_url in a WKWebView with the cbOnramp message handler (Apple Pay only — google_pay on that rail is rejected with payment_method_not_supported; the link lives about 5 minutes and counts toward the user's per-user payment limits even if never paid), web opens checkout_url in a browser context. Sandbox-mode credentials create TEST orders (never charged).

Available options:
hosted,
embedded

Response

The funding session, with the payment link to show the user.

object
string
Allowed value: "funding_session"
id
string
user_id
string
status
enum<string>
Available options:
pending,
processing,
completed,
failed,
expired
amount_cents
integer
currency
string
payment_method
enum<string>
Available options:
apple_pay,
google_pay
checkout_url
string

The payment link to show the user. hosted: an Agentcard-hosted page, present while the link can still be opened. embedded: the raw provider Apple Pay link, present ONLY on the create response; the poll endpoint never re-serves it, so load it in an in-app webview immediately, never relay it, and create a new session if it lapses.

failure_reason
enum<string> | null
Available options:
region_not_supported,
provider_error,
null
completed_at
string<date-time> | null
created_at
string<date-time>
expires_at
string<date-time>

On the create response: hosted links stay openable for 30 minutes; embedded links are single-use and expire about 5 minutes after creation (create a new session instead of retrying a lapsed link). On the poll endpoint, expires_at always reflects the session's 30-minute fundability window, not the embedded link's shorter life.

Which kind of checkout_url this session carries. Returned only on the create response; the poll endpoint does not include it.

Available options:
hosted,
embedded
fee_cents
integer | null

The payment provider's fee in USD cents, which Agentcard covers (see fees_covered) — the user's wallet is credited the full amount_cents, so do NOT gross up the charge. Null while the fee isn't known yet (e.g. a hosted link whose payment order hasn't been minted) — null means unknown, never free. Always present on both the create response and the poll endpoint.

fees_covered
boolean | null

Whether Agentcard absorbs the provider fee for this session. true: the wallet receives the full amount_cents — send the exact amount the user should receive and do not gross up. false (rare): the fee was anomalous and the wallet receives the net amount. null: the fee isn't known yet. After completion this reflects the actual outcome.

checkout_style
enum<string>

Embedded create responses only — which rendering contract applies. 'crossmint_sdk': initialize Crossmint's native mobile checkout SDK with the crossmint object (native Apple Pay / Google Pay sheet in-app; checkout_url stays a web fallback). 'cb_onramp': load checkout_url in a WKWebView with a script message handler named cbOnramp (native Apple Pay button page with lifecycle events). 'web': open checkout_url in a browser context; the page navigates to /fund/success on completion. Treat unrecognized values as 'web'.

Available options:
crossmint_sdk,
cb_onramp,
web
embed_url
string

Embedded create responses on the crossmint_sdk style only — the PREFERRED integration. A fully-built, provider-opaque wallet-button page: load it as-is in an in-app webview (Agentcard's iOS tooling renders it as a native-looking Apple Pay button pill). Built entirely server-side, so the payment rail behind it can change without any partner-side work. Same one-time, single-order lifetime as the credentials it embeds.

crossmint
object

Embedded create responses on the crossmint_sdk style only. Boot credentials for Crossmint's native checkout SDK (Swift / Kotlin / React Native) — an alternative to embed_url for apps that prefer the vendor SDK. client_secret is scoped to this single order and is returned exactly once — hand it to the paying user's device, never store or relay it; create a new session if it is lost.