Skip to content

02 Get your data in /Integrations

Ada

Send AI agent conversations from Ada to Dimension Labs with the universal API.

  1. A AdaTickets, 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 your Ada AI agent conversations, message by message: what each customer wrote and what the AI agent answered. You run it on your side with the universal API, using Ada’s Data Export API.

  • An Ada dashboard account that can open the API keys page.
  • An Ada API key and your bot handle (the <handle> in https://<handle>.ada.support).
  • A Dimension Labs API key for a new Universal API source. See Your API key.

Ada’s own steps are in Authentication.

  1. In the Ada dashboard, open your API settings:
    • For a generative AI agent: Config > PLATFORM > API keys.
    • For a scripted bot: Settings > Integrations > APIs.
  2. Click Get started (no keys yet) or New API key.
  3. Enter a Name for the key and click Generate key.
  4. Click Copy and store the key in a secrets manager. Ada shows it only once.

Send the key as a bearer token on every request: Authorization: Bearer YOUR_ADA_API_KEY.

Use the Data Export API messages endpoint, filtered by creation time:

Terminal window
curl -H "Authorization: Bearer $ADA_API_KEY" \
"https://YOUR_HANDLE.ada.support/api/v2/export/messages?created_since=2026-10-13T00:00:00Z&created_to=2026-10-13T23:59:59Z"
  • created_since is required and must be an ISO 8601 UTC time ending in Z. If you leave out created_to, Ada returns only the 7 days after created_since.
  • Each response has data (the messages) and meta.next_page_uri. Keep requesting next_page_uri until it is empty.
  • page_size defaults to, and is capped at, 10,000 records.
  • For conversation-level fields (CSAT, language, escalation, topic), call GET /api/v2/export/conversations with updated_since.

Ada’s documented limits for the Data Export API (v2): 10 requests per second per endpoint, 100 per minute and 15,000 per day; a query’s end date can be at most 60 days after its start date; and the API holds the past 12 months of data. A 429 response carries a Retry-After header in seconds.

Dimension LabsAdaNotes
textmessage_data.bodyPresent on text messages. Skip messages without a body (greetings, quick replies, meta events).
userIdchatter_idAda’s id for the chatter. Use it on both the chatter’s and the AI agent’s messages.
incomingsenderAda’s message object docs: sender is a unique id for a chatter, or Ada for the AI agent or scripted bot. incoming is true when sender is not Ada (compare case-insensitively).
sessionIdconversation_id
conversationIdconversation_id
dimensionlabs_timestampdate_createdConvert with toMillis.
platformJsonanswer_title, review, link_was_clicked, _idAdd conversation fields from the conversations endpoint if you need them: csat, language, is_escalated, platform, end_user_id.

Personal data. Chatters sometimes type names, emails or order numbers: redact them before you send. See PII redaction.

// ada.mjs. Environment: ADA_HANDLE, ADA_API_KEY, DL_API_KEY.
import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;
const WINDOW = 60 * 24 * HOUR; // Ada allows at most 60 days per query
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const BASE = `https://${process.env.ADA_HANDLE}.ada.support`;
const iso = (ms) => new Date(ms).toISOString().replace(/\.\d+Z$/, 'Z');
// Retries Ada'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: `Bearer ${process.env.ADA_API_KEY}` } });
if (res.ok) return res.json();
if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Ada returned ${res.status}: ${await res.text()}`);
await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000);
}
}
// Sends every message created at or after `since` (Unix ms). Returns the new `since`.
export async function syncAda({ since, historical = false }) {
let newest = since;
for (let from = since; from < Date.now(); from += WINDOW) {
const to = Math.min(from + WINDOW, Date.now());
let next = `${BASE}/api/v2/export/messages?created_since=${iso(from)}&created_to=${iso(to)}`;
while (next) {
const page = await get(new URL(next, BASE).href); // works whether next_page_uri is a full URL or a path
for (const m of page.data ?? []) {
const text = m.message_data?.body;
if (!text || !text.trim()) continue;
const id = m.conversation_id.replace(/:/g, '-');
const ts = toMillis(m.date_created);
await sendToDimensionLabs({
text,
userId: m.chatter_id || id,
incoming: String(m.sender).toLowerCase() !== 'ada',
sessionId: id,
conversationId: id,
dimensionlabs_timestamp: ts,
platformJson: { answer_title: m.answer_title, review: m.review, ada_message_id: m._id },
}, { historical: historical || ts < Date.now() - HOUR });
newest = Math.max(newest, ts);
}
next = page.meta?.next_page_uri;
}
}
return newest;
}

Save the code as ada.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncAda as sync } from './ada.mjs';.

  • History first: node run.mjs 2025-11-01 sends every message since that date to /trackhistorical, in 60-day windows. Ada’s API holds the past 12 months. 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.

You can also upload a file of exported messages and check it first with the file checker, then run a backfill.

Checked against Ada’s documentation on 2026-10-13.