Skip to content

02 Get your data in /Integrations

Cognigy

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

  1. C CognigyTickets, 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 Cognigy.AI conversations, message by message: what each end user wrote, what the AI agent answered and what a human agent wrote after a handover. Each Cognigy session becomes one conversation. You run it on your side: a small script pulls from the Cognigy OData Analytics endpoint and posts each message to the universal API.

  • A Cognigy user with the odata global role, and an API key for that user.
  • Your OData domain: https://odata-trial.cognigy.ai on a trial, https://odata-<your-domain>.cognigy.ai on shared SaaS, https://odata-<backend-url> on premises, or https://cognigy-odata-<region>.nicecxone.com through NICE CXone. On dedicated SaaS, ask Cognigy for the URL.
  • Optionally, your project id, to give the job its own rate limit budget.
  • A Dimension Labs API key for a new Universal API source. See Your API key.
  1. Make sure the user has the odata global role (OData authentication). An API key has the same permissions as its user.
  2. Open My Profile: click your user icon in the upper-right corner of the Main page.
  3. In the API Keys section, click +, enter a name and click Confirm.
  4. Click the key’s value to copy it, and store it in a secrets manager. Cognigy shows it only once (My Profile).

Send the key in the apikey header. Cognigy also accepts it as a query parameter, but the header keeps it out of URLs and logs.

Use the Conversations collection of OData endpoint version 2.4 (Cognigy 4.65 and later). It logs every session message from the end user, the AI agent and the human agent, one record per message:

Terminal window
curl -H "apikey: $COGNIGY_API_KEY" \
"https://odata-trial.cognigy.ai/v2.4/Conversations?\$filter=timestamp%20ge%20'2026-10-01T00:00:00.000Z'&\$orderby=timestamp%20asc&\$top=1000"
  • Dates are ISO 8601 in UTC, in single quotes. $filter, $orderby, $top, $skip and $select are supported, and each may appear only once in a URL.
  • High $skip values can time out. Page by time instead (keyset pagination): ask again from the last timestamp you received.
  • Add projectId eq '<project-id>' to the filter to use that project’s own request budget.
  • OData data isn’t updated in real time, so the code re-reads the last hour on every run.

Cognigy’s default request limits per budget: a burst of 50 requests, then 10 requests a second, with at most 4 requests at a time. Going over returns 429 with a Retry-After header in seconds (request limits).

Cognigy deletes analytics data, Conversations included, after a time to live set for your organization (data expiration). History only goes back that far.

Dimension LabsCognigyNotes
textinputTextSkip records where it is empty. Skip source suggestion: those are suggestions nobody sent.
userIdcontactIdThe id of the connecting user. Use it on both sides of the conversation. Fall back to the sessionId.
incomingsourcetrue when source is user. bot (the AI agent) and agent (a human agent) are false.
sessionIdsessionIdPrefix it, for example cognigy-<sessionId>.
conversationIdsessionIdSame value as sessionId. Keeps the conversation together across a handover to a human agent.
dimensionlabs_timestamptimestampConvert with toMillis.
platformJsonchannel, flowName, projectName, endpointName, localeReferenceId, inHandoverConversation, rating, idrating is 1 or -1 when the user rated the session.

If an endpoint has Mask Sensitive Analytics turned on, inputText arrives masked; if Collect Analytics is off, nothing is logged at all. Version 2.4 names the record id id; version 2.3 used _id.

Personal data. Check what your channels put in contactId: if it holds a phone number or email address, use the sessionId instead. End users sometimes type names, emails or order numbers: redact them before you send, or turn on Cognigy’s own Data Redaction for the project. See PII redaction.

// cognigy.mjs. Environment: COGNIGY_ODATA_URL (for example https://odata-trial.cognigy.ai), COGNIGY_API_KEY, COGNIGY_PROJECT_ID (optional), DL_API_KEY.
import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;
const PAGE = 1000;
const OVERLAP = HOUR; // OData isn't real time: re-read the last hour. Duplicates are removed.
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const BASE = `${process.env.COGNIGY_ODATA_URL.replace(/\/$/, '')}/v2.4/Conversations`;
const PROJECT = process.env.COGNIGY_PROJECT_ID;
const iso = (ms) => new Date(ms).toISOString();
const safe = (s) => String(s).replace(/:/g, '-');
// Retries Cognigy'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: { apikey: process.env.COGNIGY_API_KEY } });
if (res.ok) return res.json();
if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Cognigy returned ${res.status}: ${await res.text()}`);
await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000);
}
}
// Sends every message logged at or after `since` (Unix ms). Returns the new `since`.
export async function syncCognigy({ since, historical = false }) {
let newest = since;
let from = Math.max(0, since - OVERLAP);
let skip = 0; // records already read that share the `from` timestamp
for (;;) {
const filter = [`timestamp ge '${iso(from)}'`, PROJECT && `projectId eq '${PROJECT}'`].filter(Boolean).join(' and ');
const rows = (await get(`${BASE}?$filter=${encodeURIComponent(filter)}&$orderby=${encodeURIComponent('timestamp asc,id asc')}&$top=${PAGE}${skip ? `&$skip=${skip}` : ''}`)).value ?? [];
for (const r of rows) {
const ts = toMillis(r.timestamp);
newest = Math.max(newest, ts);
if (r.source === 'suggestion' || !r.inputText?.trim()) continue;
const id = `cognigy-${safe(r.sessionId)}`;
await sendToDimensionLabs({
text: r.inputText,
userId: r.contactId ? safe(r.contactId) : id,
incoming: r.source === 'user',
sessionId: id,
conversationId: id,
dimensionlabs_timestamp: ts,
platformJson: {
channel: r.channel, flow: r.flowName, project: r.projectName, endpoint: r.endpointName, locale_id: r.localeReferenceId,
source: r.source, in_handover: r.inHandoverConversation, rating: r.rating, cognigy_record_id: r.id,
},
}, { historical: historical || ts < Date.now() - HOUR });
}
if (rows.length < PAGE) break;
// Keyset pagination: continue from the last timestamp, skipping the records already read at it.
const lastTs = toMillis(rows[rows.length - 1].timestamp);
const atLast = rows.filter((r) => toMillis(r.timestamp) === lastTs).length;
skip = lastTs === from ? skip + atLast : atLast;
from = lastTs;
}
return newest;
}

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

  • History first: node run.mjs 2026-01-01 sends every message since that date to /trackhistorical, as far back as your data expiration allows. 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 one-hour overlap on each run costs nothing but a few extra calls.

The code sends one request at a time, well inside Cognigy’s limits, and waits whenever Cognigy asks it to. The same OData feed also opens in Excel or Power BI, so you can export it to a file instead, upload the file (check it with the file checker), then run a backfill.

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