Webhooks
Messaging API - Webhooks
Webhook contract
Agoda sends one new-message notification to the configured HTTPS destination when an eligible message is available for the recipient Channel Manager.
Delivery is at-least-once. The same metadata.uuid and body are reused on a retry. Receivers MUST perform durable deduplication on metadata.uuid. Gaps are possible: sustained delivery failure may leave a notification undelivered, and there is no public webhook history, replay endpoint, or dead-letter access.
Operational delivery settings are confirmed at certification. They are not part of this contract. Receivers must tolerate redelivery at any operational pacing Agoda applies.
Agoda configures the destination during certification. There is no public API for webhook registration or destination changes.
Callback authentication. Callbacks use OAuth 2.0 client credentials. Agoda obtains a token from the Channel Manager token endpoint and sends Authorization: Bearer on the callback POST, including retries.
Transport
| Item | Requirement |
|---|---|
| Method | HTTPS POST. |
| Header | Content-Type: application/json. Authorization: Bearer from OAuth 2.0 client credentials. |
| Redirects | Not followed. |
| Receiver URL | Must use a publicly reachable hostname with a valid TLS certificate. Loopback, link-local, metadata-service, private-network, and non-public destinations are rejected. |
| Callback credentials | OAuth 2.0 client credentials. Agoda obtains a token from the Channel Manager token endpoint and sends Authorization: Bearer on the callback POST, including retries. |
HTTPS and TLS protect transport. Restrict your receiver to the required public HTTPS shape, validate the payload, and maintain durable deduplication.
The webhook body has no data wrapper. It is {metadata, payload} only. It is not the four-field REST envelope.
Treat each notification as a hint. Confirm against GET /properties/{property_id}/conversations/{conversation_id} before acting or showing content to hotel staff. Notification order is not guaranteed, including within one recipient and across retries. message_id from a read is the ordering authority.
Payload schema and example
{
"metadata": {
"uuid": "2ed6657d-e927-568b-95e1-2665a8aea6a2",
"type": "MESSAGING_API_NEW_MESSAGE",
"payload_version": "1.0"
},
"payload": {
"message_id": "918273644",
"message_type": "free_text",
"timestamp": "2026-07-23T07:15:30.123Z",
"content": "Can we check in early?",
"attributes": {},
"attachment_ids": [],
"sender": {
"participant_type": "guest"
},
"conversation": {
"property_id": "1042",
"conversation_id": "418092345",
"conversation_reference": "9384756102",
"conversation_type": "reservation"
}
}
}The webhook payload has no status, no read_by, and no reply_to. Read state is obtained from a confirming GET via the participant unread counter. Metadata uses snake_case payload_version. payload.attachment_ids is always present ([] when none). The body has no file bytes.
| Field | Type | Required | Meaning |
|---|---|---|---|
metadata.uuid | string (UUID) | Yes | Stable recipient-specific UUID. Deduplicate durably on this exact value. Unchanged when Agoda retries the same notification. |
metadata.type | string | Yes | MESSAGING_API_NEW_MESSAGE. |
metadata.payload_version | string | Yes | 1.0. snake_case, not payloadVersion. |
payload.message_id | string | Yes | Message ID (positive signed-64 decimal string). |
payload.message_type | string | Yes | Documented example free_text. Ordinary text messages appear as free_text. Other stored types are returned as-is; ignore unknown values. |
payload.timestamp | string (date-time) | Yes | RFC 3339 UTC timestamp. |
payload.content | string | Yes | Full partner-safe message text. |
payload.attributes | object | Yes | Object; empty unless a documented safe attribute applies. |
payload.attachment_ids | array of string | Yes | Attachment IDs on this message. Always present. Empty array when none. Ids only; use download for file bytes. |
payload.sender.participant_type | string | Yes | property, guest, or agoda. |
payload.conversation.property_id | string | Yes | Authorized Agoda property ID for the recipient Channel Manager. |
payload.conversation.conversation_id | string | Yes | Conversation ID (positive signed-64 decimal string). |
payload.conversation.conversation_reference | string | Yes | Agoda booking ID. |
payload.conversation.conversation_type | string | Yes | reservation. |
metadata.payload_version versions the webhook body independently of the REST path. The current value is "1.0". Additive payload fields are compatible. Receivers MUST ignore unknown payload fields.
Receiver requirements
- MUST parse and validate the exact versioned body.
- MUST deduplicate durably by
metadata.uuid. - MUST persist accepted work before returning a 2xx response.
- SHOULD return quickly and process downstream work asynchronously.
- MUST treat any 2xx response as transport acknowledgement only, not as a human-read acknowledgement.
- MUST confirm against a conversation-detail
GETbefore acting on content.
Retry and recovery
Agoda retries the same metadata.uuid and body after a network failure or any non-2xx response. A timeout or crash after your receiver returns 2xx can still result in redelivery, so durable deduplication on metadata.uuid is required.
Sustained delivery failure may leave a notification gap. There is no public webhook history, replay endpoint, or dead-letter access. After downtime, catch up with the REST conversation list and conversation-detail GET. Deduplicate deliveries with metadata.uuid. When reconciling messages, use message_id as ordering and identity.
Retain your own delivery and dedupe state.
Ordering is not guaranteed, including within one recipient and across retries. message_id from a read is the ordering authority. A failed callback for one recipient does not block others. Agoda confirms delivery settings during certification.
Updated 3 days ago

