02 Get your data in /Integrations
Gong
Send Gong call transcripts to Dimension Labs with the universal API, your team and the customer kept apart.
- G GongTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in each Gong call as one conversation, turn by turn: what your prospects and customers said, and what your own team said back. You run it on your side: a small script pulls from the Gong API and posts each turn to the universal API.
What you need
Section titled “What you need”- A Gong tech admin: only they can create API keys. Gong’s API is available on any Gong plan.
- A Gong API access key and secret, and your company’s Gong API base URL.
- The email domains your company uses, for example
acme.com, as a fallback for speakers Gong can’t place. - A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Gong
Section titled “1. Get API access in Gong”- From the left sidebar, click Admin center.
- In the Settings tab, click API under Ecosystem.
- Click + Get API key and enter a name. TTL (days) and Trusted IPs are optional; if you set trusted IPs, the key only works from those addresses.
- Click Get API key, then copy the access key and secret. You can’t see the secret again after you close the dialog.
- Find your base URL for API calls on Gong’s API authentication page, as described in Gong’s API introduction.
Authenticate with Basic auth: the access key and secret joined with a colon, Base64-encoded (manage API keys).
2. Pull new calls
Section titled “2. Pull new calls”Two POST endpoints take the same date filter, on call start time:
curl -u "$GONG_ACCESS_KEY:$GONG_ACCESS_KEY_SECRET" -H "Content-Type: application/json" \ -d '{"filter":{"fromDateTime":"2026-10-25T00:00:00Z","toDateTime":"2026-10-26T00:00:00Z"},"contentSelector":{"exposedFields":{"parties":true}}}' \ "$GONG_BASE_URL/v2/calls/extensive"
curl -u "$GONG_ACCESS_KEY:$GONG_ACCESS_KEY_SECRET" -H "Content-Type: application/json" \ -d '{"filter":{"fromDateTime":"2026-10-25T00:00:00Z","toDateTime":"2026-10-26T00:00:00Z"}}' \ "$GONG_BASE_URL/v2/calls/transcript"/v2/calls/extensivereturns each call’s metadata and, withparties: true, its participants. It needs the scopeapi:calls:read:extensivefor OAuth apps./v2/calls/transcriptreturns each call’s transcript as monologues: aspeakerId, atopicand thesentences, each withstartandendin milliseconds from the start of the call. It needsapi:calls:read:transcript.- Both return a
records.cursorwhen there are more pages. Repeat the call with thatcursorand the same filter until it is missing. A404means no calls in the period. - The filter is on when the call started, not when its transcript was ready. The code below waits
GONG_DELAY_HOURS(3 by default) before it reads a call, so the transcript is there. Gong doesn’t publish how long transcription takes: if recent calls arrive without their turns, raise the delay.
Rate limits, per Gong: 3 API calls per second and 10,000 per day. Going over returns 429 with a Retry-After header in seconds (what the Gong API provides).
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | Gong | Notes |
|---|---|---|
text | monologue sentences[].text | One message per monologue: the sentences joined. |
userId | party userId, else a hash of emailAddress, else speakerId | Match the monologue’s speakerId to the party with the same speakerId. Team members have a Gong userId; for others the code sends a hash of the email, never the email itself. |
incoming | party affiliation | true for External (prospects, customers), false for Internal (your team). For Unknown, the code checks the email domain against COMPANY_DOMAINS: a match is your team, anything else is incoming. |
sessionId | callId | Prefixed, for example gong-7782342274025937895. |
conversationId | callId | Same value as sessionId. |
dimensionlabs_timestamp | call started plus the first sentence’s start | start is milliseconds from the start of the call. Turns closer than a second apart are spaced to 1,000 ms. |
platformJson | direction, system, scope, media, language, workspaceId, duration; monologue topic | scope says whether the call had any external participants. |
Gong tells your team apart from everyone else itself: each party has an affiliation of Internal, External or Unknown, and the call has a scope of Internal, External or Unknown. Internal-only calls come through too; filter them later on scope if you only want customer calls.
Personal data. Speaker names, email addresses, phone numbers and job titles are in parties: don’t send them, and don’t send the call title, which often names the customer. The code uses Gong’s user id or a hash instead. Transcripts themselves hold names, companies and numbers: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// gong.mjs. Environment: GONG_BASE_URL, GONG_ACCESS_KEY, GONG_ACCESS_KEY_SECRET, COMPANY_DOMAINS (for example "acme.com,acme.co.uk"),// GONG_DELAY_HOURS (default 3), DL_API_KEY.import { createHash } from 'node:crypto';import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const base = process.env.GONG_BASE_URL.replace(/\/$/, '');const auth = 'Basic ' + Buffer.from(`${process.env.GONG_ACCESS_KEY}:${process.env.GONG_ACCESS_KEY_SECRET}`).toString('base64');const DELAY = Number(process.env.GONG_DELAY_HOURS ?? 3) * HOUR;const DOMAINS = (process.env.COMPANY_DOMAINS ?? '').toLowerCase().split(',').map((d) => d.trim()).filter(Boolean);const hash = (s) => createHash('sha256').update(s.trim().toLowerCase()).digest('hex').slice(0, 32);
// Retries Gong's rate limits (429) and server errors (5xx), honoring Retry-After. A 404 means no calls in the period.async function post(path, body) { for (let attempt = 0; ; attempt++) { const res = await fetch(`${base}${path}`, { method: 'POST', headers: { Authorization: auth, 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (res.ok) return res.json(); if (res.status === 404) return {}; if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Gong returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000); }}
// Reads every page, passing records.cursor back with the same filter.async function all(path, body, key) { const items = []; for (let cursor; ; ) { const page = await post(path, cursor ? { ...body, cursor } : body); items.push(...(page[key] ?? [])); cursor = page.records?.cursor; if (!cursor) return items; }}
// Your team is outgoing. Gong's affiliation decides; for Unknown, fall back to your email domains.function isInternal(party) { if (party?.affiliation === 'Internal') return true; if (party?.affiliation === 'External') return false; const domain = party?.emailAddress?.split('@')[1]?.toLowerCase(); return Boolean(domain && DOMAINS.includes(domain));}
// Sends every call that started at or after `since` (Unix ms) and at least GONG_DELAY_HOURS ago. Returns the new `since`.export async function syncGong({ since, historical = false }) { const until = Date.now() - DELAY; if (until <= since) return since; const filter = { fromDateTime: new Date(since).toISOString(), toDateTime: new Date(until).toISOString() }; const calls = new Map((await all('/v2/calls/extensive', { filter, contentSelector: { exposedFields: { parties: true } } }, 'calls')) .map((c) => [c.metaData.id, c])); for (const t of await all('/v2/calls/transcript', { filter }, 'callTranscripts')) { const call = calls.get(t.callId); if (!call) continue; const m = call.metaData; const speakers = new Map((call.parties ?? []).filter((p) => p.speakerId).map((p) => [p.speakerId, p])); const started = toMillis(m.started); let last = 0; for (const mono of t.transcript ?? []) { const text = (mono.sentences ?? []).map((s) => s.text).join(' ').trim(); if (!text) continue; const party = speakers.get(mono.speakerId); const ts = Math.max(started + (mono.sentences[0].start ?? 0), last + 1000); // keep turns at least a second apart last = ts; await sendToDimensionLabs({ text, userId: `gong-${party?.userId ?? (party?.emailAddress ? hash(party.emailAddress) : mono.speakerId)}`, incoming: !isInternal(party), sessionId: `gong-${t.callId}`, conversationId: `gong-${t.callId}`, dimensionlabs_timestamp: ts, platformJson: { callId: t.callId, topic: mono.topic, affiliation: party?.affiliation ?? 'Unknown', direction: m.direction, system: m.system, scope: m.scope, media: m.media, language: m.language, workspaceId: m.workspaceId, durationSeconds: m.duration, }, }, { historical: historical || ts < Date.now() - HOUR }); } } return until;}5. Run it
Section titled “5. Run it”Save the code as gong.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncGong as sync } from './gong.mjs';.
- History first:
node run.mjs 2025-01-01sends every call since that date to/trackhistorical. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends calls once they are
GONG_DELAY_HOURSold. By then every turn is more than an hour old, so it goes to/trackhistorical: start a backfill from the Backfill tab whenever you want the latest calls enriched. - Re-runs are safe: duplicates are removed automatically. If a call’s transcript was late, run
node run.mjswith a date before it to pick it up.
Gong allows 10,000 API calls a day, shared by every integration on your Gong account. Each run costs a few calls per page of results, so a history load fits comfortably; the code waits whenever Gong asks it to. For analytics at scale, Gong’s data cloud shares your Gong data to Snowflake, BigQuery or Databricks, refreshed daily; from there, follow the Snowflake or BigQuery guide. You can also upload a file of transcripts (see Call transcripts, and check it with the file checker), then run a backfill.
Gong reference
Section titled “Gong reference”- Manage API keys
- Gong API introduction (base URL, authentication, limits, cursors)
- What the Gong API provides
- Retrieve transcripts of calls (/v2/calls/transcript)
- Retrieve detailed call data (/v2/calls/extensive)
- Choosing between Gong’s API and data cloud
Checked against Gong’s documentation on 2026-10-26.