Use CSV ingestion to send text-based conversations to Heap Conversational Intelligence when a direct integration with your customer support platform isn't available or hasn't been configured.
You must provide two CSV files:
- conversations.csv, with one row per conversation.
- messages.csv, with one row per message.
The files are linked by source_id and conversation_source_id.
Before you begin
Make sure that:
- Both files are UTF-8 encoded.
- The comma (
,) is used as the delimiter. - Both files are delivered together as one batch.
- The delivery method is the one configured for your account, such as a single upload, SFTP drop, or shared Amazon S3 prefix.
- Voice conversations aren't included in this format. Don't set channel to
VOICE. - Identifiers remain stable between uploads.
- Timestamps include a timezone.
If a field from your source system doesn't map clearly to the format, contact your Customer Success Manager before generating the export.
General formatting requirements
| Type | Format | Example |
| Datetime | RFC 3339 with a timezone. UTC is preferred. |
2026-03-12T14:22:05Z or 2026-03-12T09:22:05-05:00
|
| Boolean | Lowercase true or false
|
true |
| Float | Decimal using a period (.) |
0.85 |
| List | JSON array in one field | ["billing","refund"] |
| Object | JSON object in one field | {"region":"EMEA","priority":"P2"} |
| Empty or unknown value | Leave the cell empty |
CSV fields that contain a comma, quotation mark, or newline must be enclosed in double quotation marks, in accordance with RFC 4180. Escape a quotation mark inside a quoted field by doubling it.
For example:
CONV-1001,CHAT,2026-03-12T14:20:00Z,2026-03-12T15:08:00Z,agent-42,Jane Smith,0.75,4,Alex Rivera,Acme,"[""billing"",""refund""]",Timestamps must be complete and monotonic:
- Every message must have a
source_createdvalue. - The earliest message timestamp must be at or after the conversation's
source_createdtimestamp. - Messages belonging to the same conversation must be ordered by
source_created.
File 1: conversations.csv
Create one row for each conversation.
Required columns
| Column | Type | Description |
|---|---|---|
source_id |
String | A unique identifier for the conversation in your source system. Use the same value for re-uploads. Heap uses it to de-duplicate conversations and link messages. |
channel |
Enum | The conversation channel. Use one of: EMAIL, CHAT, SMS, MESSAGING, WEB, MOBILE_SDK, FACEBOOK, INSTAGRAM, TWITTER, WHATSAPP, TIKTOK, or API. |
source_created |
Datetime | The timestamp when the conversation was originally created in your source system. |
Don't use VOICE for CSV ingestion. Voice conversations use a separate ingestion method.
Optional columns
| Column | Type | Description |
|---|---|---|
solve_time |
Datetime | The timestamp when the conversation was solved. Leave empty if the conversation isn't solved or the value is unknown. |
solving_agent_source_id |
String | The stable identifier of the agent who solved the conversation. |
solving_agent_name |
String | The display name of the agent who solved the conversation. |
normalized_csat |
Float | The customer satisfaction score normalized to a range from 0.0 -1.0. For example, a 4/5 rating can be represented as 0.75, and a 5/5 rating as 1.0. |
raw_rating |
String | The rating as it appears in your source system, such as "5", "good", or "Very Satisfied". Provide this together with normalized_csat when possible. |
customer_name |
String | The display name of the customer. Don't include additional personally identifiable information beyond what your source system already exposes. |
brand |
String | The brand associated with the conversation, if your organization operates multiple brands. |
tags |
JSON array of strings | Conversation-level tags, such as ["refund","priority-high"]. |
related_ticket_source_id |
String | The source_id of a related or parent conversation, if applicable. |
Example
source_id,channel,source_created,solve_time,solving_agent_source_id,solving_agent_name,normalized_csat,raw_rating,customer_name,brand,tags,related_ticket_source_id
CONV-1001,CHAT,2026-03-12T14:20:00Z,2026-03-12T15:08:00Z,agent-42,Jane Smith,0.75,4,Alex Rivera,Acme,"[""billing"",""refund""]",
CONV-1002,EMAIL,2026-03-12T15:45:00Z,,,,,,Sam Lee,Acme,"[""shipping""]",File 2: messages.csv
Create one row for each message. Each row must link to a conversation through conversation_source_id.
Messages for the same conversation must be ordered by source_created.
Required columns
| Column | Type | Description |
|---|---|---|
conversation_source_id |
String | Must match a source_id in conversations.csv. |
message_source_id |
String | A unique identifier for the message within the conversation. Heap uses it for de-duplication and updates. |
message_type |
Enum | Use IN for a customer-to-agent message or OUT for an agent-to-customer message. |
message_body |
String | The message text. Preserve newlines using \n inside a quoted field. |
source_created |
Datetime | The timestamp when the message was sent. Messages must be sortable by this field. |
Optional columns
| Column | Type | Description |
|---|---|---|
author_source_id |
String | The stable identifier of the author. Use the agent ID for OUT messages and the customer ID for IN messages. For customer messages, this isn't strictly required and can be empty. |
author_name |
String | The display name of the author. |
author_email |
String | The email address of the author, when available. |
is_author_bot |
Boolean | Set to true when the message was sent by a bot or automation instead of a human agent. The default is false. |
For CSV ingestion, message_type accepts only IN and OUT. Don't use the API values OUT-HUMAN, OUT-BOT, or OUT-AUTO-REPLY. Use is_author_bot to identify messages sent by a bot or automation.
Example
conversation_source_id,message_source_id,message_type,message_body,source_created,author_source_id,author_name,author_email,is_author_bot
CONV-1001,MSG-1,IN,"Hi, I was double-charged for my last order.",2026-03-12T14:20:05Z,cust-9001,Alex Rivera,alex@example.com,false
CONV-1001,MSG-2,OUT,"Hello Alex — I'm sorry to hear that. Let me pull up your order.",2026-03-12T14:20:45Z,agent-42,Jane Smith,jane@acme.com,false
CONV-1001,MSG-3,IN,"Order #A7788.",2026-03-12T14:21:10Z,cust-9001,Alex Rivera,alex@example.com,false
CONV-1001,MSG-4,OUT,"I've issued a refund for the duplicate charge. It will appear in 3–5 business days.",2026-03-12T15:07:50Z,agent-42,Jane Smith,jane@acme.com,false
CONV-1002,MSG-1,IN,"Where is my package?",2026-03-12T15:45:00Z,cust-9002,Sam Lee,sam@example.com,falseDeliver the files
Deliver conversations.csv and messages.csv together as a single batch using the delivery method configured for your account.
Before sending the files, confirm that:
- Every
messages.csvrow has aconversation_source_idthat exists inconversations.csv. - Every
source_idinconversations.csvis unique. - Every (
conversation_source_id,message_source_id) pair inmessages.csvis unique. - All timestamps use RFC 3339 format and include Z or a numeric timezone offset.
- Every message timestamp is at or after the conversation's
source_createdtimestamp. - Messages for each conversation are ordered by
source_created. - Every channel value is supported and isn't
VOICE. - Every
is_author_botvalue is lowercasetrueorfalse. - JSON fields contain valid JSON after CSV parsing.
- Both files are UTF-8 encoded.
Re-upload conversations
You can re-upload a conversation by using the same source_id.
When you re-upload:
- Heap updates the existing conversation.
- New messages are appended.
- Existing messages are updated in place when their
message_source_idmatches an existing message. -
source_idandmessage_source_idmust not be renamed between uploads.