Webhooks & Events
Receive real-time notifications when resources change in anny. Register a webhook subscription, and the platform will POST event payloads to your URL.
For general request conventions, see JSON-API Conventions. For authentication, see Authentication.
Prerequisites
- A valid Bearer token with admin access
- An HTTPS endpoint capable of receiving POST requests
- Organization context (
?o={organization_id})
Creating Webhooks via the Admin Dashboard
The easiest way to set up webhooks is through the anny admin dashboard:
- Navigate to Organization Settings → API → Webhooks
- Click Create Webhook
- Enter your HTTPS endpoint URL, select the events to subscribe to, and save
For a step-by-step walkthrough with screenshots, see the help center article on webhooks.
Creating a Webhook Subscription via API
Request path documentation: paths/admin/webhooks.yaml
POST /api/v1/webhook-subscriptions?o={org_id}
{
"data": {
"type": "webhook-subscriptions",
"attributes": {
"name": "My Integration",
"url": "https://example.com/webhooks/anny",
"events": ["bookings.created", "bookings.updated", "orders.created"]
}
}
}
Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Descriptive label for the subscription |
url |
string | Yes | HTTPS endpoint to receive webhook POSTs |
events |
string[] | Yes | Array of event identifiers to subscribe to |
custom_headers |
object | No | Additional headers sent with each webhook request |
is_active |
boolean | No | Defaults to true. Set to false to pause delivery. |
Response
The response includes a signing_key — store this securely. It is auto-generated (64-character random string) and used to verify payload signatures.
{
"data": {
"type": "webhook-subscriptions",
"id": "ws-uuid",
"attributes": {
"name": "My Integration",
"url": "https://example.com/webhooks/anny",
"events": ["bookings.created", "bookings.updated", "orders.created"],
"signing_key": "a1b2c3...64chars",
"is_active": true,
"failure_count": 0,
"created_at": "2026-03-25T10:00:00+00:00"
}
}
}
Event Catalog
Bookings
| Event | Triggered when |
|---|---|
bookings.created |
A new booking is created |
bookings.updated |
Booking attributes change (status, dates, etc.) |
bookings.started |
Booking start time is reached |
bookings.ended |
Booking end time is reached |
bookings.checked-in |
Customer checks in |
bookings.checked-out |
Customer checks out |
bookings.deleted |
Booking is permanently deleted |
Orders
| Event | Triggered when |
|---|---|
orders.created |
Checkout is completed and order is placed |
orders.updated |
Order attributes change |
orders.deleted |
Order is permanently deleted |
Invoices
| Event | Triggered when |
|---|---|
invoices.created |
Invoice is generated |
invoices.finalised |
Invoice is locked and assigned a number |
invoices.updated |
Invoice attributes change |
invoices.deleted |
Invoice is deleted |
Customers
| Event | Triggered when |
|---|---|
customers.created |
Customer record is created |
customers.updated |
Customer data changes |
customers.deleted |
Customer is deleted |
Subscriptions (Plans)
| Event | Triggered when |
|---|---|
subscriptions.created |
Subscription is created |
subscriptions.started |
Subscription period begins |
subscriptions.updated |
Subscription attributes change |
subscriptions.renewed |
Subscription renews for a new period |
subscriptions.charged |
Subscription payment is processed |
subscriptions.suspended |
Subscription is paused |
subscriptions.canceled |
Subscription is cancelled |
subscriptions.reactivated |
Suspended subscription resumes |
subscriptions.ended |
Subscription expires |
Timeslots
| Event | Triggered when |
|---|---|
timeslots.created |
Timeslot is created |
timeslots.updated |
Timeslot changes |
timeslots.started |
Timeslot start time is reached |
timeslots.ended |
Timeslot end time is reached |
timeslots.deleted |
Timeslot is deleted |
Communities
| Event | Triggered when |
|---|---|
community-accounts.joined |
Member joins a community |
community-accounts.left |
Member leaves a community |
Payload Structure
Every webhook delivery is a POST request with Content-Type: application/json. The payload uses a normalized flat JSON format — not JSON-API.
{
"event": "bookings.updated",
"event_id": "evt-a1b2c3d4-...",
"webhook_id": "ws-uuid",
"triggered_at": "2026-03-25T10:00:00+00:00",
"data": {
"id": "b1",
"type": "bookings",
"start_date": "2026-04-01T10:00:00+02:00",
"end_date": "2026-04-01T11:00:00+02:00",
"status": "accepted",
"resource": {
"id": "r1",
"type": "resources",
"name": "Meeting Room A",
"slug": "meeting-room-a"
},
"service": {
"id": "s1",
"type": "services",
"name": "Standard Hourly"
},
"customer": {
"id": "c1",
"type": "customers",
"first_name": "Jane",
"last_name": "Doe"
}
}
}
Key differences from JSON-API
| JSON-API (management endpoints) | Webhook payload | |
|---|---|---|
| Attributes | Nested under data.attributes |
Flat on data top level |
| Relationships | Under data.relationships as ID references |
Nested as full objects under their name |
| Side-loaded | included array |
Not used — relations are inline |
| Content-Type | application/vnd.api+json |
application/json |
Included relations per entity
| Entity | Included relations |
|---|---|
| Bookings | resource, resource.parent, service, customer, customer.address |
| Orders | customer, bookings |
| Invoices | customer, order |
| Customers | address |
| Subscriptions | customer, plan |
| Timeslots | resource, service |
| Community Accounts | customer, community |
Note: Included relations and fieldsets may vary by event. The table above shows typical inclusions.
Signature Verification
Every webhook request includes a Signature header containing an HMAC SHA-256 hash of the request body, signed with your subscription's signing_key.
Verifying the Signature
received_signature = request.headers["Signature"]
expected_signature = HMAC-SHA256(signing_key, request.body)
if received_signature != expected_signature:
reject request (return 401)
Important: Always verify the signature before processing the payload. This prevents spoofed requests.
Retry Policy
If your endpoint doesn't respond with a 2xx status code within 10 seconds, the delivery is retried:
| Attempt | Timing |
|---|---|
| 1st | Immediate |
| 2nd | Exponential backoff |
| 3rd | Exponential backoff |
After 3 failed attempts for a single event, that delivery is abandoned.
Auto-Deactivation
If deliveries fail repeatedly across events, the subscription's failure_count increments. After 5 consecutive failures, the subscription is automatically deactivated:
is_activeis set tofalsefailure_countresets to0- Organization admins receive a notification about the deactivation
To reactivate, update the subscription:
PATCH /api/v1/webhook-subscriptions/{id}?o={org_id}
{
"data": {
"type": "webhook-subscriptions",
"id": "{id}",
"attributes": {
"is_active": true
}
}
}
Managing Subscriptions
List All Subscriptions
GET /api/v1/webhook-subscriptions?o={org_id}
Update Events
PATCH /api/v1/webhook-subscriptions/{id}?o={org_id}
{
"data": {
"type": "webhook-subscriptions",
"id": "{id}",
"attributes": {
"events": ["bookings.created", "bookings.updated", "customers.created"]
}
}
}
Delete a Subscription
DELETE /api/v1/webhook-subscriptions/{id}?o={org_id}
Best Practices
- Respond quickly — return
200immediately and process the payload asynchronously. The 10-second timeout is strict. - Handle duplicates — use
event_idto deduplicate. The same event may be delivered more than once on retries. - Verify signatures — always validate the
Signatureheader before trusting the payload. - Subscribe selectively — only subscribe to events you actually process to minimize traffic.
- Monitor
failure_count— periodically check subscription status to catch issues before auto-deactivation.
Normalized JSON via Accept Header
Webhook management endpoints (and all other API GET endpoints) also support Accept: application/json to receive the same flat/normalized format used in webhook payloads.
GET /api/v1/webhook-subscriptions?o={org_id}
Accept: application/json
This returns attributes at the top level instead of nested under data.attributes, matching the webhook payload structure.
POSTandPATCHrequests must still useContent-Type: application/vnd.api+jsonwith standard JSON-API format.