Webhooks
Webhooks let Elivaas push you a notification the moment catalogue data changes — a listing, property, inventory or rates — so you can keep your copy in sync without polling.
Each webhook is a thin notification: it tells you what changed (an entity type and id, plus an affected date window for inventory/rate changes), not the changed values themselves. When you receive one, re-fetch the current state from the read endpoints. This keeps you authoritative against live data and means you don't have to worry about deliveries arriving out of order.
Managing endpoints
Register and manage your webhook endpoints yourself via the API — no email required. Every call is scoped to the partner your API key authenticates as, so you only ever see and change your own endpoints. See the Webhooks API reference for full schemas.
| Action | Request |
|---|---|
| List your endpoints | GET /api/webhooks/endpoints |
| Create an endpoint | POST /api/webhooks/endpoints |
| Get one | GET /api/webhooks/endpoints/{id} |
| Update / rotate secret | PUT /api/webhooks/endpoints/{id} |
| Delete | DELETE /api/webhooks/endpoints/{id} |
| List subscribable event types | GET /api/webhooks/event-types |
A new endpoint is active immediately. Specify eventTypes to subscribe to specific events, or omit
it to receive all of them.
curl -u 'dk_live_xxxxxxxxxxxxxxxxxxxxxxxx:' \
-X POST https://partner-api.elivaas.com/api/webhooks/endpoints \
-H 'Content-Type: application/json' \
-d '{
"url": "https://hooks.example.com/elivaas",
"authType": "HMAC",
"signingSecret": "your-shared-secret",
"eventTypes": ["inventory.changed", "rate.changed"]
}'
The response echoes the endpoint without the secret — secrets are write-only. The
signingSecretConfigured / authSecretConfigured flags tell you whether a secret is set.
On PUT, omit signingSecret/authSecret to keep the stored value, or send a new value to
rotate it. You never need to re-send a secret just to change other fields.
Event types
eventType | entityType | Sent when | Date window |
|---|---|---|---|
listing.updated | listing | A listing's catalogue data changes | — |
property.updated | property | A property's data changes | — |
inventory.changed | inventory | A sync changed availability for a property | fromDate..toDate |
rate.changed | rate | A sync changed a property's rates | fromDate..toDate |
inventory.changed and rate.changed are sent only when data actually changed — inventory and
rates are re-synced on a schedule, and a re-sync that produces identical values sends no webhook. These
events carry the affected date window so you can re-fetch just those dates instead of the whole
calendar.
Payload
Every webhook is an HTTP POST with this JSON body:
{
"eventType": "inventory.changed",
"entityType": "inventory",
"entityId": "prop_8fQ2zK",
"fromDate": "2026-07-01",
"toDate": "2026-07-09",
"occurredAt": "2026-06-18T11:04:22Z",
"dedupKey": "0f3c1e9a-8d52-4f7b-9b1a-2c6f0a5d44e1"
}
| Field | Type | Notes |
|---|---|---|
eventType | string | One of the types above. |
entityType | string | Entity class, e.g. inventory. |
entityId | string | Id of the changed entity. For inventory.changed / rate.changed this is the property id. |
fromDate | string | null | ISO-8601 date, inclusive — first affected date. null for listing.updated / property.updated. |
toDate | string | null | ISO-8601 date, inclusive — last affected date. null for listing.updated / property.updated. |
occurredAt | string | ISO-8601 UTC timestamp of the change. |
dedupKey | string | Unique per event — dedupe on this. |
On inventory.changed / rate.changed, call GET /api/inventory or
GET /api/rates for entityId over fromDate..toDate to pull the new values.
On listing.updated, re-fetch the listing via GET /api/listings.
Delivery & retries
- Delivery is asynchronous and at-least-once — you may occasionally receive the same event twice.
- Respond
2xxas soon as you've safely accepted the event; do the re-fetch afterwards so you don't time out. 4xxis treated as a permanent contract error and is not retried. Fix the endpoint and ask us to replay if needed.5xx, timeouts and connection errors are retried with exponential backoff up to the maximum attempts configured for your endpoint.
Security
You choose how your endpoint is authenticated when you register it:
| Auth | What we send |
|---|---|
| None | — |
| Basic | Authorization: Basic … |
| Bearer | Authorization: Bearer <token> |
| HMAC (recommended) | X-Webhook-Signature: <base64(HMAC-SHA256(body, your secret))> |
With HMAC, recompute the signature over the raw request body with your shared secret and compare it (constant-time) before trusting the payload:
import crypto from 'node:crypto';
function verify(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody) // the raw bytes, before JSON.parse
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader),
);
}
Any static custom headers you register are sent on every call.
Idempotency
Because delivery is at-least-once, store recently-seen dedupKeys and ignore repeats. Processing
should be idempotent anyway — re-fetching current state for the same entity twice is always safe.