Charge one action from the fan's wallet balance
Charges one of your experience's priced actions straight to the fan's Fanvue wallet balance. No payment dialog is shown — the fan sees nothing, your surface is not interrupted, and the call returns once the money has moved.
That is only possible because the fan agreed to it beforehand. A charge requires a live **spend consent** for this experience, which the fan gives when they top up inside the experience. Without one you get 403 `consent_required`; send the fan back through the in-experience top-up to obtain it. Consent is per app and per experience, and the fan can revoke it.
You name the action, never the amount: `externalActionId` selects an action from this experience's catalogue and Fanvue resolves the price the creator set. Owner app only, and the fan must be one of yours.
**Fulfil on the webhook, never on the bridge result.** Grant the action when you receive `app.experience.action.payment.succeeded`, keyed on `purchase_reference` so a redelivery grants nothing twice. The `purchaseResult` message your iframe receives from Fanvue is UX only — it tells your surface to stop showing a spinner, and it is never proof that money moved. This read is the reconciliation path: poll it when you think you missed a webhook, or to confirm one you are unsure about.
**`Idempotency-Key` is required** (400 without it). Reuse the same key on a retry: once the first charge settled, the same purchase is replayed; while it is still running you get 409. A key reused for a *different* charge is 409 as well, so pick a fresh one per action. Keys are scoped to your app, the fan and the experience, and remembered for 24 hours.
A fan who cannot afford the action is 402 with their `balance` and the `amount` needed — nothing is charged and no purchase attempt is recorded.
post/experiences/{experienceUuid}/action-purchases
Path parameters
experienceUuidstring uuid required
Example:00000000-0000-4000-8000-000000000010
Headers
X-Fanvue-API-Versionstring required
Example:2025-06-26
API version to use for the request
Idempotency-Keystring required
Opaque key that makes this charge safe to retry. Required: the charge moves money, so a retry without one could debit the fan twice.
Request body
Example request
{
"externalActionId": "spin",
"fanUuid": "00000000-0000-4000-8000-000000000003",
"clientReferenceId": "round-42"
}Response
The settled purchase, and the fan's remaining wallet balance
Example response
{
"purchaseReference": "expact_5c9a1f0e-9f3f-4c2a-8f31-1d0f6a2b7c44",
"experienceUuid": "00000000-0000-4000-8000-000000000010",
"externalActionId": "spin",
"clientReferenceId": "round-42",
"buyerUuid": "00000000-0000-4000-8000-000000000003",
"amount": 700,
"currency": "USD",
"invoiceNumber": "FVE-20260904-1234",
"createdAt": "2026-09-04T10:00:00.000Z",
"paidAt": "2026-09-04T10:00:02.000Z",
"walletBalance": 1800
}Changes
Changed in 1 of the 5 revisions of this API.1
- ○
endpoint added
endpoint-added
- ○