02 Get your data in /Integrations
Freshchat
Send Freshchat chat transcripts to Dimension Labs with the universal API.
- F FreshchatTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in your Freshchat chats: what each customer wrote and what your agents and bots replied, with one session per interaction (from the first message until the chat is resolved). You run it on your side: a small script pulls the Chat-Transcript raw report from the Freshchat API and posts each message to the universal API.
What you need
Section titled “What you need”- A Freshchat admin, to generate an API key.
- Your Freshchat API key and account URL.
- Node’s
csv-parsepackage:npm install csv-parse. The report arrives as CSV. - A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Freshchat
Section titled “1. Get API access in Freshchat”Freshchat’s own steps are in its API reference, under Authentication.
- In a standalone Freshchat account, go to Admin > CONFIGURE > API Tokens and click Generate Token. Copy the API key.
- In Freshchat inside Freshsales Suite, go to Settings > Admin Settings > Website Tracking and APIs > API Settings. Under API DETAILS FOR CHAT, copy Your API Key (click Generate token if it is empty). The same section shows Your chat URL.
- Send the key as
Authorization: Bearer YOUR_API_KEY.
Your account URL is https://<account-name>.freshchat.com for a standalone account, or your chat URL for Freshsales Suite. API paths go under /v2.
2. Pull new chats
Section titled “2. Pull new chats”Freshchat has no endpoint that lists every conversation. The raw reports API does: ask for a Chat-Transcript report, wait for it, then download it.
curl -X POST "https://YOUR_ACCOUNT.freshchat.com/v2/reports/raw" \ -H "Authorization: Bearer $FRESHCHAT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"start":"2026-10-25T00:00:00.000Z","end":"2026-10-26T00:00:00.000Z","event":"Chat-Transcript","format":"csv"}'- The response holds the report
id. CallGET /v2/reports/raw/{id}untilstatusisCOMPLETED(orFAILED). Then each entry inlinkshas a download URL inlink.href. - A
Chat-Transcriptwindow can be at most 24 hours, andstartcan be no earlier than 15 months ago. - The download links expire after one hour, so download them as soon as the report is ready.
- Freshworks’s extract API article says reports can take up to an hour to generate and only one report request is processed per minute. Its raw data article lists 120 Chat-Transcript requests a day.
Rate limits, per Freshchat: the hourly number of API calls depends on your plan. Read it from the X-RateLimit-Limit and X-RateLimit-Remaining response headers.
3. Map the fields
Section titled “3. Map the fields”Each row of the report is one message.
| Dimension Labs | Freshchat | Notes |
|---|---|---|
text | message_parts | A JSON array in the CSV cell. Join the text.content of each part. Skip rows whose message_type is private (private notes) or system. |
userId | customer_id | Freshchat’s id for the customer who started the chat, on every message. Fall back to the sessionId. |
incoming | actor_type | true when actor_type is USER. AGENT, BOT and SYSTEM are your side. |
sessionId | interaction_raw_id | One interaction, from the first message until the chat is resolved. A reopened chat gets a new one. Prefix it, for example freshchat-.... |
conversationId | conversation_id | The whole conversation thread, across interactions. |
dimensionlabs_timestamp | created_time | UTC. Convert with toMillis. |
platformJson | channel_name, message_source, actor_type | channel_name is the topic, such as Billing or Sales. |
Personal data. Don’t send actor_email, actor_phone, actor_first_name or actor_last_name. Messages can hold names, emails and order numbers: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// freshchat.mjs. Environment: FRESHCHAT_URL (for example https://yourcompany.freshchat.com), FRESHCHAT_API_KEY, DL_API_KEY.// Needs: npm install csv-parseimport { parse } from 'csv-parse/sync';import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const DAY = 24 * HOUR;const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const base = `${process.env.FRESHCHAT_URL.replace(/\/$/, '')}/v2`;const headers = { Authorization: `Bearer ${process.env.FRESHCHAT_API_KEY}`, 'Content-Type': 'application/json', Accept: 'application/json' };
// Retries rate limits (429) and server errors (5xx), honoring Retry-After.async function call(url, options = {}) { for (let attempt = 0; ; attempt++) { const res = await fetch(url, options); if (res.ok) return res; if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Freshchat returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000); }}
// Asks for one Chat-Transcript report and waits until it is ready (up to 80 minutes).async function transcriptReport(start, end) { const body = JSON.stringify({ start: new Date(start).toISOString(), end: new Date(end).toISOString(), event: 'Chat-Transcript', format: 'csv' }); const { id } = await (await call(`${base}/reports/raw`, { method: 'POST', headers, body })).json(); for (let i = 0; i < 80; i++) { const report = await (await call(`${base}/reports/raw/${id}`, { headers })).json(); if (report.status === 'COMPLETED') return report; if (report.status === 'FAILED') throw new Error(`Freshchat report ${id} failed`); await sleep(60 * 1000); } throw new Error(`Freshchat report ${id} was not ready after 80 minutes`);}
function textOf(row) { try { return JSON.parse(row.message_parts || '[]').map((p) => p.text?.content).filter(Boolean).join('\n'); } catch { return ''; }}
// Sends every customer, agent and bot message created at or after `since` (Unix ms). Returns the new `since`.export async function syncFreshchat({ since, historical = false }) { const now = Date.now(); let start = since; while (start < now) { const end = Math.min(start + DAY, now); // Chat-Transcript reports cover 24 hours at most const report = await transcriptReport(start, end); for (const { link } of report.links ?? []) { const csv = await (await call(link.href)).text(); // download right away: links expire after an hour for (const row of parse(csv, { columns: true, skip_empty_lines: true })) { const text = textOf(row); if (row.message_type !== 'normal' || !text.trim()) continue; // skips private notes and system messages const ts = toMillis(row.created_time); const sessionId = `freshchat-${row.interaction_raw_id || row.conversation_id}`.replace(/:/g, '-'); await sendToDimensionLabs({ text, userId: row.customer_id || sessionId, incoming: String(row.actor_type).toUpperCase() === 'USER', sessionId, conversationId: `freshchat-${row.conversation_id}`, dimensionlabs_timestamp: ts, platformJson: { channel_name: row.channel_name, message_source: row.message_source, actor_type: row.actor_type }, }, { historical: historical || ts < Date.now() - HOUR }); } } start = end; if (start < now) await sleep(60 * 1000); // Freshworks processes one report request a minute } return start;}5. Run it
Section titled “5. Run it”Save the code as freshchat.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncFreshchat as sync } from './freshchat.mjs';.
- History first:
node run.mjs 2025-08-01asks for one report per day since that date (15 months back at most) and sends every message to/trackhistorical. Then start a backfill from the Backfill tab. - Then every hour: a report can take up to an hour to generate and the daily number of report requests is limited, so run this job hourly rather than every 15 minutes. Use
run.mjson a server or cron, not Lambda: waiting for a report can outlast a Lambda run. Messages that are more than an hour old when the job sees them go to/trackhistorical, and need a backfill. - Re-runs are safe: duplicates are removed automatically.
A history load uses one report request per day of history and waits at least a minute between requests, so a long history takes hours, and more than 120 days of history needs more than one day of runs. Run it again with the same date if it stops: what was already sent is deduplicated. You can also upload a file of exported chats with one row per message (check it with the file checker), then run a backfill.
Freshchat reference
Section titled “Freshchat reference”- Freshchat API reference (Authentication, Rate limits, Reports, The message object)
- Extract API
- How can I fetch the raw data of my chats?
Checked against Freshchat’s documentation on 2026-10-26.