02 Get your data in /Integrations
Amazon Connect
Send chat transcripts and call transcripts from Amazon Connect to Dimension Labs with the universal API.
- AC Amazon ConnectTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in Amazon Connect chat transcripts and call transcripts, turn by turn, from the files that conversational analytics (Contact Lens) writes to your instance’s S3 bucket. You run it on your side with the universal API. AWS now calls the product Amazon Connect Customer.
What you need
Section titled “What you need”- An Amazon Connect instance with conversational analytics turned on for the flows you want: speech analytics for calls, chat analytics for chats.
- An IAM role or user for your sync with
s3:ListBucketands3:GetObjecton the instance’s bucket. If the bucket is encrypted with a KMS key, the role also needs to use that key. - A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get access in Amazon Connect
Section titled “1. Get access in Amazon Connect”- In the Amazon Connect console, open your instance and choose Data storage. Note the S3 bucket set there. AWS’s steps are in Update instance settings.
- In your flows, add a Set recording and analytics behavior block (or Set recording, analytics and processing behavior in new flows) and turn on conversational analytics: Enable chat analytics for chats, and call recording plus speech analytics for calls. AWS’s steps are in Enable conversational analytics.
- In IAM, give the role that runs your sync read access to that bucket.
2. Pull new transcripts
Section titled “2. Pull new transcripts”Conversational analytics writes one JSON file per contact, in folders by date:
Analysis/Voice/2026/10/13/{contact id}_analysis_2026-10-13T21:14:16Z.jsonAnalysis/Chat/2026/10/13/{contact id}_analysis_2026-10-13T21:14:16Z.jsonWhen you turn on redaction in the flow block, redacted copies sit under Analysis/Voice/Redacted/ and Analysis/Chat/Redacted/. The code below reads those by default, because Dimension Labs doesn’t store personal data. Set CONNECT_REDACTED=false only if your files hold no personal data.
List new files with S3 ListObjectsV2:
aws s3api list-objects-v2 --bucket YOUR_CONNECT_BUCKET --prefix "Analysis/Chat/2026/10/13/"ListObjectsV2returns up to 1,000 keys per request. WhenIsTruncatedis true, sendNextContinuationTokenasContinuationTokenfor the next page.- Keys inside a day are not in time order, so use each file’s
LastModifiedas the cursor: send files modified after the last run and up to the start of this one. - The date in the path is when the agent connected (
ConnectedToAgentTimestamp), not when the contact started.
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | Amazon Connect | Notes |
|---|---|---|
text | Transcript[].Content | For chats, keep items with Type MESSAGE; skip EVENT items. |
userId | contact id | No customer id in the file. Use the contact id, or look up your own internal customer id in your system (never a phone number). |
incoming | ParticipantRole | true when the role is CUSTOMER. AGENT and SYSTEM are your side. Call turns carry a ParticipantId; look its role up in the file’s Participants list. |
sessionId | contact id | From the file name, before _analysis_. |
conversationId | contact id | A transfer creates a new contact id. |
dimensionlabs_timestamp | chat: AbsoluteTime; call: file time plus BeginOffsetMillis | Call turns only carry offsets. Add each turn’s BeginOffsetMillis to the time in the file name, which is when the agent connected. Turns closer than a second apart are spaced to 1,000 ms. |
platformJson | Channel, LanguageCode, Sentiment, Categories.MatchedCategories | Sentiment is per turn. Leave out DisplayName: it is a participant’s name. |
4. Send it
Section titled “4. Send it”Install the S3 client in your project first: npm install @aws-sdk/client-s3. The role the job runs as needs s3:ListBucket and s3:GetObject on the bucket.
// amazon-connect.mjs. Environment: CONNECT_BUCKET, CONNECT_REDACTED (default true), DL_API_KEY.import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';import { S3Client, ListObjectsV2Command, GetObjectCommand } from '@aws-sdk/client-s3';
const HOUR = 60 * 60 * 1000;const DAY = 24 * HOUR;const s3 = new S3Client({}); // the SDK retries throttling and server errors itselfconst Bucket = process.env.CONNECT_BUCKET;const REDACTED = process.env.CONNECT_REDACTED !== 'false' ? 'Redacted/' : '';
// Sends every transcript file written at or after `since` (Unix ms). Returns the new `since`.export async function syncConnect({ since, historical = false }) { let newest = since; for (const kind of ['Voice', 'Chat']) { // Folders are dated by when the agent connected, so start a day early for contacts that crossed midnight. for (let d = since - DAY; d <= Date.now(); d += DAY) { const day = new Date(d).toISOString().slice(0, 10).replace(/-/g, '/'); let token; do { const list = await s3.send(new ListObjectsV2Command({ Bucket, Prefix: `Analysis/${kind}/${REDACTED}${day}/`, ContinuationToken: token })); for (const obj of list.Contents ?? []) { const modified = obj.LastModified.getTime(); const name = obj.Key.split('/').pop().match(/^(.+?)_analysis_(?:redacted_)?(.+)\.json$/i); if (modified < since || !name) continue; const [, contactId, connected] = name; const file = JSON.parse(await (await s3.send(new GetObjectCommand({ Bucket, Key: obj.Key }))).Body.transformToString()); const roles = Object.fromEntries((file.Participants ?? []).map((p) => [p.ParticipantId, p.ParticipantRole])); let last = 0; for (const t of file.Transcript ?? []) { if (kind === 'Chat' && t.Type !== 'MESSAGE') continue; if (!t.Content?.trim()) continue; const role = t.ParticipantRole ?? roles[t.ParticipantId]; const at = kind === 'Chat' ? toMillis(t.AbsoluteTime) : toMillis(connected) + t.BeginOffsetMillis; const ts = Math.max(at, last + 1000); // keep turns at least a second apart last = ts; await sendToDimensionLabs({ text: t.Content, userId: contactId, incoming: role === 'CUSTOMER', sessionId: contactId, conversationId: contactId, dimensionlabs_timestamp: ts, platformJson: { channel: file.Channel, language: file.LanguageCode, sentiment: t.Sentiment, role, categories: file.Categories?.MatchedCategories }, }, { historical: historical || ts < Date.now() - HOUR }); } newest = Math.max(newest, modified); } token = list.IsTruncated ? list.NextContinuationToken : undefined; } while (token); } } return newest;}5. Run it
Section titled “5. Run it”Save the code as amazon-connect.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncConnect as sync } from './amazon-connect.mjs';. On Lambda, add the S3 permissions to the function’s execution role.
- History first:
node run.mjs 2025-01-01sends every file since that date to/trackhistorical. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends new transcripts to
/track, and they are enriched automatically every day. Contact Lens writes a file after the contact ends, so the turns of a contact that lasted more than an hour go to/trackhistorical, and need a backfill. - Re-runs are safe: duplicates are removed automatically.
You can also upload a file built from these JSON files (see Call transcripts) and check it first with the file checker, then run a backfill.
Amazon Connect reference
Section titled “Amazon Connect reference”- What is Connect Customer?
- Update instance settings (Data storage)
- Enable conversational analytics
- Output file locations for files analyzed by conversational analytics
- Example conversational analytics output files for calls
- Example conversational analytics output files for chats
- Amazon S3 ListObjectsV2
- Amazon S3 GetObject
Checked against Amazon Connect’s documentation on 2026-10-13.