API reference
Messaging API - API reference
Operations
All paths are relative to the Base URL in Conventions. Include the existing Supply Connectivity authentication required for your integration. JSON responses follow the success or error envelope unless the endpoint returns 204 or download bytes. Responses use Cache-Control: no-store.
Required response fields are non-null unless explicitly stated as nullable.
| Operation | Method and path | Success |
|---|---|---|
| List conversations | GET /properties/{property_id}/conversations | 200, data.conversations |
| Get reservation conversation | GET /properties/{property_id}/conversations/type/reservation?conversation_reference= | 200, data.conversation |
| Get conversation detail | GET /properties/{property_id}/conversations/{conversation_id} | 200 |
| Send text message | POST /properties/{property_id}/conversations/{conversation_id} | 202 |
| Mark conversation read | PUT /properties/{property_id}/conversations/{conversation_id}/read | 204, no body |
| Upload attachment | POST /properties/{property_id}/conversations/{conversation_id}/attachments | 200, data.attachment_id |
| Download attachment | GET /properties/{property_id}/conversations/{conversation_id}/attachments/{attachment_id} | 200, file bytes |
| Get attachment metadata | GET /properties/{property_id}/conversations/{conversation_id}/attachments/{attachment_id}/file_info | 200 |
List property conversations
Returns eligible reservation conversations for one entitled property. Each conversation row includes its latest eligible message when one exists, or "messages": [] when the conversation is empty. Empty conversations may appear in the list.
Pagination uses page_id only. Clients must not send a limit, page_size, offset, or cursor parameter. Each page contains at most 50 conversations; Agoda may return fewer. Keep requesting the next page until next_page_id is null. A page with fewer than 50 items is not necessarily the last page.
GET /properties/{property_id}/conversations
GET /properties/{property_id}/conversations?page_id={page_id}| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
page_id | Query | string | No | Opaque token from data.next_page_id. Omit for the first page. Do not send limit, page_size, offset, or cursor. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Request body: none.
Success: 200 OK
{
"data": {
"ok": true,
"conversations": [
{
"conversation_id": "418092345",
"conversation_reference": "9384756102",
"conversation_type": "reservation",
"access": "read_write",
"participants": [
{ "type": "property", "unread_messages_count": 1 },
{ "type": "guest", "unread_messages_count": 0 },
{ "type": "agoda", "unread_messages_count": 0 }
],
"messages": [
{
"message_id": "918273644",
"message_type": "free_text",
"content": "Can we check in early?",
"timestamp": "2026-07-23T07:15:30.123Z",
"sender_type": "guest",
"attributes": {},
"attachment_ids": []
}
]
},
{
"conversation_id": "418092346",
"conversation_reference": "9384756103",
"conversation_type": "reservation",
"access": "read_only",
"participants": [
{ "type": "property", "unread_messages_count": 0 },
{ "type": "guest", "unread_messages_count": 0 }
],
"messages": []
}
],
"next_page_id": null
},
"errors": [],
"warnings": [],
"meta": {
"ruid": "dd28b426-70c2-4b16-8f16-0edddac1ac2f"
}
}The first row is a three-party reservation thread. The guest-origin message has not been seen by the property: the property participant has unread_messages_count: 1, the guest has 0, and agoda has 0.
| Field | Type | Required | Meaning |
|---|---|---|---|
data.ok | boolean | Yes | JSON boolean true. |
data.conversations | array | Yes | Conversation rows for this page. Always an array, never null. An empty page is []. |
data.next_page_id | string or null | Yes | Always present. Opaque token for the next page, or null on the last page. |
The list response does not return property-level unread aggregates such as unread_conversations or unread_messages. Unread state appears only per conversation via each participant's unread_messages_count.
A 200 list page is complete for that page. A short page with a non-null next_page_id is complete. Continue until next_page_id is null. Incomplete property coverage, an unvalidated unread count, or a missing required binding is 503 DEPENDENCY_UNAVAILABLE without a partial page. Treat absence of a conversation on a successful page as absence, not as a dropped indeterminate row.
List 404 is not a missing conversation_id (the request has none). After the Channel Manager is entitled to property_id (403 is the not-entitled case), 404 means a required resource in that property scope is unknown, hidden, foreign, or wrong-scope. Treat it as opaque.
Relevant errors: 400 INVALID_REQUEST, 400 INVALID_PAGE_ID, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 404 RESOURCE_NOT_FOUND, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR.
Get reservation conversation
Reads the reservation conversation for an Agoda booking ID.
Not every reservation at a property you are entitled to has a messaging conversation available, and a request for one that does not returns 404 RESOURCE_NOT_FOUND.
When messages are present, the response returns the newest at most 50 eligible messages (descending message_id). data.next_page_id is present and always null. Passing page_id to this endpoint returns 400 INVALID_PAGE_ID. To read messages older than the returned page, use the conversation_id from this response with Get conversation detail. The conversation is wrapped as data.conversation.
GET /properties/{property_id}/conversations/type/reservation?conversation_reference={conversation_reference}| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_reference | Query | string | Yes | Agoda booking ID matching ^[1-9][0-9]{0,14}$. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Request body: none. Do not send page_id. If page_id is present, the response is 400 INVALID_PAGE_ID.
Success: 200 OK
{
"data": {
"ok": true,
"conversation": {
"conversation_id": "418092345",
"conversation_reference": "9384756102",
"conversation_type": "reservation",
"access": "read_write",
"participants": [
{ "type": "property", "unread_messages_count": 0 },
{ "type": "guest", "unread_messages_count": 0 }
],
"messages": []
},
"next_page_id": null
},
"errors": [],
"warnings": [],
"meta": {
"ruid": "dd28b426-70c2-4b16-8f16-0edddac1ac2f"
}
}| Field | Type | Required | Meaning |
|---|---|---|---|
data.ok | boolean | Yes | JSON boolean true. |
data.conversation | object | Yes | The reservation conversation. Always present on 200, never null. |
data.next_page_id | string or null | Yes | Always present. Always null on this endpoint. |
Relevant errors: 400 INVALID_REQUEST, 400 INVALID_PAGE_ID, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 404 RESOURCE_NOT_FOUND, 409 RESERVATION_CONVERSATION_CONFLICT, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR.
409 RESERVATION_CONVERSATION_CONFLICT means multiple or incompatible reservation conversations prevent safe selection or creation. Do not repeat the reservation GET blindly. Contact support with meta.ruid.
Get conversation detail
Returns a supported reservation conversation and a page of eligible messages for one entitled property. The conversation is wrapped as data.conversation.
GET /properties/{property_id}/conversations/{conversation_id}
GET /properties/{property_id}/conversations/{conversation_id}?page_id={page_id}| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_id | Path | string | Yes | Opaque positive signed-64 decimal string. |
page_id | Query | string | No | Opaque token from data.next_page_id. Omit for the first page. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Request body: none.
Success: 200 OK
{
"data": {
"ok": true,
"conversation": {
"conversation_id": "418092345",
"conversation_reference": "9384756102",
"conversation_type": "reservation",
"access": "read_write",
"participants": [
{ "type": "property", "unread_messages_count": 1 },
{ "type": "guest", "unread_messages_count": 0 },
{ "type": "agoda", "unread_messages_count": 0 }
],
"messages": [
{
"message_id": "918273644",
"message_type": "free_text",
"content": "Can we check in early?",
"timestamp": "2026-07-23T07:15:30.123Z",
"sender_type": "guest",
"attributes": {},
"attachment_ids": []
}
]
},
"next_page_id": "eyJvIjoibSIsInAiOiI5MTgyNzM2NDQifQ"
},
"errors": [],
"warnings": [],
"meta": {
"ruid": "dd28b426-70c2-4b16-8f16-0edddac1ac2f"
}
}| Field | Type | Required | Meaning |
|---|---|---|---|
data.ok | boolean | Yes | JSON boolean true. |
data.conversation | object | Yes | The conversation. Always present on 200, never null. |
data.next_page_id | string or null | Yes | Always present. Opaque token for older messages, or null on the last page. |
Relevant errors: 400 INVALID_REQUEST, 400 INVALID_PAGE_ID, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 404 RESOURCE_NOT_FOUND, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR.
Each page contains at most 50 eligible messages in descending message_id order (most recent first). A page may contain fewer, including "messages": []. next_page_id may still be a string. Continue until next_page_id is null. The next page continues with older messages. message_id, not timestamp, is the paging and ordering authority. A missing or incomplete dependency result is 503. An empty eligible window is not a missing dependency.
A message accepted with 202 may be absent from this GET until processing completes. See Eventual consistency.
Send text message
Submits a free-text message to a writable reservation conversation. A successful 202 means Agoda accepted the message at the API acceptance boundary. It does not mean the message was delivered, became externally visible on a subsequent GET, or was read.
The send is asynchronous. See Eventual consistency.
Treat access as the send and upload eligibility signal. Recheck it on every send POST. A previous read_write does not guarantee that a later write will be accepted. If access is read_only, send returns 403 CONVERSATION_READ_ONLY.
POST /properties/{property_id}/conversations/{conversation_id}| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_id | Path | string | Yes | Opaque positive signed-64 decimal string. |
Content-Type | Header | string | Yes | application/json; charset=utf-8. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Request body:
{
"message": {
"content": "We can accommodate an early check-in from 14:00.",
"attachment_ids": ["918273800"]
}
}| Field | Type | Required | Meaning |
|---|---|---|---|
message | object | Yes | Send payload wrapper. Unknown fields on this object are rejected. |
message.content | string | Yes | Non-empty, well-formed text. Must satisfy Message size. Agoda does not normalize or truncate it. |
message.attachment_ids | array of string | No | Opaque attachment IDs from a prior upload on this conversation. Omit or send []. |
Unknown fields on the request object or on message are rejected with 400 INVALID_REQUEST. Do not send a flat { "content": "..." } body.
Content that is not well-formed UTF-16 is rejected with 400 INVALID_REQUEST. Content that fails the size dual rule is rejected with 413 MESSAGE_TOO_LARGE. An unknown attachment id is 404 RESOURCE_NOT_FOUND.
Do not send Idempotency-Key, a client message ID, a request fingerprint, or a replay key. Every POST is a new send attempt.
Success: 202 Accepted
{
"data": {
"ok": true,
"message_id": "918273700",
"guest_has_account": true,
"attachment_ids": ["918273800"]
},
"errors": [],
"warnings": [],
"meta": {
"ruid": "dd28b426-70c2-4b16-8f16-0edddac1ac2f"
}
}| Field | Type | Required | Meaning |
|---|---|---|---|
data.ok | boolean | Yes | JSON boolean true. |
data.message_id | string | Yes | Opaque ID of the accepted message. |
data.guest_has_account | boolean | Yes | true when the guest has an Agoda account. false when they do not, in which case the message reaches the guest by email. |
data.attachment_ids | array of string | Yes | Attachment IDs accepted with this send. Always present. Empty array when none. |
The send 202 body has no data.status and no next_page_id.
guest_has_account appears only in this 202 success body (not on list, get, or mark-read responses).
Relevant errors: 400 INVALID_REQUEST, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 403 CONVERSATION_READ_ONLY, 404 RESOURCE_NOT_FOUND, 413 MESSAGE_TOO_LARGE, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR.
On timeout, connection loss, or 500/503 after the request may have been accepted, treat the result as unknown and do not blindly resend. See Send attempts and Best practices.
Do not treat an immediate GET as a delivery check. See Eventual consistency.
Mark conversation read
Sets the property's unread counter for that conversation to zero. The operation is repeatable. The request has no body.
PUT /properties/{property_id}/conversations/{conversation_id}/read| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_id | Path | string | Yes | Opaque positive signed-64 decimal string. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Request body: none.
Success: 204 No Content. The response has no body. This is the single exception to the JSON envelope. Use the X-Request-Id response header for support correlation.
Relevant errors: 400 INVALID_REQUEST, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 404 RESOURCE_NOT_FOUND, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR. Error responses use the JSON error envelope (data: null, errors, warnings, meta).
Observable effect. The entire observable effect is that the property's unread counter for that conversation becomes zero.
Upload attachment
Uploads a file for a later send on the same conversation. Send the file as multipart/form-data. The file part is named attachment. Put the filename in Content-Disposition filename. Allowed types: JPEG (image/jpeg) and PNG (image/png) only. Maximum size: 10 MiB. Images that contain a QR code are rejected (400 INVALID_REQUEST).
POST /properties/{property_id}/conversations/{conversation_id}/attachments| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_id | Path | string | Yes | Opaque positive signed-64 decimal string. |
attachment | Body (multipart) | file | Yes | File part name. Filename is Content-Disposition filename. Allowed types: JPEG (image/jpeg) and PNG (image/png) only. Maximum 10 MiB. |
Content-Type | Header | string | Yes | multipart/form-data. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Unsupported media type or a missing file is 400 INVALID_REQUEST. A file larger than 10 MiB is 413 MESSAGE_TOO_LARGE.
Treat access as the send and upload eligibility signal. Recheck it on every upload POST. A previous read_write does not guarantee that a later write will be accepted. If access is read_only, upload returns 403 CONVERSATION_READ_ONLY. Do not upload; continue reading if needed.
Success: 200 OK
{
"data": {
"ok": true,
"attachment_id": "918273800"
},
"errors": [],
"warnings": [],
"meta": {
"ruid": "dd28b426-70c2-4b16-8f16-0edddac1ac2f"
}
}| Field | Type | Required | Meaning |
|---|---|---|---|
data.ok | boolean | Yes | JSON boolean true. |
data.attachment_id | string | Yes | Opaque ID to pass in send attachment_ids. |
Relevant errors: 400 INVALID_REQUEST, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 403 CONVERSATION_READ_ONLY, 404 RESOURCE_NOT_FOUND, 413 MESSAGE_TOO_LARGE, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR.
Download attachment
Returns the file as raw bytes. This success is not JSON.
GET /properties/{property_id}/conversations/{conversation_id}/attachments/{attachment_id}| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_id | Path | string | Yes | Opaque positive signed-64 decimal string. |
attachment_id | Path | string | Yes | Opaque attachment ID from upload or a message attachment_ids entry. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Success: 200 OK with the file bytes.
| Header | Meaning |
|---|---|
Content-Type | The file's media type. |
Content-Disposition | attachment; filename= plus the file name. |
X-Content-Type-Options | nosniff. |
X-Request-Id | Correlation id. Present on every response. |
After upload, call Get attachment metadata until it returns 200, then download. 404 means the file is still being processed, or it has expired or been deleted. Files are kept for 28 days. Send may use attachment_id from a 200 upload.
Relevant errors: 400 INVALID_REQUEST, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 404 RESOURCE_NOT_FOUND, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR. Error responses use the JSON error envelope.
Get attachment metadata
Returns public file facts only. No file bytes and no download URL.
GET /properties/{property_id}/conversations/{conversation_id}/attachments/{attachment_id}/file_info| Name | In | Type | Required | Description |
|---|---|---|---|---|
property_id | Path | string | Yes | Authorized Agoda property ID. See Identifiers. |
conversation_id | Path | string | Yes | Opaque positive signed-64 decimal string. |
attachment_id | Path | string | Yes | Opaque attachment ID. |
| Supply Connectivity credentials | Header | string | Yes | Use the existing authentication method for your integration. |
Success: 200 OK
{
"data": {
"ok": true,
"file_name": "photo.jpg",
"file_type": "image/jpeg",
"file_size": 102400
},
"errors": [],
"warnings": [],
"meta": {
"ruid": "dd28b426-70c2-4b16-8f16-0edddac1ac2f"
}
}| Field | Type | Required | Meaning |
|---|---|---|---|
data.ok | boolean | Yes | JSON boolean true. |
data.file_name | string | Yes | File name supplied at upload. |
data.file_type | string | Yes | Media type (image/jpeg or image/png). |
data.file_size | integer | Yes | Size in bytes. |
After upload, call Get attachment metadata until it returns 200, then download. 404 means the file is still being processed, or it has expired or been deleted. Files are kept for 28 days.
Relevant errors: 400 INVALID_REQUEST, 401 AUTHENTICATION_REQUIRED, 403 PROPERTY_ACCESS_DENIED, 404 RESOURCE_NOT_FOUND, 503 DEPENDENCY_UNAVAILABLE, and 500 INTERNAL_ERROR.
Eventual consistency
A message accepted with 202 may not appear in GET until Agoda has finished processing it. Partners MUST NOT treat an immediate GET as a delivery check.
Keep message_id from the 202. Confirm visibility with a later conversation-detail GET, or with a webhook followed by a confirming GET. Notification order is not guaranteed; message_id is the ordering authority.
Updated 3 days ago

