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. The body’s reason names the wall. restricted_country (or a *_region_unsupported value) means the user’s country of residence isn’t served for funding: nothing about their verification is missing, so don’t send them back through identity verification. incomplete_address, no_identity_document, no_nationality, no_phone, and no_full_ssn (US residents) clear once that detail is on the user’s verification.
  • 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. Every session has a 30-minute window (expires_at), but a link is consumed by use, not just by time. A hosted link is single-use: the moment the user opens the page and starts the payment sheet, that link is spent, even if they close it without paying. An embedded link must be rendered immediately (its underlying payment token lives about 5 minutes).
  • Abandoned sessions need no cleanup. There is no cancel endpoint because none is needed: an unpaid session never charges, holds no funds, and flips to expired on its own. To retry, create a new session at any time; sessions are independent of each other, and creating hosted sessions is free and not throttled. See session status for the poll-side rule.

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.