Conversations Export API - Sync Message History on a Schedule
Pull your ChatMaxima conversation history as flat message rows with the export API, using date windows and cursor pagination for a reliable daily sync.
Overview
The Conversations Export API returns your team's conversation history as flat message rows. Each row carries the contact's name and mobile number along with the message itself, plus the conversation's current status, department, labels, and the agent who replied, so you can rebuild full conversations in your own database, CRM, or data warehouse.
This is a pull endpoint. Your system calls it on a schedule (for example, a nightly job) and pages through everything inside a date window. It is the simplest way to keep an external copy of your conversations in sync.
A typical use case is a daily export into a warehouse or CRM: every night your job pulls the previous day's messages and loads them into your own tables for reporting or analytics.
Endpoint
GET https://chatmaxima.com/api/v2/conversations/export/
Authentication
Send your API key as the raw value of the Authorization header. You can find the key in the dashboard under Developer settings.
curl -G https://chatmaxima.com/api/v2/conversations/export/ \
-H "Authorization: YOUR_API_KEY" \
--data-urlencode "cursor=0" \
--data-urlencode "limit=500"
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor | integer | 0 | The last message_id you received. Pass meta.next_cursor from the previous response to fetch the next page. Paging is keyed on the message id, so you never miss or duplicate a row. |
limit | integer | 500 | Number of rows per page. Minimum 1, maximum 1000. |
from | string | current UTC day start | Lower bound on message time. Accepts ISO 8601 (2026-07-01T00:00:00) or YYYY-MM-DD HH:MM:SS. |
to | string | current UTC day end | Upper bound on message time. Same format as from. |
Note: Use the
Tseparated ISO format (2026-07-01T00:00:00) when building URLs, since it has no space to encode. The server accepts both forms.
Date window rules
Every export request runs against a bounded date window:
- No dates given - the window defaults to the current UTC day, from
00:00:00to23:59:59. The applied window is echoed back inmeta.fromandmeta.to. - Only
fromgiven -tois automatically set to 3 days afterfrom. - Only
togiven -fromis automatically set to 3 days beforeto. - Maximum span is 3 days - a wider window is rejected.
Requests that break the rules return HTTP 400 with an error code:
| Error code | Meaning |
|---|---|
INVALID_DATE_FORMAT | from or to is not a parseable datetime. |
INVALID_DATE_RANGE | to is earlier than from. |
DATE_RANGE_TOO_LARGE | The window between from and to exceeds 3 days. |
To export more than 3 days of history, walk the range in consecutive windows (see the backfill example below).
Sample response
{
"status": "success",
"code": 200,
"message": "Conversation export",
"data": [
{
"message_id": 340117,
"conversation_alias": "fyz2ul5qki_923001234567",
"lead_id": 366246,
"contact_name": "Ahmed Raza",
"contact_mobile": "923001234567",
"contact_email": "",
"direction": "inbound",
"message_type": "incoming",
"message_text": "Where is my order?",
"media_url": null,
"agent_name": null,
"message_datetime": "2026-07-08 10:12:48.7011",
"status": "Open",
"department": "Customer Service",
"labels": "Delivery,Priority"
},
{
"message_id": 340118,
"conversation_alias": "fyz2ul5qki_923001234567",
"lead_id": 366246,
"contact_name": "Ahmed Raza",
"contact_mobile": "923001234567",
"contact_email": "",
"direction": "outbound",
"message_type": "bot",
"message_text": "Your order is out for delivery and arrives today.",
"media_url": null,
"agent_name": null,
"message_datetime": "2026-07-08 10:12:49.1233",
"status": "Open",
"department": "Customer Service",
"labels": "Delivery,Priority"
},
{
"message_id": 340119,
"conversation_alias": "fyz2ul5qki_923001234567",
"lead_id": 366246,
"contact_name": "Ahmed Raza",
"contact_mobile": "923001234567",
"contact_email": "",
"direction": "outbound",
"message_type": "outgoing",
"message_text": "Apologies for the delay, it is on the way now.",
"media_url": null,
"agent_name": "Sana",
"message_datetime": "2026-07-08 10:15:02.8890",
"status": "Resolved",
"department": "Customer Service",
"labels": "Delivery,Priority"
}
],
"meta": {
"count": 3,
"limit": 500,
"next_cursor": "340119",
"has_more": false,
"from": "2026-07-08 00:00:00",
"to": "2026-07-08 23:59:59"
}
}
Response fields
Each object in data is a single message.
| Field | Type | Description |
|---|---|---|
message_id | integer | Stable unique id for the message. Also the cursor key. Use it to deduplicate on your side. |
conversation_alias | string | Groups messages that belong to the same conversation or thread. |
lead_id | integer | The contact's id in ChatMaxima. |
contact_name | string | The contact's display name. |
contact_mobile | string | The contact's mobile number. |
contact_email | string | The contact's email, when available. |
direction | string | inbound for messages from the customer, outbound for replies from a bot or agent. |
message_type | string | Raw type such as incoming, outgoing, bot, or broadcast. |
message_text | string | The message body. |
media_url | string | URL of the attached media, when the message carries an image, audio, or document. null for text-only messages. |
agent_name | string | The agent who sent the message. null for bot or automated messages. |
message_datetime | string | When the message was sent, with microsecond precision. |
status | string | The conversation's current status, such as Open, Pending, or Resolved. |
department | string | The department the conversation is currently assigned to. |
labels | string | Comma-separated list of labels on the conversation. null when no labels are set. |
Note:
status,department, andlabelsreflect the conversation's current state, not the state at the time the message was sent. Every message of the same conversation carries the same values.
The meta object describes the page, the cursor, and the applied date window.
| Field | Type | Description |
|---|---|---|
count | integer | Number of rows in this page. |
limit | integer | The limit that was applied. |
next_cursor | string | Pass this as cursor on the next call. null when there is nothing more to fetch. |
has_more | boolean | true while more pages are available. Keep paging while this is true. |
from | string | The lower bound that was applied, including defaults. |
to | string | The upper bound that was applied, including defaults. |
Daily incremental pull
The recommended pattern pulls one full day with an explicit window and pages through it with the cursor. Cursor paging is keyed on message_id, so re-running a window never creates gaps or duplicates.
- Set
fromandtoto the day you want to export. - Call with
cursor=0, read the rows fromdata, and store them. - If
meta.has_moreistrue, call again withcursorset tometa.next_cursorand repeat. - When
meta.has_moreisfalse, the day is complete.
# Pull one day, first page
curl -G https://chatmaxima.com/api/v2/conversations/export/ \
-H "Authorization: YOUR_API_KEY" \
--data-urlencode "from=2026-07-07T00:00:00" \
--data-urlencode "to=2026-07-07T23:59:59" \
--data-urlencode "cursor=0" \
--data-urlencode "limit=500"
# Next page of the same day, continue from next_cursor
curl -G https://chatmaxima.com/api/v2/conversations/export/ \
-H "Authorization: YOUR_API_KEY" \
--data-urlencode "from=2026-07-07T00:00:00" \
--data-urlencode "to=2026-07-07T23:59:59" \
--data-urlencode "cursor=340119" \
--data-urlencode "limit=500"
If you call without any dates, you get the current UTC day, which is handy for intraday syncs of today's traffic.
Backfill older history
To backfill more than 3 days, walk the history in consecutive windows of up to 3 days each, paging every window to completion before moving to the next:
# Window 1
from=2026-07-01T00:00:00 to=2026-07-03T23:59:59
# Window 2
from=2026-07-04T00:00:00 to=2026-07-06T23:59:59
# ... and so on until you reach today
Best practices
- Page until
has_moreis false - one call returns at mostlimitrows, so always loop within a window. - Deduplicate on
message_id- safe to re-run a job without creating duplicates. - Use explicit
from/toin scheduled jobs - the no-date default always means "today in UTC", so a nightly job that runs after midnight should pass the previous day explicitly. - Keep windows at 3 days or less - wider ranges return
DATE_RANGE_TOO_LARGE; split backfills into consecutive windows. - Keep your key server side - never expose the API key in a browser or mobile app.