Skip to content

02 Get your data in

Conversation data format

The universal format for conversations, reviews, surveys and tickets, column by column, with what breaks each one.

One row is one message. A conversation is one chat, ticket, call, review or survey response, and its rows share a sessionId. A review is a one-message conversation; a chat or ticket is several rows.

conv-1001cust-17trueMy order never arrived1703240320000chatconv-1001cust-17falseSorry about that. Can you share the order number?1703240325000chatconv-1001cust-17trueDM34137KMX1703240331000chatreview-77rev-77trueGreat staff but the wait was 40 minutes1703278090000google_reviews

text Required. The words to enrich. Must not be empty on any row.

One row is one message. Pick a column to see its rule.
ColumnRequiredTypeExampleNotes
textYestextMy order never arrivedThe words to enrich. Must not be empty on any row.
userIdYestextcust-4471920Who sent the message. Must not be empty: if you don’t have one, copy the sessionId into it (the file checker does this for you). For a bot conversation, use the end user’s id on both their messages and the bot’s (the userId rule). Never an email address, phone number or name: use your tool’s internal id.
incomingYestrue or false, lowercasetruetrue when the customer or end user wrote it; false for your agent, bot or company.
dimensionlabs_timestampYesnumber, Unix time in milliseconds (13 digits)1703240320000When the message was sent.
sessionIdYestext1434868331Groups the rows of one conversation. Enrichment reads all the rows with the same sessionId at once. No colons, and no personal data.
conversationIdRecommendedtextconv-88213407Keeps a multi-participant thread together, for example when a human agent joins a bot chat. No colons.
intentOnly for an intent-based chatbottext in a CSV; an object in JSONLTRACK_ORDERThe intent your bot matched. Leave it out if your bot doesn’t use intents.
any other columnOptionalanythingstore_id, rating, planKept with the conversation as metadata. You map it during upload, and it appears in the structured_metadata table, where the agent can filter and join on it. Leave out emails, phone numbers, names and addresses (PII redaction).

Column names are case-sensitive: userId and sessionId, not userid or SESSIONID. The upload screen’s column mapping lets you point your own column names at these fields, and the converter renames them for you. Uploads accept CSV, TSV and JSONL files of any size: Dimension Labs splits a large file for you.

These are the problems behind most failed uploads. The file checker finds every one.

  • Empty text. Any row with an empty text cell. Remove the row.
  • Booleans in capitals. TRUE and FALSE, or a mix of cases. Use lowercase true and false.
  • Timestamps in seconds. A 10-digit number like 1703240320 is read as a date in 1970. Multiply by 1,000.
  • Excel’s scientific notation. Excel rewrites long numbers as 1.70324E+12 when it saves a CSV, and the digits it drops are gone. Re-export from the source system, or give the checker the original .xlsx.
  • Newlines inside a text field. A line break inside a review or message can split one record into several. Replace line breaks with spaces.
  • Every field in quotes. Some tools write every cell wrapped in quotes, including numbers and empty cells. Write the file with quotes only where needed.
  • Colons in ids. sessionId and conversationId can’t contain :. Use - or _.
  • A byte order mark, or Windows line endings. Save as UTF-8 without a BOM, with Unix (LF) line endings.
  • A header with a backslash or a trailing space. Data Source with a trailing space becomes a different column from Data Source.
  • No header row. The upload needs column names in the first row. Add one in a text editor.
  • Keep messages in the same conversation at least one second (1,000 ms) apart.
  • Don’t use placeholder text such as [No review text] for an empty review. Drop the row instead.
  • Pick one encoding for booleans and dates in your extra columns, and stick to it. Everything you upload is stored as text, so Yes, yes and 1 become three different values.
sessionId,userId,incoming,text,dimensionlabs_timestamp,channel
conv-1001,cust-17,true,My order never arrived,1703240320000,chat
conv-1001,cust-17,false,Sorry about that. Can you share the order number?,1703240325000,chat
conv-1001,cust-17,true,DM34137KMX,1703240331000,chat
review-77,rev-77,true,Great staff but the wait was 40 minutes,1703278090000,google_reviews

The agent’s reply in conv-1001 carries the customer’s id, cust-17. Download it as a template: conversation template. Then check your own file with the file checker.

A JSONL file has one JSON object per line, with no commas between lines and no surrounding brackets. A line has the same shape as a universal API message: intent is an object, and extra fields go in a nested platformJson object. This example file holds two bot conversations: a customer asking how to track a delivery, and a customer whose order number isn’t found and who asks for a person.

{"text":"hey","userId":"7675836189527716","incoming":true,"sessionId":"1434868331","conversationId":"1434868331","dimensionlabs_timestamp":1703240310000,"intent":{"name":"HELLO","confidence":0.96},"platformJson":{"customerTier":"silver","locale":"en-CA"}}
{"text":"Hi! How can I help you?","userId":"7675836189527716","incoming":false,"sessionId":"1434868331","conversationId":"1434868331","dimensionlabs_timestamp":1703240315000,"intent":{"name":"HELLO_RESPONSE","confidence":1},"platformJson":{"customerTier":"silver","locale":"en-CA"}}
{"text":"I want to know where to track my deliveries","userId":"7675836189527716","incoming":true,"sessionId":"1434868331","conversationId":"1434868331","dimensionlabs_timestamp":1703240320000,"intent":{"name":"TRACK_ORDER","confidence":0.91},"platformJson":{"customerTier":"silver","locale":"en-CA"}}
{"text":"DM34137KMX","userId":"1165873486259425","incoming":true,"sessionId":"9950934871","conversationId":"9950934871","dimensionlabs_timestamp":1703278090000,"intent":{"name":"ORDER_NUMBER","confidence":0.88},"platformJson":{"customerTier":"bronze","locale":"en-CA"}}
{"text":"Sorry, I can't find any existing order matching that number.","userId":"1165873486259425","incoming":false,"sessionId":"9950934871","conversationId":"9950934871","dimensionlabs_timestamp":1703278095000,"intent":{"name":"ORDER_NOT_FOUND","confidence":1},"platformJson":{"customerTier":"bronze","locale":"en-CA"}}
{"text":"Operator","userId":"1165873486259425","incoming":true,"sessionId":"9950934871","conversationId":"9950934871","dimensionlabs_timestamp":1703278100000,"intent":{"name":"REQUEST_HUMAN","confidence":0.97},"platformJson":{"customerTier":"bronze","locale":"en-CA"}}
{"text":"Okay, I'm getting someone from my team to help you.","userId":"1165873486259425","incoming":false,"sessionId":"9950934871","conversationId":"9950934871","dimensionlabs_timestamp":1703278105000,"intent":{"name":"REQUEST_HUMAN_RESPONSE","confidence":1},"platformJson":{"customerTier":"bronze","locale":"en-CA"}}

Things to notice:

  • userId is the customer’s id on the bot’s messages too.
  • incoming is a real boolean, true or false, without quotes.
  • Timestamps rise within each conversation and are at least a second apart.
  • customerTier and locale are extra fields in platformJson, kept as metadata the agent can filter on.
  • The timestamp field is dimensionlabs_timestamp. Older examples used other names, such as dimension_timestamp, which don’t work.