02 Get your data in /Integrations
Dixa
Send Dixa conversations and their messages to Dimension Labs with the universal API.
- D DixaTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in each Dixa conversation as one conversation: the customer’s messages and your agents’ and automations’ replies, across email, chat, messaging and social channels. You run it on your side: a small script pulls from the Dixa Exports API and posts each message to the universal API.
What you need
Section titled “What you need”- A Dixa administrator: only admins can create API tokens. Agents can’t.
- A Dixa API token.
- A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Dixa
Section titled “1. Get API access in Dixa”- In Dixa, go to Settings, scroll to the Manage menu and open Integrations.
- At the top of the page, open the API Tokens tab and click Add API token.
- Name the token and choose Dixa API as the API version.
- Add the permissions the token needs, then click Save changes.
- Copy the token (create an API token).
Send it in the header as Authorization: bearer <API token>. Basic auth with the user bearer and the token as the password also works (Exports API guide).
2. Pull new messages
Section titled “2. Pull new messages”The Exports API has two endpoints on https://exports.dixa.io: GET /v1/message_export for messages and GET /v1/conversation_export for conversations. Pull messages for a time window:
curl -H "Authorization: bearer $DIXA_API_TOKEN" \ "https://exports.dixa.io/v1/message_export?created_after=2026-10-01&created_before=2026-10-15"Times are Unix milliseconds or yyyy-mm-dd. There is no paging: each response is the whole window as one JSON array, and a window can span at most 31 days. A request needs at least one _after and one _before parameter, or a list of conversation ids.
The message export doesn’t carry the customer’s id or the conversation’s tags. Get those from GET /v1/conversation_export?csids=123,456, which takes a comma-separated list of conversation ids and returns requester_id, tags, queue_name, status and more.
Dixa’s reference describes created_after and created_before on the message export as conversation creation dates. To catch replies that arrive after a conversation starts, the code reads back 7 days on every run (LOOKBACK_DAYS) and sends only messages newer than the cursor. Raise it if your conversations stay open longer.
Rate limits, per Dixa, are per token: the message export allows 3 requests a minute and the conversation export 10 a minute. Going over returns 429 (Exports API guide).
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | Dixa | Notes |
|---|---|---|
text | message text | Skip messages with empty text: phone and voicemail messages have none. The message export has no internal notes. |
userId | conversation requester_id | Dixa’s internal id for the end user, on both their messages and your replies. Fall back to the sessionId. |
incoming | message direction | inbound is from the customer, so incoming is true. outbound is from your side. |
sessionId | message csid | The conversation id. Prefix it, for example dixa-123. |
conversationId | message csid | Same value as sessionId. |
dimensionlabs_timestamp | message created_at | Already Unix milliseconds. Messages with the same time in one conversation get 1,000 ms added each. |
platformJson | message initial_channel, is_automated_message; conversation queue_name, status, tags | is_automated_message marks widget chat messages that weren’t written by an agent. |
Personal data. Don’t send author_name, author_email, from, to, the phone number fields or requester_email. Leave out the conversation’s conversation_wrapup_notes: they are internal. Messages can hold addresses, phone numbers and order details: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// dixa.mjs. Environment: DIXA_API_TOKEN, DL_API_KEY.import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const DAY = 24 * HOUR;const LOOKBACK_DAYS = 7;const WINDOW = 30 * DAY; // Dixa allows at most 31 days per request.const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const base = 'https://exports.dixa.io/v1';const auth = `bearer ${process.env.DIXA_API_TOKEN}`;
// Retries Dixa's rate limits (429) and server errors (5xx), honoring Retry-After.async function get(url) { for (let attempt = 0; ; attempt++) { const res = await fetch(url, { headers: { Authorization: auth } }); if (res.ok) return res.json(); if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Dixa returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 20 * (attempt + 1)) * 1000); }}
// Conversations by id, 100 per request, at most 10 requests a minute.async function conversations(ids) { const byId = new Map(); for (let i = 0; i < ids.length; i += 100) { for (const c of await get(`${base}/conversation_export?csids=${ids.slice(i, i + 100).join(',')}`)) byId.set(c.id, c); await sleep(6000); } return byId;}
// Sends every message written at or after `since` (Unix ms). Returns the new `since`.export async function syncDixa({ since, historical = false }) { let newest = since; const now = Date.now(); for (let start = since - LOOKBACK_DAYS * DAY; start < now; start += WINDOW) { const end = Math.min(start + WINDOW, now); const messages = (await get(`${base}/message_export?created_after=${start}&created_before=${end}`)) .filter((m) => toMillis(m.created_at) >= since && m.text?.trim()) .sort((a, b) => a.csid - b.csid || a.created_at - b.created_at); await sleep(20000); // the message export allows 3 requests a minute const convs = await conversations([...new Set(messages.map((m) => m.csid))]); let last = { csid: null, ts: 0 }; for (const m of messages) { let ts = toMillis(m.created_at); if (m.csid === last.csid && ts <= last.ts) ts = last.ts + 1000; last = { csid: m.csid, ts }; const c = convs.get(m.csid); await sendToDimensionLabs({ text: m.text, userId: c?.requester_id ?? `dixa-${m.csid}`, incoming: m.direction === 'inbound', sessionId: `dixa-${m.csid}`, conversationId: `dixa-${m.csid}`, dimensionlabs_timestamp: ts, platformJson: { channel: m.initial_channel, automated: m.is_automated_message, queue: c?.queue_name, status: c?.status, tags: c?.tags }, }, { historical: historical || ts < Date.now() - HOUR }); newest = Math.max(newest, toMillis(m.created_at)); } } return newest;}5. Run it
Section titled “5. Run it”Save the code as dixa.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncDixa as sync } from './dixa.mjs';.
- History first:
node run.mjs 2025-01-01sends every message since that date to/trackhistorical, one 30-day window at a time. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends new messages to
/track, and they are enriched automatically every day. A message that is more than an hour old when the job sees it goes to/trackhistorical, and needs a backfill. - Re-runs are safe: duplicates are removed automatically, so the 7-day lookback doesn’t create copies.
The message export allows only 3 requests a minute, so the code waits 20 seconds after each window, and 6 seconds after each batch of 100 conversations. A long history takes a while. You can also upload a file of conversations from Dixa’s CSV export (check it with the file checker), then run a backfill.
Dixa reference
Section titled “Dixa reference”- Create an API token
- Export conversations and messages via API
- Exports API reference
- API standards and rules
Checked against Dixa’s documentation on 2026-10-26.