> ## 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.

# Subscribe to webhooks

> Register webhook endpoints from the Events tab, the App Manifest or POST /webhooks/subscriptions, with the scope, limit and test rules of each path.

You register a webhook endpoint in one of three places. The Events tab and the App Manifest register destinations for your app as a whole, and the API registers one on behalf of the creator whose access token you send. Whichever you use, an endpoint receives an event only when a destination exists for the topic and the consenting token holds the topic's scope.

Before you register, make sure your endpoint answers the verification probe and checks signatures. The [complete receiver](/docs/webhooks/signature-verification#complete-receiver) does both in one Express handler, or you can mount the Builder SDK's [webhook receiver](/docs/app-store/sdk/webhooks).

## Scope per family

| Family | Scope |
| - | - |
| Creator | `read:creator` for payments, subscriptions, follows, rewards, refunds and disputes; `read:chat` for messages, chat state, fan status and presence; `read:post` for post likes and comments; `read:experience` for experience subscriptions |
| Checkout | `read:creator` |
| App | `read:self` |
| Legacy | `read:creator`, except `message.received` and `message.read` which need `read:chat` |

[Scopes and webhook events](/docs/authentication/scopes#scopes-and-webhook-events) lists the scope of every topic.

## Events tab

The **Events** tab of your app in the Developer Area holds one destination per event type.

| Rule | Behaviour |
| - | - |
| Signing secret | One per app, shared by every endpoint on the app. **View signing secret** shows it on demand. |
| Limit | 20 destinations per app, so at most 20 event types. The tab shows the count in use. |
| Scopes | An event whose scope your app lacks shows a missing-permissions warning and is not delivered. Add the scope on the **Authentication** tab or in `oauth.scopes` of your App Manifest. |
| Payouts | `payout.paid` and `payout.failed` are not in the picker. Subscribe to them through the API. |
| Loopback URLs | A destination pointing at localhost is disabled when the list loads. |
| History | **History** on a row shows recent delivery attempts. |
| Disabled destinations | A destination disabled after repeated failures is re-enabled with the row's toggle. |

Creators who authorised your app before you added a scope must authorise it again before the new events are delivered. Checkout events appear in the picker for accounts that have checkout links enabled.

## App Manifest

`webhooks.destinations[]` in `app-manifest.json` declares the same destinations as a list of `{ "topic": "...", "url": "..." }` objects.

| Rule | Behaviour |
| - | - |
| URL | Public `https://` only. Loopback, private and credentialed URLs fail validation. |
| Duplicates | Two destinations with the same topic fail validation. |
| Ownership | While the manifest sets `webhooks`, the Events tab lists only the manifest's rows, read-only. Settings rows return when the manifest drops the list. |
| Missing scope | A destination whose topic needs a scope you lack waits until the scope is granted. |
| Reserved topics | `creator.payment.pending`, `creator.payment.failed`, `checkout_link.refund.rejected`, `checkout_link.refund.failed` and `app.subscription.deactivated` validate and never deliver. So do the legacy names `message.reaction`, `post.like` and `post.comment`. |

The schema is under [`webhooks`](/docs/app-store/app-manifest/schema#webhooks) in the manifest reference.

## API

`POST /webhooks/subscriptions` creates a destination on behalf of the creator whose access token you send.

| Rule | Behaviour |
| - | - |
| Body | `url` and `events`, an array of 1 to 50 event names. |
| Scopes | `read:self` to call the endpoint, plus the scope of every requested event on the token. A missing scope returns `403`. |
| Probe | Fanvue POSTs an unsigned `fanvue.webhook.verification` body to the URL first; see [Verification probe](/docs/webhooks/index#verification-probe). |
| Response | `id` and `signingSecret` (`whsec_` plus 64 hex characters). The secret is returned once. |
| Limit | 20 subscriptions per creator and client. Exceeding it returns `400` with the body `{ "error": "You've reached the maximum number of webhook subscriptions. Delete one before creating another." }`. |
| Listing | `GET /webhooks/subscriptions` returns `id`, `url`, `events` and `createdAt`. There is no enabled flag. |
| Removal | `DELETE /webhooks/subscriptions/{id}`. There is no enable or edit endpoint: delete and recreate. |
| Disabled destinations | A destination disabled after repeated failures sends no notification to API subscribers. Delete and recreate it. |

49 events are subscribable through the API, every Creator, Checkout and Legacy topic in the [Event catalogue](/docs/webhooks/event-catalog). `app.*` events are not.

```bash theme={null}
curl -X POST https://api.fanvue.com/webhooks/subscriptions \
  -H "Authorization: Bearer <token>" \
  -H "X-Fanvue-API-Version: 2025-06-26" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/fanvue",
    "events": ["creator.payment.succeeded", "creator.subscription.activated"]
  }'
```

```json theme={null}
{
  "id": "b6f0e6d2-1f0a-4f1e-9b3a-1c2d3e4f5a6b",
  "signingSecret": "whsec_3b1f8a0c2d4e6f8091a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f708"
}
```

Every subscribe call mints a new destination with its own secret, even for the same URL and creator. Keep a secret per subscription `id` and select the right one when verifying. Deleting one subscription does not affect the secrets of the others.

## Zapier and the Checkout Links page

**Configure webhooks** on a creator's Checkout Links page offers two routes. **No-code (Zapier)** opens the Zapier guide, and **Code (create an app)** opens the Developer Area. Zapier subscribes and unsubscribes through the API on the creator's behalf and verifies signatures itself, so a creator using Zapier manages no URL or secret.

## Test a destination

The Events tab and the API each send a synthetic event to a destination.

| Path | Rule |
| - | - |
| Events tab **Test** | Sends to the row's endpoint. Refused until the app owner has authorised the app with the event's scope. The error is the generic message "Re-authorize your app and grant the required scopes before testing this webhook." and does not name the scopes. |
| `POST /webhooks/test` | Body `{ "event": "creator.follow.created" }`. Rate limited to 30 calls per minute. Returns `404` when the client has no active subscription for the event. Rejects the nine deprecated flat events with a `400` validation body; the response does not name the replacement, so use [the replacement table](/docs/webhooks/event-catalog#legacy-events-deprecated). |

Test payloads carry `TEST-` prefixed ids inside `data`. The envelope `id` is random, and the API returns it.

Test payloads also populate `purchaser.email` and `transaction_id` on payment events. Production `creator.payment.*` deliveries set both to `null`, so don't build on either field there. Production `checkout_link.payment.*` deliveries carry the real values when Fanvue has them.

```bash theme={null}
curl -X POST https://api.fanvue.com/webhooks/test \
  -H "Authorization: Bearer <token>" \
  -H "X-Fanvue-API-Version: 2025-06-26" \
  -H "Content-Type: application/json" \
  -d '{ "event": "creator.follow.created" }'
```

```json theme={null}
{ "id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f", "event": "creator.follow.created" }
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.