02 Get your data in /Integrations
Front
Send Front conversations and their messages to Dimension Labs with the universal API.
- F FrontTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in the messages of your Front conversations, inbound from customers and outbound from your team, one conversation per Front conversation. You run it on your side: a small script reads Front’s events feed and posts each message to the universal API.
What you need
Section titled “What you need”- A Front admin: only admins can create or manage API tokens.
- A Front API token with read access to the inboxes you want to send.
- A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Front
Section titled “1. Get API access in Front”- Go to Settings > Developers.
- Open the API Tokens tab and click Create API token.
- Give it a descriptive name.
- Choose the Access resources feature, the namespaces it covers (for example All shared workspaces), and Read permission. Private inboxes are only included if the teammate has allowed API access in their settings.
- Click Create and copy the token. You can’t view it again later (API tokens).
- Send it as
Authorization: Bearer YOUR_TOKENon every call.
2. Pull new messages
Section titled “2. Pull new messages”List events filtered to message events after your cursor. inbound is a message received, outbound a message sent and out_reply a reply sent. Each event carries the message as target.data and its conversation (events).
curl -H "Authorization: Bearer $FRONT_TOKEN" \ "https://api2.frontapp.com/events?q[types]=inbound&q[types]=outbound&q[types]=out_reply&q[after]=1760313600&sort_by=created_at&sort_order=asc"q[after] is a Unix timestamp in seconds and filters on when the event was emitted (emitted_at), which for imported messages can be later than the message’s own created_at. This endpoint returns at most 15 events per page. Follow _pagination.next until it is null (pagination, query object).
To read one conversation in full, list conversation messages: GET /conversations/{conversation_id}/messages.
Rate limits, per Front, are per company: 50 requests per minute on Starter, 100 on Professional and 200 on Enterprise. Responses carry x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset headers (rate limits).
3. Map the fields
Section titled “3. Map the fields”| Dimension Labs | Front | Notes |
|---|---|---|
text | message text, else body with HTML stripped | text is the plain version for email messages. body can be HTML. |
userId | the conversation’s contact id | The id at the end of recipient._links.related.contact (it starts with crd_), on every message of the conversation. Fall back to the conversation id. Don’t use recipient.handle: it is the customer’s email address or phone number. |
incoming | message is_inbound | true for a message received, false for one sent. |
sessionId | conversation id | Front ids such as cnv_yo1kg5q have no colons. |
conversationId | conversation id | Same value as sessionId. |
dimensionlabs_timestamp | message created_at | Unix seconds, with a fraction. toMillis turns them into whole milliseconds. |
platformJson | message type; conversation tags, status, ticket_ids, custom_fields | type is the channel, such as email, sms or front_chat. |
Personal data. Email bodies hold signatures, addresses and phone numbers: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// front.mjs. Environment: FRONT_TOKEN, DL_API_KEY.import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const stripHtml = (html) => html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();const types = ['inbound', 'outbound', 'out_reply'].map((t) => `q[types]=${t}`).join('&');
// Retries Front'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 ${process.env.FRONT_TOKEN}` } }); if (res.ok) return res.json(); if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Front returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000); }}
// Sends every message event emitted at or after `since` (Unix ms). Returns the new `since`.export async function syncFront({ since, historical = false }) { let newest = since; let url = `https://api2.frontapp.com/events?${types}&q[after]=${Math.floor(since / 1000) - 1}&sort_by=created_at&sort_order=asc`; while (url) { const page = await get(url); for (const event of page._results) { const m = event.target?.data; const conv = event.conversation; newest = Math.max(newest, toMillis(event.emitted_at)); const text = m?.text || (m?.body ? stripHtml(m.body) : ''); if (!text) continue; const ts = toMillis(m.created_at); const contact = conv.recipient?._links?.related?.contact?.split('/').pop(); await sendToDimensionLabs({ text, userId: contact || conv.id, incoming: m.is_inbound, sessionId: conv.id, conversationId: conv.id, dimensionlabs_timestamp: ts, platformJson: { channel: m.type, status: conv.status, tags: conv.tags?.map((t) => t.name), ticket_ids: conv.ticket_ids, custom_fields: conv.custom_fields }, }, { historical: historical || ts < Date.now() - HOUR }); } url = page._pagination?.next; } return newest;}The cursor follows emitted_at, so an imported message whose created_at is old still gets picked up; it goes to /trackhistorical because of its age.
5. Run it
Section titled “5. Run it”Save the code as front.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncFront as sync } from './front.mjs';.
- History first:
node run.mjs 2025-01-01sends every message event since that date to/trackhistorical. 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.
At 15 events per page and 50 requests a minute on Starter, a history load sends about 750 messages a minute at most; the code waits whenever Front asks it to. You can also upload a file of exported messages (check it with the file checker), then run a backfill.
Front reference
Section titled “Front reference”Checked against Front’s documentation on 2026-10-13.