02 Get your data in /Integrations
LiveChat
Send LiveChat chats and transcripts to Dimension Labs with the universal API, on a schedule, with history and backfill.
- L LiveChatTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in your LiveChat chats, one message at a time: what each customer wrote and what your agents and bots replied. You run it on your side with the universal API, using LiveChat’s Agent Chat Web API.
What you need
Section titled “What you need”- A LiveChat account that can sign in to the Developer Console. A token runs with the permissions of the account that created it, so use an account that can see the chats you want.
- A Personal Access Token with the
chats--all:roscope (orchats--access:rofor only the groups the account belongs to). - A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in LiveChat
Section titled “1. Get API access in LiveChat”LiveChat’s own steps are in Personal access tokens.
- Open the Developer Console and go to Settings > Authorization > Personal Access Tokens.
- Select the scopes. For this guide, select
chats--all:ro. You can’t change scopes after you create the token. - Select Create token + and copy the Personal Access Token, your Account ID and the Base64 encoded token.
Personal Access Tokens use Basic authentication: the username is your Account ID and the password is the token. Send the Base64 encoded value as Authorization: Basic YOUR_BASE64_TOKEN.
2. Pull new chats
Section titled “2. Pull new chats”Use the list_archives method. It returns one entry per thread, with that thread’s events (messages) included.
curl -X POST https://api.livechatinc.com/v3.6/agent/action/list_archives \ -H "Authorization: Basic $LIVECHAT_BASE64_TOKEN" \ -H "Content-Type: application/json" \ -d '{"filters":{"from":"2026-10-13T00:00:00.000000+00:00","event_types":{"values":["message"]}},"sort_order":"asc","limit":100}'filters.fromandfilters.totake RFC 3339 dates with microseconds.sort_orderisasc(oldest first) ordesc(the default).limitgoes up to 100.- Each response may include
next_page_id. Send it aspage_idin the next request, with nofilters,limitorsort_order: LiveChat keeps the filters from the first request. Apage_idexpires after one month. thread.activeistruewhile a chat is still going. The code below skips those threads and picks them up on a later run, once they have ended.- LiveChat applies rate limits and returns a “Too many requests” error when you exceed them. Its docs don’t publish the numbers.
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | LiveChat | Notes |
|---|---|---|
text | thread.events[].text | Only events with type message. |
userId | the user in chats[].users with type customer | LiveChat’s internal customer id, on every message of the chat. Don’t use the customer’s email or name. |
incoming | events[].author_id | true when the author is a user whose type is customer. Agents and bots have type agent. |
sessionId | thread.id | One thread is one conversation. |
conversationId | chats[].id | Groups every thread of the same chat. |
dimensionlabs_timestamp | events[].created_at | Convert with toMillis. |
platformJson | thread.access.group_ids, thread.customer_visit.geolocation.country_code, chats[].properties, thread.properties | Group ids are useful for joins by team or brand. |
Personal data. Customers often type names, emails and order numbers into chats: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// livechat.mjs. Environment: LIVECHAT_BASE64_TOKEN, DL_API_KEY.import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const ENDPOINT = 'https://api.livechatinc.com/v3.6/agent/action/list_archives';const headers = { Authorization: `Basic ${process.env.LIVECHAT_BASE64_TOKEN}`, 'Content-Type': 'application/json' };const rfc3339 = (ms) => new Date(ms).toISOString().replace('Z', '000+00:00'); // microseconds, as LiveChat asks
// Retries LiveChat's "Too many requests" (429) and server errors (5xx) with a growing pause.async function post(body) { for (let attempt = 0; ; attempt++) { const res = await fetch(ENDPOINT, { method: 'POST', headers, body: JSON.stringify(body) }); if (res.ok) return res.json(); if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`LiveChat returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000); }}
// Sends every ended chat thread started at or after `since` (Unix ms). Returns the new `since`.export async function syncLiveChat({ since, historical = false }) { let body = { filters: { from: rfc3339(since), event_types: { values: ['message'] } }, sort_order: 'asc', limit: 100 }; let newest = since; let holdAt = Infinity; // the start of the oldest chat that is still going while (body) { const page = await post(body); for (const chat of page.chats ?? []) { const t = chat.thread; const started = toMillis(t.created_at); if (t.active && started > Date.now() - 24 * HOUR) { holdAt = Math.min(holdAt, started); continue; } // send it once it ends const customer = chat.users.find((u) => u.type === 'customer'); for (const e of t.events ?? []) { if (e.type !== 'message' || !e.text?.trim()) continue; const ts = toMillis(e.created_at); await sendToDimensionLabs({ text: e.text, userId: customer?.id ?? t.id, incoming: e.author_id === customer?.id, sessionId: t.id, conversationId: chat.id, dimensionlabs_timestamp: ts, platformJson: { group_ids: t.access?.group_ids, country: t.customer_visit?.geolocation?.country_code }, }, { historical: historical || ts < Date.now() - HOUR }); } newest = Math.max(newest, started); } body = page.next_page_id ? { page_id: page.next_page_id } : null; } return Math.min(newest, holdAt);}A chat that is still going is skipped, and the cursor stays at its start, so the next run picks it up once it has ended. Later chats that already ended are sent straight away; when the cursor is held, they come round again on the next run and the duplicates are removed automatically. A chat left open for more than a day is sent as it is, so one forgotten chat can’t hold the cursor back.
5. Run it
Section titled “5. Run it”Save the code as livechat.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncLiveChat as sync } from './livechat.mjs';.
- History first:
node run.mjs 2025-01-01sends every chat since that date to/trackhistorical. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends ended chats to
/track, and they are enriched automatically every day. Because a chat is sent once it ends, the first messages of a chat that ran for more than an hour go to/trackhistorical, and need a backfill. - Re-runs are safe: duplicates are removed automatically.
You can also upload a file of exported chats and check it first with the file checker, then run a backfill.
LiveChat reference
Section titled “LiveChat reference”Checked against LiveChat’s documentation on 2026-10-13.