Conversions overview
The concept page Server-side conversions explains what 3x does. This section explains how to set it up and operate it.
Setup in four steps
- Destinations – one per platform, with credentials, mode and transport: Meta, LinkedIn, Google Ads, TikTok.
- Definitions – what counts as a conversion and how it maps to each platform (Definitions).
- Recording – automatically from events, goals or leads, or explicitly from your server, the MCP server or the dashboard.
- Test –
dry_run→test→live(Test modes).
Destinations
GET /v1/projects/{project}/destinations lists the four providers; PUT …/destinations/{provider}
updates one (scope project:admin):
{
"enabled": true,
"mode": "test",
"transport": "direct",
"settings": { "pixelId": "1234567890123456", "apiVersion": "v26.0", "testEventCode": "TEST12345" },
"secrets": { "accessToken": "EAAB…" }
}| Field | Values |
|---|---|
mode | live (send for real), test (platform test endpoints/validate-only), dry_run (build payloads, log a PII-free summary, send nothing) |
transport | direct (3x calls the platform API, default), composio (through the project's Composio ad connection), feed (Google Ads CSV feed only) |
secrets | stored encrypted (AES-256-GCM); responses only contain secretsSet and masked secretsHint. Send null to remove a secret |
POST …/destinations/{provider}/test sends a synthetic test event in test mode and reports the
platform's answer.
The deployment caps every destination: X3_CONVERSIONS_MODE (dry_run by default outside
X3_ENV=production) and the kill switch X3_CONVERSIONS_DISABLED=1.
Composio transport
| Provider | Through Composio | Notes |
|---|---|---|
| Meta | METAADS_SEND_CONVERSION_EVENTS | needs a Meta connection whose token can access the pixel |
| Google Ads | uploadClickConversions via the Composio proxy | uses the Google connection; loginCustomerId for MCC access |
/rest/conversionEvents via the Composio proxy | needs rw_conversions on the LinkedIn auth config | |
| TikTok | – | direct only |
A provider is either direct or Composio, never both. Select the connection with adConnectionId.
Recording from your server
import { createX3Server, requestContext } from "@3x-engine/sdk/server";
const x3 = createX3Server({ apiUrl: process.env.X3_API_URL!, apiKey: process.env.X3_SECRET_KEY! });
await x3.conversions.create({
definition: "demo_booked",
sessionId, // from getIds() in the browser, sent with your form
eventId: `booking:${bookingId}`,
email, phone, firstName, lastName,
value: 500, currency: "EUR",
...requestContext(request), // IP, user agent, _fbp/_fbc/_ttp (after consent)
});See Server helper for request context (IP, user agent, _fbp/_fbc) and the
combined leads.create({ conversion }) call.
Watching the pipeline
- Dashboard → Conversions: last conversions with per-platform status, retry button, filters.
GET /v1/projects/{project}/conversions?provider=meta&provider_status=failed(scopeconversions:read).- MCP
conversions_list,conversions_mark,conversions_retry,conversions_update_status,conversions_definitions_list. - Troubleshooting lists every status and error with its fix.