Skip to content

02 Get your data in /Integrations

Google Dialogflow CX

Send agent conversations from Google Dialogflow CX to Dimension Labs with the universal API.

  1. GD Google Dialogflow CXTickets, 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 your Dialogflow CX (Conversational Agents) conversations, turn by turn: what each end user said or typed and what the agent answered. Each Dialogflow conversation becomes one conversation. You run it on your side: a small script pulls from the Dialogflow CX Conversations API and posts each message to the universal API.

The Conversations API returns each turn’s user text and agent text as plain strings, which keeps the code short. If you already export conversation history to BigQuery, you can use that table instead: see BigQuery export.

  • Conversation history turned on for the agent. Dialogflow only stores conversations from the time it is on.
  • A Google Cloud service account with the IAM permissions dialogflow.conversations.list and dialogflow.conversations.get on the agent’s project. A custom role with just those two is enough.
  • Your project id, the agent’s location (for example global or us-central1) and the agent id.
  • A Dimension Labs API key for a new Universal API source. See Your API key.
  1. Turn on conversation history: open the Conversational Agents console, choose your project and agent, and click the settings icon in the top right corner. Under Logging settings, turn on Enable conversation history, then click Save (agent settings).
  2. In the Google Cloud console, create a service account for the job, and grant it a role with dialogflow.conversations.list and dialogflow.conversations.get (access control).
  3. On the Service accounts page, click the account’s email address, open the Keys tab, and choose Add key > Create new key. Select JSON and click Create (create a service account key). Store the file in a secrets manager. The code finds it through the GOOGLE_APPLICATION_CREDENTIALS environment variable. On Google Cloud, an attached service account works without a key file (Application Default Credentials).

Requests need the OAuth scope https://www.googleapis.com/auth/dialogflow (or cloud-platform). The google-auth-library package gets the token for you: npm install google-auth-library.

Use the right host. For the global location the host is dialogflow.googleapis.com; for any other region it is <region>-dialogflow.googleapis.com, and the region must also appear in the path (regionalization).

List conversations created since a point in time:

Terminal window
curl -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://dialogflow.googleapis.com/v3beta1/projects/YOUR_PROJECT/locations/global/agents/YOUR_AGENT_ID/conversations?pageSize=1000&filter=createTime%20%3E%20%222026-10-01T00:00:00Z%22"
  • filter takes createTime > "<RFC 3339 time>". Results come newest first.
  • pageSize defaults to 100 and goes up to 1,000. Pass nextPageToken as pageToken until it is empty.
  • The list leaves interactions empty. Call GET https://<host>/v3beta1/<conversation name> for each conversation to get its turns.
  • Each interaction has requestUtterances (the user’s text, or the transcript of their audio), responseUtterances (the agent’s text or the transcript of its audio, joined into one string) and createTime.
  • Dialogflow logs at most 500 turns per conversation. How long it keeps conversations is set by the retention_window_days security setting.
  • The Conversations API is on the v3beta1 version of the API.

A Dialogflow session stays open for 30 minutes after the last request by default, so a conversation can keep growing after it first appears. The code re-reads the conversations created in the last two hours on every run.

Dimension LabsDialogflow CXNotes
textinteraction requestUtterances (user), responseUtterances (agent)One interaction gives up to two messages. Skip empty ones, such as an event-only request.
userIdconversation idDialogflow doesn’t give an end-user id here, so use the sessionId on both sides.
incomingwhich field the text came fromtrue for requestUtterances, false for responseUtterances.
sessionIdthe last segment of the conversation namePrefix it, for example dialogflow-<conversation id>.
conversationIdthe last segment of the conversation nameSame value as sessionId.
dimensionlabs_timestampinteraction createTimeConvert with toMillis. The agent’s reply gets the same time plus 1,000 ms.
platformJsonconversation languageCode, type, environment.displayName, metrics.hasLiveAgentHandoff; interaction response.queryResult.match.intent.displayName, response.queryResult.currentPage.displayNametype is TEXT, AUDIO or UNDETERMINED.

Personal data. End users sometimes say or type names, emails or account numbers: redact them before you send. See PII redaction.

// dialogflow-cx.mjs. Environment: GOOGLE_APPLICATION_CREDENTIALS (service account key file), DIALOGFLOW_PROJECT, DIALOGFLOW_LOCATION (for example global), DIALOGFLOW_AGENT_ID, DL_API_KEY.
import { GoogleAuth } from 'google-auth-library';
import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;
const LOOKBACK = 2 * HOUR; // re-read recent conversations: they keep growing until the session ends
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const auth = new GoogleAuth({ scopes: 'https://www.googleapis.com/auth/dialogflow' });
const LOC = process.env.DIALOGFLOW_LOCATION;
const HOST = `https://${LOC === 'global' ? '' : `${LOC}-`}dialogflow.googleapis.com/v3beta1`;
const AGENT = `projects/${process.env.DIALOGFLOW_PROJECT}/locations/${LOC}/agents/${process.env.DIALOGFLOW_AGENT_ID}`;
// Retries Dialogflow's rate limits (429) and server errors (5xx), honoring Retry-After.
async function get(url) {
for (let attempt = 0; ; attempt++) {
const res = await fetch(url, { headers: { Authorization: `Bearer ${await auth.getAccessToken()}` } });
if (res.ok) return res.json();
if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Dialogflow returned ${res.status}: ${await res.text()}`);
await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000);
}
}
// Sends every turn of the conversations created since `since` (Unix ms), minus the lookback. Returns the new `since`.
export async function syncDialogflowCx({ since, historical = false }) {
const started = Date.now();
const filter = `createTime > "${new Date(since - LOOKBACK).toISOString()}"`;
const names = [];
let pageToken = '';
do {
const page = await get(`${HOST}/${AGENT}/conversations?pageSize=1000&filter=${encodeURIComponent(filter)}${pageToken ? `&pageToken=${encodeURIComponent(pageToken)}` : ''}`);
names.push(...(page.conversations ?? []).map((c) => c.name));
pageToken = page.nextPageToken;
} while (pageToken);
for (const name of names.reverse()) { // oldest first
const c = await get(`${HOST}/${name}`);
const id = `dialogflow-${name.split('/').pop()}`.replace(/:/g, '-');
const turns = (c.interactions ?? []).slice().sort((a, b) => toMillis(a.createTime) - toMillis(b.createTime));
for (const i of turns) {
const ts = toMillis(i.createTime);
const result = i.response?.queryResult;
const platformJson = {
language: c.languageCode, type: c.type, environment: c.environment?.displayName,
live_agent_handoff: c.metrics?.hasLiveAgentHandoff,
intent: result?.match?.intent?.displayName, page: result?.currentPage?.displayName,
};
for (const [text, incoming, at] of [[i.requestUtterances, true, ts], [i.responseUtterances, false, ts + 1000]]) {
if (!text?.trim()) continue;
await sendToDimensionLabs({
text,
userId: id,
incoming,
sessionId: id,
conversationId: id,
dimensionlabs_timestamp: at,
platformJson,
}, { historical: historical || at < Date.now() - HOUR });
}
}
}
return started;
}

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

  • History first: node run.mjs 2026-01-01 sends every conversation created since that date to /trackhistorical, as far back as your retention setting allows. Then start a backfill from the Backfill tab.
  • Then every 15 minutes: the scheduled job sends new messages to /track, and they are enriched automatically every day. A message that is more than an hour old when the job sees it goes to /trackhistorical, and needs a backfill.
  • Re-runs are safe: duplicates are removed automatically. Each run re-sends the turns of the last two hours of conversations, so late turns are caught; the repeats are dropped. If your sessions run longer than that (sessionTtl can be up to a day), raise LOOKBACK.

Each conversation costs one get call, so a long history takes a while; the code waits whenever Google asks it to. In the console, Conversation history > Export all conversations downloads at most 50 conversations as CSV, so for a file route use BigQuery.

Dialogflow can write all live conversation history to a BigQuery table, one row per turn, with conversation_name, turn_position, request_time, and the request and response as JSON (export to BigQuery). You create the dataset and table yourself, then turn on BigQuery export in the agent settings. It needs conversation history turned on too, and only covers conversations from the time it is set up.

If you use it, read new rows by request_time and follow the BigQuery guide, with the same direction rules: the user’s input from request is incoming, the agent’s reply from response is outgoing, and conversation_name is the session.

Checked against Google’s documentation on 2026-10-26.