Exports run in the background. You start one, wait for it to finish, then download the file.
Start an export
POST /case/{caseId}/export
For phone data:
source:conversation. This covers messages and chats collected from phones.type(required): the output format, for examplepdf,rsmf,csv,textordoc. The full list is in the reference.sortBy(required):old-to-newornew-to-old.- Limit the export to one phone: set
deviceto the device id (thesourceIdfrom data-status-v2). - Limit it to specific conversations: set
conversationsto a list of conversation ids. You can find them withGET /conversation/search, filtered bycaseanddevices. - Optional:
tags: only messages with these tags. Send tag ids, not names (see Step 4).startDateandendDate: a date window.excludeConversations: conversations to skip.excludeDeviceSources: message sources to skip within a device, such ascall_logorvoicemail.chunkType: split the export byday,week,monthoryear.timezone: an IANA name such asAmerica/New_York, used to format timestamps.fileName.emails: extra addresses to notify when the export finishes.
The call returns the export record. Save its id from data.id.
An empty selection means "the whole case". If you send neither device nor conversations, the export includes every conversation on the case. If you meant a narrow export, double-check your selection before calling.
Your organization needs an active plan to start an export (see Step 2). Starting an export also needs admin access to the case; the case's owner has it.
Wait for it
GET /case/{caseId}/export lists the case's exports. Pass exportId to get just yours; it is still a list, so read the row from data.data. Then read its status:
| Status | Meaning |
|---|---|
not-started |
Queued |
inprogress |
Running. progress, conversationsProcessed and messagesProcessed show how far along it is. |
completed |
Done. file is set. |
failed |
Failed. errorMessage says why. |
cancelled |
Cancelled. |
Poll every 30 to 60 seconds. Large exports can take a long time.
Download the file
When status is completed, take the file id from the export's file.id and call GET /common/file/{fileId}/signed-url. It returns a temporary download url and the time it stops working, expiresAt (up to 24 hours later).
Download promptly, and do not store or share signed URLs. They expire, and anyone holding one can download the file while it is valid.
Retry and clean up
POST /case/{caseId}/export/{exportId}/rerunruns a failed or unfinished export again with the same settings. A completed export cannot be rerun; start a new one instead.DELETE /case/{caseId}/export/{exportId}removes an export.
Export one phone's tagged messages as a PDF:
curl -X POST "https://api.usehearsay.com/case/$CASE_ID/export" \
-H "Authorization: Bearer $TOKEN" \
-H "organization: $ORG_ID" \
-H "Content-Type: application/json" \
-d '{
"source": "conversation",
"type": "pdf",
"sortBy": "old-to-new",
"device": "507f1f77bcf86cd799439012",
"tags": ["507f1f77bcf86cd799439016"],
"timezone": "America/New_York"
}'
Poll until it is done:
curl "https://api.usehearsay.com/case/$CASE_ID/export?exportId=$EXPORT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "organization: $ORG_ID"
{
"data": {
"data": [
{
"id": "507f1f77bcf86cd799439014",
"status": "completed",
"progress": 100,
"fileName": "doe-v-example-export.pdf",
"file": { "id": "507f1f77bcf86cd799439015" },
"errorMessage": null
}
],
"total": 1,
"nextPage": null
},
"statusCode": 200,
"timestamp": "2026-01-15T10:15:00.000Z"
}
Get the download link:
curl "https://api.usehearsay.com/common/file/$FILE_ID/signed-url" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"url": "https://bucket.s3.amazonaws.com/files/report.pdf?X-Amz-Signature=abc123",
"expiresAt": "2026-01-15T10:30:00.000Z"
},
"statusCode": 200,
"timestamp": "2026-01-15T10:15:00.000Z"
}