02 Get your data in /Integrations
Drift
Send Drift conversations and their messages to Dimension Labs with the universal API.
- D DriftTickets, 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 Drift conversation as one conversation: what the site visitor (the contact) wrote and what your team and bots replied. You run it on your side: a small script pulls from the Drift API and posts each message to the universal API.
What you need
Section titled “What you need”- A Drift user who can create and install an app in your Drift organization from dev.drift.com.
- An access token for that app with the
conversation_readscope. - A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Drift
Section titled “1. Get API access in Drift”Drift’s own steps are in Setting up an integration.
- Go to dev.drift.com and click Build Your App under Your Apps.
- On the OAuth & Permissions page, add the
conversation_readscope (scopes). - Under Activate Your App, click Install App to Drift. This reveals a non-expiring access token for your own organization. Copy it.
- Send it as
Authorization: Bearer YOUR_TOKEN.
Tokens are bound to the scopes they were granted with. If you add a scope later, regenerate the token. You don’t need to build the OAuth flow for an app that only reads your own organization.
2. Pull new conversations
Section titled “2. Pull new conversations”Drift has no bulk export of conversation data. Its export guide says to list conversations, then fetch each one’s messages.
List conversations. The list is ordered by most recently updated, so stop once you reach conversations older than your cursor:
curl -H "Authorization: Bearer $DRIFT_TOKEN" \ "https://api.drift.com/conversations/list?limit=50"Drift’s pages give the maximum limit as 100 on one page and 50 on another, so the code uses 50. Each conversation has id, status, contactId, inboxId, createdAt and updatedAt (Unix milliseconds). Follow links.next for the next page.
For each conversation, retrieve its messages with GET https://driftapi.com/conversations/{conversationId}/messages. Pages are always 50 messages. Pass the pagination.next value from the response as next to get the next page. Attachments are fetched separately.
Drift returns 429 with the error type rate_limit when you have used up your calls for the time window (data and errors). Its docs don’t publish the numbers.
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | Drift | Notes |
|---|---|---|
text | message body | Drift allows <b>, <em> and <a> tags and HTML entities in body: the code strips them. Skip messages whose type is private_note: only your team can see those. |
userId | conversation contactId | Drift’s internal id for the contact, on every message of the conversation. Fall back to the sessionId. |
incoming | message author.type | true when author.type is contact. Your team and bots are user. |
sessionId | conversation id | Prefix it, for example drift-3782727146. |
conversationId | conversation id | Same value as sessionId. |
dimensionlabs_timestamp | message createdAt | Convert with toMillis. |
platformJson | conversation status, inboxId; message author.bot | author.bot tells bot replies apart from your team’s. |
Bot messages shown before a visitor’s first message can arrive inside that message’s attributes.preMessages rather than as messages of their own. The code sends messages only.
Personal data. Visitors often type their name, email and phone number into chats: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// drift.mjs. Environment: DRIFT_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 headers = { Authorization: `Bearer ${process.env.DRIFT_TOKEN}` };const ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', '#39': "'", nbsp: ' ' };const plain = (body) => String(body ?? '').replace(/<[^>]+>/g, '').replace(/&(amp|lt|gt|quot|#39|nbsp);/g, (_, e) => ENTITIES[e]);
// Retries Drift'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 }); if (res.ok) return res.json(); if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Drift returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000); }}
// Sends every chat message written at or after `since` (Unix ms). Returns the new `since`.export async function syncDrift({ since, historical = false }) { const changed = (c) => toMillis(c.updatedAt) >= since; const conversations = []; for (let url = 'https://api.drift.com/conversations/list?limit=50'; url; ) { const page = await get(url); const items = page.data ?? []; conversations.push(...items.filter(changed)); url = items.length && items.every(changed) ? page.links?.next : null; // newest first: stop at the cursor } let newest = since; for (const c of conversations) { const id = `drift-${c.id}`; for (let next = null; ; ) { const res = await get(`https://driftapi.com/conversations/${c.id}/messages${next ? `?next=${encodeURIComponent(next)}` : ''}`); const messages = res.data?.messages ?? []; for (const m of messages) { const ts = toMillis(m.createdAt); const text = plain(m.body); if (m.type !== 'chat' || ts < since || !text.trim()) continue; await sendToDimensionLabs({ text, userId: c.contactId ? String(c.contactId) : id, incoming: m.author?.type === 'contact', sessionId: id, conversationId: id, dimensionlabs_timestamp: ts, platformJson: { status: c.status, inboxId: c.inboxId, bot: m.author?.bot ?? false }, }, { historical: historical || ts < Date.now() - HOUR }); newest = Math.max(newest, ts); } next = res.pagination?.next; if (!next || !messages.length) break; } } return newest;}5. Run it
Section titled “5. Run it”Save the code as drift.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncDrift as sync } from './drift.mjs';.
- History first:
node run.mjs 2025-01-01sends every message since that date to/trackhistorical. 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.
Each conversation costs at least one call, so a history load takes a while; the code waits whenever Drift returns 429. You can also upload a file of exported conversations with one row per message (check it with the file checker), then run a backfill.
Drift reference
Section titled “Drift reference”- Setting up an integration
- Authentication and scopes
- How to export conversations from Drift
- List conversations
- Retrieving a conversation’s messages
- Message model
- Conversation model
- Data and errors
Checked against Drift’s documentation on 2026-10-26.