> ## 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.* events for your app's own 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 [Webhooks Overview](/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>
  `client_reference_id` and `metadata` appear on every app resource, but are
  currently always `null` / empty — Fanvue does not yet capture an app-supplied
  external reference or attribution map at purchase time. Do not depend on them
  yet.
</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>
