> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# App Webhooks

> Receive app.payment.*, app.subscription.*, app.refund.created, and app.dispute.* webhook events for your Fanvue app’s own on-platform sales.

Fanvue sends webhooks for your app's own sales so you can fulfil access and reconcile money: one-time item purchases and subscriptions, their payments, refunds, and disputes.

They are **scoped to your app's own sales**, so the payload only ever concerns a purchase of your app (the buyer uuid and the amounts), never a creator's wider platform activity.

## Setup and delivery

App webhooks are configured like every other Fanvue webhook: in the Developer Area **Events** tab, add an endpoint and select the `app.*` events you want, then enable the required scope. See the [Webhooks Overview](/docs/webhooks/index) and [legacy setup walkthrough](/docs/webhooks/webhooks-overview) for the full setup, signing secret, and local testing flow.

* **Scope:** every `app.*` event requires the `read:self` scope, the payload only concerns your app's own sale (the buyer uuid and amounts). Enable it in the **Authentication** tab.
* **One endpoint, many events:** `app.*` events arrive on the same endpoints as the platform events (`follow.new`, `purchase.new`, …) and checkout events (`checkout_link.*`) you have subscribed to. Branch on the event `type` (and `data.object`) to route them.

## Amounts and currency

Monetary amounts are integers in the currency's **minor units** (for USD, cents, so `999` means `$9.99`). Each resource carries its own `currency` (ISO 4217), which may be `null` when not yet known.

## Event envelope

Every app webhook is delivered as an HTTP `POST` with a Standard-Webhooks-style envelope:

```json theme={null}
{
  "id": "<event id>",
  "type": "app.payment.succeeded",
  "timestamp": "2026-06-17T13:12:45.123Z",
  "data": { "object": "payment", "...": "resource fields" }
}
```

* `id`: unique event id, stable across delivery retries (use it to dedupe). On `app.subscription.*` the id covers the state transition rather than the occurrence, so pair it with `timestamp` if the same transition can repeat within a billing period, see [Delivery, Retries and Idempotency](/docs/webhooks/delivery-and-idempotency#deduplicating-events).
* `type`: the event topic (see [Available events](#available-events)).
* `timestamp`: ISO 8601 time the event was emitted.
* `data`: the resource object. `data.object` is `"payment"`, `"subscription"`, `"refund"`, or `"dispute"`.

Fields inside `data` are **snake\_case**.

## Available events

All `app.*` events require the `read:self` scope. Each group below links to a reference page with full field definitions and example payloads.

### Payments ([reference](/docs/app-store/webhooks/payments))

* `app.payment.pending`: a charge is created and awaiting confirmation
* `app.payment.succeeded`: a charge succeeds
* `app.payment.failed`: a charge fails

`app.payment.*` events cover **both** one-time purchases and subscription charges; branch on `billing_reason` to tell them apart.

### Subscriptions ([reference](/docs/app-store/webhooks/subscriptions))

* `app.subscription.activated`: a subscription becomes active
* `app.subscription.cancel_at_period_end_changed`: auto-renew is turned off; access continues until period end

### Refunds and disputes ([reference](/docs/app-store/webhooks/refunds-disputes))

* `app.refund.created`: a one-time purchase is refunded, charged back, or cancelled
* `app.payment.refunded`: deprecated alias of `app.refund.created`
* `app.dispute.flagged`: an early chargeback warning, before a formal dispute
* `app.dispute.created`: a formal dispute (chargeback) is opened

## Money and access are separate events

The money and the access state arrive as different events: a subscription's initial charge emits **both** `app.payment.succeeded` (with `billing_reason: subscription_initial`) **and** `app.subscription.activated`. Fulfil access off the subscription event and reconcile revenue off the payment event.

<Note>
  Refund and dispute events (`app.refund.created`, `app.payment.refunded`, `app.dispute.*`) cover **one-time purchases only**, identified by a `purchase_reference` with the `appotp_` prefix. Subscription reversals and chargebacks are reconciled through the [App Subscriptions](/docs/app-store/app-subscriptions) endpoints, not these webhooks.
</Note>

<Note>
  `app.subscription.deactivated` is reserved for when a subscription's access actually ends, but is **not emitted yet**. Read live subscription state with the [App Subscriptions](/docs/app-store/app-subscriptions) endpoints in the meantime.
</Note>

<Info>
  `metadata` appears on every app resource but is currently always empty. `client_reference_id` **is** captured for one-time item purchases when you append it to the item's checkout link, and echoed on the related `app.payment.*` and refund events; it is `null` on subscription charges and whenever it wasn't provided. See [One-time Items](/docs/app-store/payments/one-time-items#attribution).
</Info>

## Verifying signatures

App webhooks are signed exactly like the rest of Fanvue's webhooks, with an `X-Fanvue-Signature` header (`t=<timestamp>,v0=<hmac-sha256-hex>`). Verify it against the **raw request body** before parsing JSON. The header breakdown, the verification flow, and complete Node and Python samples live in [Verify Webhook Signatures](/docs/webhooks/signature-verification).

## Reconciliation

Webhooks can be missed or delayed, so reconcile against the REST endpoints. The app one-time payments endpoints are read-only, require the `read:self` scope, and split into owner-scoped (all buyers of an app you own) and caller-scoped (`/me`, the authenticated user's own payments):

| Endpoint                                          | Scope  | Returns                                                    |
| ------------------------------------------------- | ------ | ---------------------------------------------------------- |
| `GET /apps/{appUuid}/payments`                    | Owner  | All buyers' one-time payments for an app you own           |
| `GET /apps/{appUuid}/payments/{invoiceNumber}`    | Owner  | One payment by Fanvue invoice number                       |
| `GET /apps/{appUuid}/payments/me`                 | Caller | The authenticated user's own one-time payments for the app |
| `GET /apps/{appUuid}/payments/me/{invoiceNumber}` | Caller | The caller's own payment by invoice number                 |

The `invoiceNumber` path parameter matches `data.id` (for a payment) or `data.payment_id` (for the original payment behind a refund). For subscription state, use the [App Subscriptions](/docs/app-store/app-subscriptions) endpoints.

<Note>
  These payment endpoints are not yet part of the generated [API Reference](/docs/api-reference/overview); they are documented here until they appear in the OpenAPI spec.
</Note>
