02 Get your data in /Integrations
NICE CXone
Send digital and voice interactions from NICE CXone to Dimension Labs with the universal API.
- NC NICE CXoneTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- 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.
What you need
Section titled “What you need”- CXone Interaction Analytics on your tenant (for voice transcripts and the analyzed digital transcripts).
- An app registered with NICE, which gives you a
client_idandclient_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.
1. Get API access in NICE CXone
Section titled “1. Get API access in NICE CXone”NICE’s own steps are in Getting Started.
- Submit the Application Registration form described in Getting Started. Choose a back-end app and single tenant. NICE replies with your
client_idandclient_secret. - In CXone, open the app selector and go to Admin > Employees > Create Employee. Create a user for the integration, with no login authenticator.
- 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.
- Get a token:
POST https://cxone.niceincontact.com/auth/tokenwith a form body ofgrant_type=password,username(the access key ID),password(the secret),client_idandclient_secret. Tokens expire (expires_in), so request a new one before each run. - Find your API base URL: read
tenantIdfrom theid_token, thenGET https://cxone.niceincontact.com/.well-known/cxone-configuration?tenantId=YOUR_TENANT_ID. Theapi_endpointfield is your base URL, for examplehttps://api-na1.niceincontact.com.
Send the token as Authorization: Bearer YOUR_TOKEN.
2. Pull new interactions
Section titled “2. Pull new interactions”List analyzed segments since your cursor, then get each segment’s transcript:
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=firstAnnotatedAtfilters by when the segment was first analyzed, which never changes. That makes it a safe cursor.beginningDateandendingDateuseyyyy-MM-ddTHH:mm:ss.SSSZin UTC.mediaTypenarrows to one channel, for examplePhoneCall,ChatorEMail.- 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
404on the transcript endpoint. Skip it. - An interaction can have several segments when it is transferred or conferenced.
interactionIdties them together.
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | NICE CXone | Notes |
|---|---|---|
text | transcriptBlocks[].text | One block per turn. |
userId | segment acdContactId | CXone’s contact id, on every block of the segment. |
incoming | transcriptBlocks[].userType | true when it is CLIENT, false when AGENT. channelName carries the same value. |
sessionId | segmentId | One segment is one conversation in Dimension Labs. |
conversationId | interactionId | Keeps transferred segments of one interaction together. |
dimensionlabs_timestamp | segment startTime | Blocks carry offsets, not clock times. The example adds 1,000 ms per block to startTime to keep the order and the one-second spacing. |
platformJson | mediaType, contactNo, resolved, dispositionCode, segmentSummaryPrimaryIntentName, enlightenSentimentIndexScore, channelType | acdContactId 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.
4. Send it
Section titled “4. Send it”// 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.
Digital only
Section titled “Digital only”Without Interaction Analytics, the Digital Engagement API (on /dfo/3.0) gives you the messages of each digital contact (case):
GET /dfo/3.0/contactslists contacts. It filters bystatus[](for exampleclosed),channel[]androutingQueueId[], sorts withsorting=updatedAtandsortingType=desc, and pages withscrollToken.GET /dfo/3.0/contacts/{contactNumber}/messagesreturns the messages. Each hasdirection(inboundfrom the customer,outboundfrom your side),messageContent.payload.text,createdAtWithMillisecondsandauthorEndUserIdentity. Text is cut at 3,000 characters; whenhasAdditionalMessageContentistrue, get the full message withGET /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.
5. Run it
Section titled “5. Run it”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.
NICE CXone reference
Section titled “NICE CXone reference”Checked against NICE CXone’s documentation on 2026-10-13.