Quickstart
This page takes you from nothing to a webhook delivering events to your endpoint.
Before you start
- Your endpoint must be reachable over HTTPS and respond within 10 seconds. See Delivery and batching.
- Each webhook type requires the matching API feature (Occupancy API, Sightings API, or Space Sensor Readings API) to be enabled on at least one of your floors, and events are only delivered for floors where it is enabled. If you cannot register a webhook, your XY Sense representative can enable the feature. See Feature enablement.
- Registering via GraphQL needs
WritePublicApion the API key. See the GraphQL quickstart.
1. Register the webhook
Register via the Admin Console at https://app.xysense.io/settings/webhooks, or programmatically with addWebhook.
Example query
mutation AddWebhook($headers: [InputPair!]) {
addWebhook(
name: "Occupancy stream"
webhookType: OccupancyChanges
endpoint: "https://example.com/hooks/xy"
email: "integrations@example.com"
additionalHeaders: $headers
signatureSecret: "<64-256 char secret>"
enabled: true
) {
id
name
endpoint
enabled
webhookType
}
}
Example variables
{
"headers": [
{
"key": "X-Partner-Token",
"value": "mytoken"
}
]
}
Example response
{
"data": {
"addWebhook": {
"id": "1c9c1e7a-38f6-4a9a-8f4c-6a1f0f2d3e4b",
"name": "Occupancy stream",
"endpoint": "https://example.com/hooks/xy",
"enabled": true,
"webhookType": "OccupancyChanges"
}
}
}
The signature secret cannot be read back after it is set, so store it when you create the webhook.
2. Handle the first delivery
Deliveries are POST requests with Content-Type: application/json; charset=utf-8. The body is a batch envelope, not a bare array of events:
{
"Id": "3f1b8f0e-2c44-4a1d-9f52-6b3f0a2d1c77",
"Type": "Occupancy",
"CustomerId": "2b7f9e10-4c3a-4b8e-9a11-7d2e5f6c8b90",
"Created": "2026-06-02T04:17:40Z",
"Sent": "2026-06-02T04:17:41Z",
"Data": [
{
"FloorSpaceId": "ac52e43d-7c21-4464-ab21-d0ace07a94b1",
"OccupancyStatus": "CurrentlyOccupied",
"Headcount": 2,
"CollectedDate": "2026-06-02T04:17:39.1066221Z",
"OccupancyStatusChangeDate": "2026-06-02T04:17:39.1066221Z",
"PreviousOccupancyStatus": "RecentlyOccupied"
}
]
}
Iterate Data and return 2xx to acknowledge the whole batch. Field names are PascalCase. See Delivery and batching for the full envelope reference.
3. Verify it is working
- Inspect delivery metrics with the
webhook(id:)query. - If your floor is in dev mode, generate traffic on demand with Webhook test events.
4. Harden it
- Add request signing or custom headers.
- Make processing idempotent — delivery is at-least-once.
- Cache reference data and resolve identifiers via GraphQL rather than querying per event.
Troubleshooting
| Symptom | Where to look |
|---|---|
| Registration rejected for a webhook type | The matching API feature is not enabled on any floor. See Feature enablement. |
You are not authorized to access the '...' field | The API key needs WritePublicApi, in addition to your user account's permissions. |
| No deliveries arriving | Check enabled on the webhook, and its delivery metrics via webhook(id:). A webhook that fails continuously is disabled automatically. |
| Deliveries stopped after an outage | The queue holds up to 1,000,000 events and retries for 24 hours. If your receiver is far behind, use resetWebhookConsumer and backfill the gap through GraphQL. |
| Receiver only sees the first event | The body is an envelope object, not an array. Iterate Data. |
| Timeouts under load | Deliveries time out after 10 seconds per attempt. Acknowledge the batch and process asynchronously. |