Receiving webhooks
Webhooks
Fansquare sends a signed POST request to your server when a sale happens, a fan is reassigned or the app asks your AI to step in or out.
Webhooks push events to your servers as they happen, instead of polling the API. Each delivery is an HTTPS POST with a JSON body, signed with a secret only you and Fansquare know.
Webhooks speed your integration up, they do not replace the API. A delivery can arrive late, twice or out of order. Keep reconciling against the list endpoints, which remain the source of truth.
Events
| Event | Sent when |
|---|---|
order.created | A new order is recorded for a creator of the agency |
order.refunded | A recorded order is refunded on MYM |
assignment.updated | A fan is assigned to an employee, or unassigned |
conversation.ai_control_requested | Someone in the app asks to relaunch or stop your AI on a conversation |
ping | You ask for a test delivery |
New event types can be added without a new API version. An endpoint only receives the types it subscribed to, plus the ping test deliveries you request: ping is not a type you subscribe to.
Register an endpoint
Create an endpoint in the app, in Settings › Developer API › Webhooks, or with POST /v1/webhooks. You choose:
the URL, in HTTPS, on a public host;
the event types it receives;
the creators it covers: all of them, or a selection. With a key limited to some creators, an endpoint covers the creators of the key at most.
Only the URL itself is checked at creation, its hostname is not resolved. A hostname that resolves to a private address is accepted, then every delivery to it fails.
The response holds the signing secret of the endpoint, fsq_whsec_ followed by 32 characters. It is shown once: store it with your other secrets. You can rotate it at any time.
An agency can register up to 10 endpoints. An endpoint is pinned to an API version at its creation, the Fansquare-Version of the creating request (the current version when you create it in the app): its payloads keep that shape.
A new endpoint, or one that you re-enable or that subscribes to more event types or creators, can take up to a minute to receive every event: an event in that minute may not reach it.
The delivery
Every delivery carries the same envelope. data depends on the event type.
{
"id": "0199c7a2-5d4e-7b3a-9f1e-2c8d6b4a1e07",
"type": "assignment.updated",
"created_at": "2026-10-12T21:14:00.000Z",
"api_version": "2026-07-06",
"data": {
"account_id": "a8c4e2f0-3b5d-4c7e-9f1a-2b4c6d8e0f13",
"fan_id": "5d0e6a2c-7f1b-4c3e-8a9d-0b2c4e6f8a1d",
"assigned_user_id": "3b9f1d7a-2c4e-4a6b-8d0f-1e3a5c7e9b2d"
}
}| Field | Meaning |
|---|---|
id | Event id. The same for every endpoint, retry and replay of this event: deduplicate on it. |
type | Event type. |
created_at | When the fact occurred, not when the delivery was sent. Each event page says which instant it is. |
api_version | Release date of the version the payload is rendered in. |
data | The event itself. |
The request also carries these headers.
| Header | Value |
|---|---|
Fansquare-Signature | v1= and the HMAC-SHA256 of the delivery, see Verify signatures |
Fansquare-Signature-Timestamp | Unix time in seconds at which this attempt was signed |
Fansquare-Event-Type | The type of the body |
Fansquare-Event-Id | The id of the body |
Fansquare-Delivery-Id | Id of this delivery to this endpoint |
User-Agent | Offmy-Webhooks/1.0 |
Respond quickly
Answer with any 2xx status within 10 seconds. Anything else counts as a failure: another status, a timeout, a connection error, or a redirect (redirects are not followed).
Acknowledge first, work later: store the event, answer 200, then process it in the background.
Retries
A failed delivery is retried three times, 10 seconds, 1 minute and 5 minutes after the previous attempt: four attempts in all. Each attempt is signed again, with a fresh timestamp.
Since a delivery can arrive more than once, make your handler idempotent: keep the event ids you processed and skip those you already know. Deliveries are not ordered: compare created_at if order matters to you.
Failures and replay
After the last attempt, the delivery is recorded as a failure, kept 30 days. List them with GET /v1/webhooks/{endpoint_id}/failures or in the app, then replay them once your server is fixed. A replay sends the original event again, with the same id.
After 10 failed deliveries in a row (each one after its four attempts), Fansquare disables the endpoint to stop hammering a broken server: its status becomes disabled, with disabled_reason set to auto_breaker. Failures recorded in the same batch count as one, and any successful delivery resets the count. Fix your server, then re-enable the endpoint with PATCH /v1/webhooks/{endpoint_id} and "status": "active".
While an endpoint is disabled, by you or after failures:
deliveries still pending go to its failures, with the error
endpoint_disabled, ready to replay;events that occur are not sent to it and not recorded: reconcile them with the list endpoints;
a replay is accepted, but goes straight back to the failures until you re-enable it.
Test your endpoint
POST /v1/webhooks/{endpoint_id}/ping sends a ping event to that endpoint, through the same pipeline as real events: same signature, same headers. Use it to check your signature verification before going live. GET /v1/webhooks/{endpoint_id}/stats gives the number of delivered and failed deliveries per event type over a window, the last 7 days by default. Counters are kept 90 days.