Inbox exports and bulk actions
Export one or many threads, and mark read or archive many at once.
#Export a conversation
Downloads one thread as a CSV file, oldest message first. Each row also carries when the contact was added and when and how they opted out, so the file stands on its own as a compliance record. Send format=json to get the same data as JSON.
Parameters
idinteger, pathRequired- The conversation id.
formatenum, querycsv(the default) orjson. Any other value gets the CSV. Send it once; a repeatedformatis a400 invalid_field.
Returns
The CSV is sent as a download named conversation_<contact>_<date>.csv. Its columns are Date (UTC), Time (UTC), Direction, From, To, Message, Status, Delivered At, Segments, Contact Added, Opt-Out At, Opt-Out Source, Opt-Out Reason, Channel and Media URL. Message is the text that went out, opt-out footer included. Opt-Out Reason says in words why the contact is opted out. Channel is SMS, MMS or iMessage, and Media URL is the link to the picture or file the message carried. Dates and times are UTC. A cell that starts like a spreadsheet formula gets a leading ' so it opens as plain text.
With format=json:
data.contactstring- The contact’s name, or their phone when they have no name.
data.phoneE.164 string- The contact’s phone.
data.exported_atISO 8601 string- When the export ran.
data.contact_added_atISO 8601 string | null- When the contact was added to your account.
data.opt_out_atISO 8601 string | null- When the contact opted out, if they did.
data.opt_out_sourcestring | null- How they opted out, such as
sms_stop_keyword. data.opt_out_reasonstring | null- The same, in words, such as
This contact replied STOP and is opted out. data.contact_statusstring | null- The contact’s status, such as
activeoropted_out. data.message_countinteger- How many messages are in
messages. data.messagesobject[]- Oldest first. Each has
date,time,direction,from,to,body,status,delivered_at,segments,channel(SMS,MMSoriMessage) andmedia_url(the picture or file, ornull).
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | The id isn’t a whole number, or format was sent more than once. |
| 403 | CONVERSATION_NOT_ASSIGNED | The key’s user only sees their own contacts, and this thread is assigned to a teammate. |
| 403 | CONVERSATION_NOT_ALLOCATED | The key’s user is limited to some of your numbers, and this thread is on another one. |
| 404 | CONVERSATION_NOT_FOUND | No such conversation on your account. |
| 429 | rate_limited | Too many requests this minute for your account. |
| 500 | EXPORT_READ_FAILED | We couldn’t read the thread. Nothing was downloaded; try again in a minute. |
#Export many conversations
Downloads the conversations you list, or every conversation on your account, as one CSV file. It’s a POST only because a long id list doesn’t fit in a URL; nothing on your account changes. Each call counts toward your account’s 6 bulk exports an hour, shared with your other exports. A call that fails with a 4xx or 5xx doesn’t count. Needs a live key.
Parameters
conversation_idsinteger[]- The conversations to export, up to 50,000. Ids that aren’t whole numbers are dropped, and ids that aren’t on your account are skipped. Required unless
allistrue. allbooleantrueexports every conversation on your account and ignoresconversation_ids. An emptyconversation_idsis not the same asall.fromstring- Optional. A day like
2026-01-31(UTC). Only messages from that day on are exported, and only the conversations that have one. tostring- Optional. A day like
2026-03-31(UTC), included. Ranges laid end to end export every message once, so a history too big for one file can come a part at a time. formatenum, querycsv(the default) orjson. Any other value gets the CSV.
Returns
A download named conversations_export_<date>.csv, one row per message, oldest first. It has the columns of Export a conversation with Contact and Phone in front. When nothing matches, you get the header row alone, not an error.
With format=json:
data.exported_atISO 8601 string- When the export ran.
data.conversation_countinteger- How many conversations are in the file.
data.total_messagesinteger- How many messages across all of them.
data.conversationsobject[]- Each has
id,contact,phone,contact_added_at,opt_out_at,opt_out_source,contact_statusandmessages, shaped as in the single export.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | NO_CONVERSATIONS_SELECTED | No conversation_ids and all isn’t true. |
| 400 | INVALID_CONVERSATION_IDS | None of the ids is a whole number. |
| 400 | INVALID_EXPORT_RANGE | from or to isn’t a day like 2026-01-31, or from is after to. |
| 400 | invalid_field | conversation_ids isn’t a list, all is a list or object, or format was sent more than once. |
| 413 | EXPORT_SELECTION_TOO_LARGE | More than 50,000 ids. limit says how many you may send. Export in smaller batches. |
| 413 | EXPORT_TOO_LARGE | Too many messages for one file. limit is the ceiling. Send a shorter from and to, or pick fewer conversations. |
| 429 | rate_limited | Your account has used its bulk exports for the hour. |
#Mark many conversations read
Clears the unread count on every conversation you list. Ids that aren’t on your account are skipped. Needs the send_sms permission and a live key.
Parameters
conversation_idsinteger[]Required- The conversations to mark read, at least one and up to 50,000. Duplicates are ignored.
Returns
updatedinteger- How many conversations went from unread to read, at the top level. Threads that were already read don’t count, so this can be
0.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | NO_CONVERSATIONS_SELECTED | conversation_ids is missing, empty or not a list. |
| 400 | INVALID_CONVERSATION_IDS | An id isn’t a positive whole number, or there are more than 50,000. |
| 429 | rate_limited | Too many requests this minute for your account. |
| 500 | CONVERSATION_UPDATE_FAILED | The change wasn’t saved. Nothing changed; try again in a minute. |
#Archive many conversations
Archives every conversation you list, as Archive a conversation does for one. Ids that aren’t on your account are skipped. Needs the send_sms permission and a live key.
Parameters
conversation_idsinteger[]Required- The conversations to archive, at least one and up to 50,000. Duplicates are ignored.
Returns
updatedinteger- How many conversations were archived, at the top level. It counts the rows that changed, not the ids you sent.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | NO_CONVERSATIONS_SELECTED | conversation_ids is missing, empty or not a list. |
| 400 | INVALID_CONVERSATION_IDS | An id isn’t a positive whole number, or there are more than 50,000. |
| 429 | rate_limited | Too many requests this minute for your account. |
| 500 | CONVERSATION_READ_FAILED | We couldn’t check your account. Nothing changed; try again. |
| 500 | (none) | The archive didn’t save. The conversations are still in your inbox; try again. |
#Mark all conversations read
Clears the unread count on every conversation in your inbox. Archived conversations are left as they are. A teammate who only sees some conversations marks only those. No body. Needs the send_sms permission and a live key.
Returns
updatedinteger- How many conversations went from unread to read, at the top level.
Errors
| HTTP | Code | When |
|---|---|---|
| 500 | CONVERSATION_UPDATE_FAILED | The change wasn’t saved. Nothing changed; try again in a minute. |