Skip to content

02 Get your data in /Integrations

NICE CXone

Send digital and voice interactions from NICE CXone to Dimension Labs with the universal API.

  1. NC NICE CXoneTickets, 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 NICE CXone interactions, turn by turn: call transcripts and digital conversations (chat, email, messaging) that Interaction Analytics has analyzed. You run it on your side with the universal API. For digital channels without Interaction Analytics, see Digital only.

  • CXone Interaction Analytics on your tenant (for voice transcripts and the analyzed digital transcripts).
  • An app registered with NICE, which gives you a client_id and client_secret. NICE says registration typically takes 2 to 3 business days.
  • A CXone user for the integration, with an access key ID and secret. The user’s roles and views decide which interactions the API returns.
  • A Dimension Labs API key for a new Universal API source. See Your API key.

NICE’s own steps are in Getting Started.

  1. Submit the Application Registration form described in Getting Started. Choose a back-end app and single tenant. NICE replies with your client_id and client_secret.
  2. In CXone, open the app selector and go to Admin > Employees > Create Employee. Create a user for the integration, with no login authenticator.
  3. Open that user, select the Security tab and click Generate New Access Key. Click SHOW SECRET KEY and store the secret. You can’t view it again.
  4. Get a token: POST https://cxone.niceincontact.com/auth/token with a form body of grant_type=password, username (the access key ID), password (the secret), client_id and client_secret. Tokens expire (expires_in), so request a new one before each run.
  5. Find your API base URL: read tenantId from the id_token, then GET https://cxone.niceincontact.com/.well-known/cxone-configuration?tenantId=YOUR_TENANT_ID. The api_endpoint field is your base URL, for example https://api-na1.niceincontact.com.

Send the token as Authorization: Bearer YOUR_TOKEN.

List analyzed segments since your cursor, then get each segment’s transcript:

Terminal window
curl -H "Authorization: Bearer $CXONE_TOKEN" \
"$CXONE_API/interaction-analytics-gateway/v2/segments/analyzed?dateField=firstAnnotatedAt&beginningDate=2026-10-13T00:00:00.000Z&order=asc&pageSize=100"
curl -H "Authorization: Bearer $CXONE_TOKEN" \
"$CXONE_API/interaction-analytics-gateway/v2/segments/SEGMENT_ID/analyzed-transcript"
  • dateField=firstAnnotatedAt filters by when the segment was first analyzed, which never changes. That makes it a safe cursor. beginningDate and endingDate use yyyy-MM-ddTHH:mm:ss.SSSZ in UTC.
  • mediaType narrows to one channel, for example PhoneCall, Chat or EMail.
  • Each page has links.next. Request it until it is missing.
  • A segment that wasn’t transcribed (too short, one-sided, or the agent isn’t set up for transcription) returns 404 on the transcript endpoint. Skip it.
  • An interaction can have several segments when it is transferred or conferenced. interactionId ties them together.
Dimension LabsNICE CXoneNotes
texttranscriptBlocks[].textOne block per turn.
userIdsegment acdContactIdCXone’s contact id, on every block of the segment.
incomingtranscriptBlocks[].userTypetrue when it is CLIENT, false when AGENT. channelName carries the same value.
sessionIdsegmentIdOne segment is one conversation in Dimension Labs.
conversationIdinteractionIdKeeps transferred segments of one interaction together.
dimensionlabs_timestampsegment startTimeBlocks carry offsets, not clock times. The example adds 1,000 ms per block to startTime to keep the order and the one-second spacing.
platformJsonmediaType, contactNo, resolved, dispositionCode, segmentSummaryPrimaryIntentName, enlightenSentimentIndexScore, channelTypeacdContactId joins to other CXone reports.

Personal data. Call and chat transcripts hold names, account numbers and addresses: redact them before you send (or use CXone’s own redaction, if your tenant has it). See PII redaction.

// nice-cxone.mjs. Environment: CXONE_API (api_endpoint from discovery), CXONE_ACCESS_KEY_ID, CXONE_ACCESS_KEY_SECRET,
// CXONE_CLIENT_ID, CXONE_CLIENT_SECRET, DL_API_KEY.
import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const API = process.env.CXONE_API;
const IA = `${API}/interaction-analytics-gateway/v2/segments`;
let token;
async function newToken() {
const body = new URLSearchParams({
grant_type: 'password', username: process.env.CXONE_ACCESS_KEY_ID, password: process.env.CXONE_ACCESS_KEY_SECRET,
client_id: process.env.CXONE_CLIENT_ID, client_secret: process.env.CXONE_CLIENT_SECRET,
});
const res = await fetch('https://cxone.niceincontact.com/auth/token', { method: 'POST', body });
if (!res.ok) throw new Error(`CXone token request returned ${res.status}: ${await res.text()}`);
token = (await res.json()).access_token;
}
// Gets a fresh token on 401, and retries rate limits (429) and server errors (5xx). Returns null on 404.
async function get(url) {
for (let attempt = 0; ; attempt++) {
if (!token) await newToken();
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (res.ok) return res.json();
if (res.status === 404) return null; // a segment that wasn't transcribed
if (res.status === 401 && attempt === 0) { token = null; continue; }
if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`CXone returned ${res.status}: ${await res.text()}`);
await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000);
}
}
// Sends every segment first analyzed at or after `since` (Unix ms). Returns the new `since`.
export async function syncCxone({ since, historical = false }) {
token = null; // a new token for each run
let next = `${IA}/analyzed?dateField=firstAnnotatedAt&beginningDate=${new Date(since).toISOString()}&order=asc&pageSize=100`;
let newest = since;
while (next) {
const page = await get(next);
for (const s of page?.interactions ?? []) {
const t = await get(`${IA}/${s.segmentId}/analyzed-transcript`);
const start = toMillis(s.startTime);
for (const [i, b] of (t?.transcriptBlocks ?? []).entries()) {
if (!b.text?.trim()) continue;
const ts = start + i * 1000;
await sendToDimensionLabs({
text: b.text,
userId: String(s.acdContactId ?? s.segmentId),
incoming: b.userType === 'CLIENT',
sessionId: s.segmentId.replace(/:/g, '-'),
conversationId: (s.interactionId ?? s.segmentId).replace(/:/g, '-'),
dimensionlabs_timestamp: ts,
platformJson: { mediaType: s.mediaType, channelType: b.channelType, acdContactId: s.acdContactId, resolved: s.resolved },
}, { historical: historical || ts < Date.now() - HOUR });
}
newest = Math.max(newest, toMillis(s.firstAnnotatedAt));
}
next = page?.links?.next ? new URL(page.links.next, API).href : null;
}
return newest;
}

A segment exactly at the cursor comes back on the next run. That’s fine: duplicates are removed automatically.

Without Interaction Analytics, the Digital Engagement API (on /dfo/3.0) gives you the messages of each digital contact (case):

  • GET /dfo/3.0/contacts lists contacts. It filters by status[] (for example closed), channel[] and routingQueueId[], sorts with sorting=updatedAt and sortingType=desc, and pages with scrollToken.
  • GET /dfo/3.0/contacts/{contactNumber}/messages returns the messages. Each has direction (inbound from the customer, outbound from your side), messageContent.payload.text, createdAtWithMilliseconds and authorEndUserIdentity. Text is cut at 3,000 characters; when hasAdditionalMessageContent is true, get the full message with GET /dfo/3.0/messages/{messageId}.

NICE’s documented throttling limits for Digital Engagement include 120,000 get contact requests (including get contact messages) per hour per tenant, and 5,000 contact searches per minute up to 300,000 per hour. The Digital Engagement APIs are only available on UserHub tenants.

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

  • History first: node run.mjs 2025-01-01, with the oldest date your tenant’s data retention keeps, sends every analyzed segment since then to /trackhistorical. Then start a backfill from the Backfill tab.
  • Then every 15 minutes: the scheduled job sends newly analyzed segments to /track, and they are enriched automatically every day. Analysis happens after the contact ends, so turns of a long contact, or of one analyzed late, can be more than an hour old by then: those go to /trackhistorical, and need a backfill.
  • Re-runs are safe: duplicates are removed automatically.

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

Checked against NICE CXone’s documentation on 2026-10-13.