# Dixa

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

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](/api/tracker).

## 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](/api/tracker#your-api-key).

## 1. Get API access in Dixa

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](https://docs.dixa.io/docs/tutorial-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](https://docs.dixa.io/docs/exports-api-guide)).

## 2. Pull new messages

The [Exports API](https://docs.dixa.io/openapi/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:

```bash
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](https://docs.dixa.io/docs/exports-api-guide)).

## 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](/data/pii-redaction).

## 4. Send it

```js
// dixa.mjs. Environment: DIXA_API_TOKEN, DL_API_KEY.


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

Save the code as `dixa.mjs` in a project set up as in [Set up your project](/api/tracker#set-up-your-project), and run it with the [scheduled sync](/api/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](/dimensions/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](/data/upload-a-file) of conversations from Dixa's CSV export (check it with the [file checker](/data/check-a-file)), then run a backfill.

## Dixa reference

- [Create an API token](https://docs.dixa.io/docs/tutorial-create-an-api-token)
- [Export conversations and messages via API](https://docs.dixa.io/docs/exports-api-guide)
- [Exports API reference](https://docs.dixa.io/openapi/exports-api)
- [API standards and rules](https://docs.dixa.io/docs/api-standards-rules)

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