Retrieve a conversation
GET /
Returns a conversation with its messages, oldest first.
Scope: inbox:read
Access: The key's member must be able to use the campaign's email inbox in DialEcho (its message and email permissions) and see the contact; other conversations are left out.
messages are the thread's stored inbound and outbound emails (drafts are not included), ordered by created_at (then id), oldest first, up to limit at a time. When has_more is true, ask for the next page with messages_cursor set to next_messages_cursor. The first page fixes the window, so a message that arrives while you page waits for your next read. Every page returns the conversation again.
When the thread was opened by an email blast or a real-estate offer that is not itself a stored message, that first email is returned separately as origin_message (is_synthetic: true), so you can see what the contact answered. Its id is not a UUID (blast-job-<uuid> or offer-send-<uuid>) and it cannot be replied to. origin_status says whether there is one: available; not_applicable when the thread has no such origin or the first email is already in messages; or unavailable when it could not be loaded (the rest of the response is still complete).
The conversation must be one the key's member can see, otherwise 404.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
conversation_id | UUID | Yes | The conversation. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Messages per page. 1 to 100. Default: 100. |
messages_cursor | string | No | The next_messages_cursor of the previous page of this conversation, unchanged. Up to 4,096 characters. |
Response
Status: 200 OK
| Name | Type | Description |
|---|---|---|
conversation | object | The conversation. |
conversation. | UUID | The conversation. |
conversation. | UUID | The conversation's campaign. |
conversation. | string | Which inbox it is in: a contact campaign's (contact) or a real-estate offer campaign's (realestate). One of contact, realestate. |
conversation. | UUID | null | The CRM contact, or null when it is not linked to one. |
conversation. | string | null | The thread subject. |
conversation. | string | null | The other party's address, following the member's email visibility. |
conversation. | string | The conversation status shown in the inbox. Archiving sets closed and restoring sets open; a suppressed conversation stays suppressed. One of open, needs_review, booked, not_interested, closed, suppressed. |
conversation. | string | Where the AI email agent is with the thread, independent of status. Archiving sets disabled. One of pending, queued, drafted, sent, auto_replied, rejected, needs_review, disabled. |
conversation. | timestamp | When the conversation last changed, read and archive changes included (RFC 3339, UTC). |
conversation. | timestamp | null | Time of its latest message. |
conversation. | timestamp | null | Time of its latest inbound message. |
conversation. | boolean | true when it was read at or after its latest inbound message. A conversation without inbound messages is read. |
conversation. | boolean | true while it is archived. |
conversation. | object | The two senders a reply can use (its send_as), as they are now. A preview, not a reservation: the sender is checked again when you reply. |
conversation. | object | The campaign mailbox the thread came from: the mailbox or account that sent the email the contact answered. |
conversation. | string | null | The sender address; null when none is set up. |
conversation. | boolean | Whether a reply can be sent from it now. |
conversation. | string | Only present when available is false: not_configured (no usable sender is set up) or reconnect_required (the account or mailbox must be reconnected in DialEcho). One of not_configured, reconnect_required. |
conversation. | object | The campaign's connected reply address, set in the campaign's email settings. On a real-estate conversation it is available only when the thread is already on that connected account. |
conversation. | string | null | The sender address; null when none is set up. |
conversation. | boolean | Whether a reply can be sent from it now. |
conversation. | string | Only present when available is false: not_configured (no usable sender is set up) or reconnect_required (the account or mailbox must be reconnected in DialEcho). One of not_configured, reconnect_required. |
messages | array of objects | This page of messages, oldest first. |
messages. | UUID | The message. Pass an inbound message's id as in_reply_to_message_id to answer it. |
messages. | UUID | Its conversation. |
messages. | UUID | The conversation's campaign. |
messages. | string | The conversation's inbox. One of contact, realestate. |
messages. | string | inbound from the contact, outbound to them. One of inbound, outbound. |
messages. | string | null | human_reply: a person wrote back. auto_reply: an auto-responder, such as an out-of-office. manual_reply: sent by your team or through this API. ai_reply: sent by the AI email agent. null when the message was never classified, as with older messages and origin_message. One of human_reply, auto_reply, manual_reply, ai_reply. |
messages. | string | null | The sender, following the member's email visibility. |
messages. | array of strings | The recipients. Addresses the member's role hides are left out. |
messages. | array of strings | The CC recipients, the same way. |
messages. | string | null | The subject. |
messages. | string | null | The plain-text body. Restricted when the member's role masks messages. |
messages. | string | null | The HTML body exactly as stored, or null (also when the role masks messages). Sanitize it before you display it and never execute it. |
messages. | array of objects | What was attached; there are no download links. Empty when the role masks messages. A detail that is not known is null. |
messages. | string | null | The file name. |
messages. | string | null | The MIME type, for example application/pdf. |
messages. | integer | null | The size in bytes. |
messages. | timestamp | null | When DialEcho stored the message (RFC 3339, UTC). The inbound feed is ordered by it. |
messages. | timestamp | null | When the mail provider received an inbound message. |
messages. | timestamp | null | When an outbound message was sent. |
messages. | boolean | true only for a conversation's origin_message, which is not a stored message. |
messages. | object | null | Set on replies sent through this API; null on every other message. |
messages. | UUID | null | The API key that sent it. |
messages. | UUID | null | The key's team member. |
messages. | boolean | Always true: sent by an integration, not typed by a person. |
messages. | UUID | null | The inbound message it answered. |
origin_message | object | null | The email that opened the thread when it is not a stored message, with the same attributes as a message and is_synthetic: true; otherwise null. |
origin_status | string | Whether there is an origin_message. One of available, not_applicable, unavailable. |
next_messages_cursor | string | null | Pass it back as messages_cursor for the next page of messages; null when there is none. |
has_more | boolean | true when more messages follow. |
Example request
curl
bash
curl "https://development.agencydevworks.ai/v1/inbox/conversations/44444444-0000-4000-8000-000000000001?limit=100" \
-H "Authorization: Bearer dei_your_key"JavaScript
js
const response = await fetch("https://development.agencydevworks.ai/v1/inbox/conversations/44444444-0000-4000-8000-000000000001?limit=100", {
headers: {
"Authorization": "Bearer dei_your_key",
},
});
const data = await response.json();Example response
Status: 200 OK
json
{
"conversation": {
"id": "44444444-0000-4000-8000-000000000001",
"campaign_id": "cccccccc-0000-4000-8000-000000000001",
"domain": "contact",
"contact_id": "bbbbbbbb-0000-4000-8000-000000000001",
"subject": "Your solar quote",
"participant_email": "pat@example.com",
"status": "open",
"ai_status": "needs_review",
"updated_at": "2026-11-02T16:20:02Z",
"last_message_at": "2026-11-02T16:20:00Z",
"last_inbound_at": "2026-11-02T16:20:00Z",
"read": false,
"archived": false,
"reply_identities": {
"campaign_mailbox": { "email": "jordan@outreach.example.com", "available": true },
"reply_address": { "email": "jordan@example.com", "available": true }
}
},
"messages": [
{
"id": "66666666-0000-4000-8000-000000000001",
"conversation_id": "44444444-0000-4000-8000-000000000001",
"campaign_id": "cccccccc-0000-4000-8000-000000000001",
"domain": "contact",
"direction": "outbound",
"kind": "ai_reply",
"from_email": "jordan@outreach.example.com",
"to_emails": ["pat@example.com"],
"cc_emails": [],
"subject": "Re: Your solar quote",
"text": "Happy to help. Which day works for a call this week?",
"html": "<p>Happy to help. Which day works for a call this week?</p>",
"attachments": [],
"created_at": "2026-11-02T15:02:11Z",
"received_at": null,
"sent_at": "2026-11-02T15:02:10Z",
"is_synthetic": false,
"external_reply": null
},
{
"id": "66666666-0000-4000-8000-000000000002",
"conversation_id": "44444444-0000-4000-8000-000000000001",
"campaign_id": "cccccccc-0000-4000-8000-000000000001",
"domain": "contact",
"direction": "inbound",
"kind": "human_reply",
"from_email": "pat@example.com",
"to_emails": ["jordan@outreach.example.com"],
"cc_emails": [],
"subject": "Re: Your solar quote",
"text": "Is Thursday afternoon available?",
"html": "<p>Is Thursday afternoon available?</p>",
"attachments": [{ "filename": "electric-bill.pdf", "content_type": "application/pdf", "size_bytes": 48213 }],
"created_at": "2026-11-02T16:20:02Z",
"received_at": "2026-11-02T16:20:00Z",
"sent_at": null,
"is_synthetic": false,
"external_reply": null
}
],
"origin_message": null,
"origin_status": "not_applicable",
"next_messages_cursor": null,
"has_more": false
}Errors
| HTTP | Code | When |
|---|---|---|
| 403 | insufficient_scope | The key does not have inbox:read. |
| 404 | not_found | The conversation does not exist, was deleted, or the key's member cannot see it. |
| 409 | resync_required | The cursor expired (after 30 days) or the key's or its member's access changed since it was issued. Start again without a cursor. |
| 422 | invalid_cursor | messages_cursor was not issued to this key for this conversation, or was changed. |
| 422 | invalid_request | conversation_id is not a UUID, limit is outside 1 to 100, or messages_cursor is over 4,096 characters. |
| 503 | dependency_unavailable | The inbox or the access check is temporarily unavailable. Retry with backoff. |
Every endpoint can also answer 401 invalid_api_key, 429 rate_limited and 503 dependency_unavailable. See Errors in the overview.
Related endpoints
- GET
/· List conversationsv1/ inbox/ conversations - POST
/· Reply to a conversationv1/ inbox/ conversations/ {conversation_id}/ replies - PATCH
/· Update a conversationv1/ inbox/ conversations/ {conversation_id}