Skip to content

02 Get your data in /Integrations

Dixa

Send Dixa conversations and their messages to Dimension Labs with the universal API.

  1. D DixaTickets, chats, calls, reviews
  2. ⟳ Your jobPull new · map fields · every 15 min
  3. { } Universal APIPOST /track · /trackhistorical
  4. 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.

  • 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. In Dixa, go to Settings, scroll to the Manage menu and open Integrations.
  2. At the top of the page, open the API Tokens tab and click Add API token.
  3. Name the token and choose Dixa API as the API version.
  4. Add the permissions the token needs, then click Save changes.
  5. 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).

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:

Terminal 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).

Dimension LabsDixaNotes
textmessage textSkip messages with empty text: phone and voicemail messages have none. The message export has no internal notes.
userIdconversation requester_idDixa’s internal id for the end user, on both their messages and your replies. Fall back to the sessionId.
incomingmessage directioninbound is from the customer, so incoming is true. outbound is from your side.
sessionIdmessage csidThe conversation id. Prefix it, for example dixa-123.
conversationIdmessage csidSame value as sessionId.
dimensionlabs_timestampmessage created_atAlready Unix milliseconds. Messages with the same time in one conversation get 1,000 ms added each.
platformJsonmessage initial_channel, is_automated_message; conversation queue_name, status, tagsis_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.

// 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;
}

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-01 sends 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.

Checked against Dixa’s documentation on 2026-10-26.