Docs

Guided onboarding

After sign-up and creating a project, X3 Growth Intelligence guides you through connecting every tool, one after another. Each step shows a live status, offers a function test and can be resumed at any time. Claude Code can drive the same onboarding through the MCP server x3.

Overview

The wizard lives at /onboarding?org=<org>&project=<project> in the dashboard. It has seven working steps and a summary screen ("Fertig"):

#StepOn screenRequired
1project"Organisation & Projekt"yes
2install"Tracking einbauen"yes
3goals"Ziele & Funnels"recommended (skippable)
4ads"Werbekonten"optional
5conversions"Conversions senden"optional
6ai"Claude verbinden"optional
7team"Team einladen"optional
done"Fertig" – summary of every integration–

Moving on is never blocked: "Weiter" always works, "Überspringen" is offered for optional steps, and the status stays honest. Re-open the onboarding any time under Projekt → Einstellungen → Setup-Status (/<org>/<project>/settings/setup); every row links back into its step, and skipped steps can be resumed ("Wieder aufnehmen"). While the required steps are incomplete, the project overview shows a slim banner ("Einrichtung: 3 von 7 Schritten erledigt – weiter").

Members who lack the permission for a step see it read-only with the chip "keine Berechtigung" – for example only organization admins can create keys or invite people.

How status is checked

Every step's state is derived from real data, not checkboxes: the API computes it on each request (GET /v1/projects/{project}/setup/status). The only stored user input is skipped, done (team only) and dismissed (banner).

StateChipMeaning
not_connected"nicht verbunden"nothing configured yet
waiting"wartet auf Daten"configured, no proof from real data yet
connected"verbunden"proven by real data within the freshness window
error"Fehler"a concrete problem – always with a reason and a fix
skipped"übersprungen"you skipped an optional step
not_applicable"nicht nötig"e.g. conversions while no ad platform is in use

A step is as bad as its worst required item (error > waiting > not_connected > connected). Real data always wins over a skip: a skipped ad step with an active connection shows connected. Every item carries a reason code (see Reason codes) with a text and a fix.

Freshness windows:

SignalWindow
Tracking connectedan accepted pageview or event within 7 days
Goal or custom event "seen"24 hours
Ad spend syncedmetrics within the last 3 days for an enabled account
UTM coveragesessions of the last 14 days
Conversion upload provenan upload (not a test) or a successful test within 30 days
Claude connectedan MCP call for the project within 30 days

Test visits count for the setup checks (see Test visits and conversions), so the function tests turn steps green; they never count in reports, UTM coverage or upload counts.

Project

Name, website address, further domains, time zone, currency and allowed origins. Checks:

  • domains – at least one real (non-localhost) domain, else only_localhost.
  • origins – every domain has an https:// allowed origin, else origin_missing with a one-click "Hinzufügen".
  • timezone – a warning timezone_utc when the project runs on UTC.

Allowed-origin edits read the current list and send the complete merged list – the tracking config replaces each top-level key as a whole.

Install tracking

Pick how the website is built (WordPress, Webflow, Shopify, Next.js, React, Astro, HTML, Google Tag Manager, Claude Code) or click "Weiß ich nicht" – X3 fetches the site's HTML through its SSRF guard and suggests the matching tab. Every code tab contains your publishable key already; Next.js, WordPress and server-hosted stacks offer the first-party proxy with a "Proxy-Schlüssel erzeugen" button. "Anleitung an Entwickler:in schicken" mails the instructions (never a secret) or invites the developer as an admin.

The live panel below the code polls the installation status every few seconds: first pageview, hosts seen (allowed or rejected), SDK version, proxy use, consent share, custom events of the last 24 h, the last event and the last website check. "Testbesuch senden" opens your site with ?x3_test=1 (the project's test parameter) – the events appear live and are marked as test traffic. "Website jetzt prüfen" runs the installation check (snippet, key, origin, proxy, CSP, events).

Typical fixes: no_pageview (publish the code, send a test visit), origin_rejected (allow the domain only if it is yours), proxy_key_derived, consent_never_granted.

Goals and funnels

Presets: "Formular abgeschickt" (form_submit, switches the forms plugin on), "Termin gebucht" (booking, with a booking adapter: Calendly, thank-you page, postMessage, window event), "WhatsApp-Klick", "Anruf-Klick", "E-Mail-Klick", "Kauf" (registers the purchase schema – needs code), "Quiz abgeschlossen" and "Danke-Seite erreicht". Each card shows "Gesehen (24 h)". The step is connected when at least one conversion goal was seen in 24 hours.

Optional: a standard funnel (landing → CTA click → your main goal) and the relevance preset "Standard (Lead-Website)", which forces your conversion goals to the top tier – previewed before it is applied.

Ad platforms

Connect Google Ads, Meta Ads, LinkedIn Ads and TikTok Ads one by one through Composio (hosted sign-in, then back to the wizard), select the ad accounts and X3 backfills 90 days of spend. X3 only reads – it never changes anything in your ad accounts. See Composio setup.

The step also shows the recommended UTM templates per platform with copy buttons and where to paste them, and checks the UTM coverage of real paid clicks of the last 14 days (utm_partial, utm_missing, no_paid_traffic).

Conversions

Part A defines what is reported (conversion definitions, suggested from your goals). Part B configures where – Meta CAPI, LinkedIn CAPI, Google Ads (API or CSV feed) and TikTok. When step 4 has a connection for the platform, "Verbindung aus Schritt 4 verwenden" needs no token at all. Secrets are stored encrypted and never shown again.

"Test-Event senden" sends one synthetic conversion in the provider's test mode (Meta: test event code; Google Ads: validate only; LinkedIn: configuration check) and shows the provider's answer – see Test modes. A destination in test mode keeps the warning mode_test until you click "Live schalten".

Part C creates a server key (events:write, leads:write, conversions:write) when your server has to report or confirm conversions.

Connect Claude

Two cards: claude.ai (custom connector with the MCP URL, OAuth sign-in – choose only this project and "Nur lesen" for analysis) and Claude Code (secret key or keyless sign-in, see Claude Code prompts). The function test lists the last MCP calls for the project ("Claude Code · Schlüssel …", tool, ✓/✗); paste the prompt "Verbindung testen" into Claude and the call appears within seconds.

Team

Invite colleagues or your agency with a role (Admin, Analyst, Leser) – optional; mark the step as done with "Fertig".

Claude Code prompts

The install step's tab "Mit Claude Code einrichten" (and step 6) offers ready-made, project-specific prompts to paste into Claude Code inside your website repository:

KindDashboard labelMCP promptWhat Claude does
install"SDK einbauen"/mcp__x3__installdetects stack and hosting, installs the SDK (+ proxy), connects the cookie banner, checks SPA page changes and CSP, runs the function test
events"Ereignisse & Ziele"/mcp__x3__eventsmaps forms, CTAs, bookings, WhatsApp/phone/mail links, videos and funnels to events and goals
server_conversions"Serverseitige Conversions"/mcp__x3__server_conversionsreports confirmed leads/bookings/purchases from your server with event-ID deduplication
mcp_analysis"Analyse mit Claude"/mcp__x3__analysisread-only analysis of channels, campaigns, pages and tracking quality
troubleshoot"Fehlersuche"/mcp__x3__debug_trackingfinds out why no or incomplete data arrives
connection_test"Verbindung testen"/mcp__x3__connection_testthree-line connection check

Get them from the dashboard tab, as MCP prompts (rendered without project data – Claude reads it via setup_status), with the tool setup_get_prompt or with GET /v1/projects/{project}/setup/prompts/{kind}. Prompts never contain a secret; values that members can edit (project, goal and domain names) appear only inside a fenced x3-daten block, and the prompt tells Claude to treat that block as data, not instructions.

Connect Claude Code (step 2 of the Claude tab). Organization admins create a key for Claude Code ("Schlüssel für Claude Code erzeugen"); it is shown once – never put it into the repository or a chat. Recommended variant, which keeps the key out of the shell history (paste the key at the hidden prompt):

read -rs X3_KEY && claude mcp add --transport http --scope local x3 https://api.x3.example/mcp --header "Authorization: Bearer $X3_KEY"; unset X3_KEY
claude mcp get x3
  • --scope local – only when Claude Code runs in this folder (install work, the default in the install tab); --scope user – every folder (analysis, the default in step 6). Never --scope project: it would write .mcp.json with the key into the repository.
  • Keyless alternative: claude mcp add --transport http --scope local x3 https://api.x3.example/mcp, then /mcp → x3 → "Authenticate" in Claude Code. On the consent page choose only this project; "Read only" is enough for analysis, installing needs "Read and write" (which can also change conversions and ad settings).
  • Setups registered under the old name 3x keep working – the prompts look for setup_status on either server.

The Claude Code key is least-privilege: analytics:read, config:write, conversions:read, ads:read. It deliberately cannot purge data, change destinations, roll or revoke keys, change domains, allowed origins or the consent rules (mode, Do-Not-Track/GPC, granted/denied values), send provider test events or create proxy keys – the prompts ask you to do those in the dashboard. Details: Claude Code.

Proxy key ownership

The first-party proxy's key (X3_PROXY_KEY) is always created by a person in the dashboard ("Proxy-Schlüssel erzeugen") – never by Claude or an OAuth app. A key minted through a Claude key or an app sign-in is derived from it and stops working when its parent expires (Claude keys: 90 days) or is revoked; proxied tracking would then fail with 403. The onboarding prompts therefore call setup_generate_snippet with mint_proxy_key: false and ask you to create the key yourself. Existing derived keys show the warning proxy_key_derived: create a new key in the dashboard, set it at your hosting, then revoke the old one.

Test visits and conversions

Visits opened with the test parameter (?x3_test=1) are marked as test traffic. They count for the setup checks (first pageview, hosts, custom events, goals seen) and are visible in tools with include_test: true; reports, UTM coverage and upload counts exclude them. Conversions from test sessions are stored with isTest and skipped by providers ("test session") unless the destination is in test mode; web-triggered conversions from test sessions are rejected with test_session while no destination is in test mode. Both are correct behaviour, not errors.

Which conversions need server code

The prompt "Serverseitige Conversions" sorts every conversion definition into one of three groups:

GroupDefinitionServer code
A – browser, automatictrigger goal or event, no server confirmationnone – an extra server report would be a duplicate
B – browser, server confirmstrigger goal/event with requireServerConfirmationconfirm with conversions.create({definition, sessionId}) without an own eventId
C – servertrigger lead, or source api without a web triggerleads.create / conversions.create with a stable eventId

See Conversion definitions and the server SDK.

API

All routes are project-scoped, tagged "Setup" in the API reference, and work with a dashboard session, a secret key or an OAuth token:

RouteExample
setup.statusGET /v1/projects/acme%2Fshop/setup/status → steps, installation, goals, ads, conversions, ai, team
setup.progressPATCH /v1/projects/{project}/setup with {"steps": {"ads": "skipped"}} (or "open" to resume)
setup.installationGET /v1/projects/{project}/setup/installation?since=2026-10-15T10:00:00Z → live panel data
setup.promptsGET /v1/projects/{project}/setup/prompts?locale=en → the six prompt kinds
setup.promptGET /v1/projects/{project}/setup/prompts/install?stack=next-app&proxy=auto&locale=de
setup.testConversionPOST /v1/projects/{project}/setup/test-conversion with {"provider": "meta", "testEventCode": "TEST123"}
setup.mcpActivityGET /v1/projects/{project}/setup/mcp-activity?limit=5 → last MCP calls
setup.detectStackPOST /v1/projects/{project}/setup/detect-stack with {"url": "https://shop.example.com"}

setup.prompt for install, events and server_conversions needs config:write or project:admin; the other kinds need analytics:read. Website checks and stack detection are limited to 10 per minute and project, test conversions to 6.

The same is available over MCP: tools setup_status, setup_get_prompt, setup_verify_installation, conversions_send_test (MCP tools) and the resources x3://setup/checklist.md, x3://setup/prompts/{kind}, x3://projects/{project}/setup-status (MCP resources).

Reason codes

Every open item of setup.status carries one of these codes. The dashboard, the MCP tool output and this page use the same texts.

Step group: Project

only_localhost

The project only knows localhost as a domain. Fix: Add the real domain of your website.

On screen: "Das Projekt kennt nur localhost als Domain." → "Füge die echte Domain deiner Website hinzu."

origin_missing

https://{host} is not an allowed origin – visits from there are rejected. Fix: Click “Add” to allow https://{host}.

On screen: "https://{host} ist nicht als Herkunft erlaubt – Besuche von dort werden abgelehnt." → "„Hinzufügen“ klicken, um https://{host} zu erlauben."

timezone_utc

The project's time zone is UTC. Fix: Is UTC right? Day boundaries in reports follow it.

On screen: "Die Zeitzone des Projekts ist UTC." → "Stimmt UTC? Tagesgrenzen in Berichten richten sich danach."

Step group: Install tracking

no_pageview

No pageview has arrived yet. Fix: Add the code, publish and click “Send test visit”.

On screen: "Noch kein Seitenaufruf angekommen." → "Code einbauen, veröffentlichen und „Testbesuch senden“ klicken."

stale_data

No data for more than 7 days (last {lastAt}). Fix: Check that the code is still on the website (“Check website now”).

On screen: "Seit über 7 Tagen keine Daten (zuletzt {lastAt})." → "Prüfe, ob der Code noch auf der Website ist („Website jetzt prüfen“)."

origin_rejected

Visits from {host} were rejected ({hits}×) – the origin is not allowed. Fix: If {host} belongs to you, click “Allow” under “Live verification → Domains seen”; otherwise ignore it. “Allow” adds exactly the origin the browser sent (scheme and port included).

On screen: "Besuche von {host} wurden abgelehnt ({hits}×) – die Herkunft ist nicht erlaubt." → "Gehört {host} dir, unter „Live-Prüfung → Domains gesehen“ auf „Erlauben“ klicken; sonst ignorieren."

quota_exceeded

The quota is exhausted ({reason}) – new data is dropped. Fix: Raise the quota or wait for the next period.

On screen: "Das Kontingent ist erschöpft ({reason}) – neue Daten werden verworfen." → "Kontingent erhöhen oder auf den nächsten Zeitraum warten."

ingest_paused

Data collection for this project is paused. Fix: Switch it back on in the project settings.

On screen: "Die Datenannahme für dieses Projekt ist pausiert." → "In den Projekteinstellungen wieder einschalten."

sdk_outdated

Outdated SDK version {seen} (current {latest}). Fix: Reload the script tag or update the npm package.

On screen: "Veraltete SDK-Version {seen} (aktuell {latest})." → "Script-Tag neu laden bzw. das npm-Paket aktualisieren."

verify_failed

The last website check failed (“{failed}”, the readable name of the failed check). Fix: Apply the hint of the failed check and click “Check website now” again.

On screen: "Die letzte Website-Prüfung ist fehlgeschlagen („{failed}“)." → "Den Hinweis bei der fehlgeschlagenen Prüfung umsetzen und „Website jetzt prüfen“ erneut klicken."

Mode “require”: nobody consented in 24 h – the cookie banner is probably not connected. Fix: Connect the banner (choose the CMP or call x3("consent", "granted")).

On screen: "Modus „require“: In 24 h hat niemand eingewilligt – der Cookie-Banner ist wohl nicht angebunden." → "Banner anbinden (CMP wählen oder x3("consent", "granted") aufrufen)."

proxy_ip_hidden

The proxy hides the visitor IP – countries and bot detection suffer. Fix: Set the proxy key in the proxy so the real IP is passed on.

On screen: "Der Proxy verdeckt die Besucher-IP – Länder und Bot-Erkennung leiden." → "Proxy-Schlüssel im Proxy setzen, damit die echte IP weitergegeben wird."

proxy_key_derived

Your proxy key depends on a Claude key or an app sign-in and stops working when that expires or is revoked. Fix: Create a new proxy key in the dashboard, set it at your hosting, then revoke the old one.

On screen: "Dein Proxy-Schlüssel hängt an einem Claude-Schlüssel oder einer App-Anmeldung und hört auf zu funktionieren, wenn diese abläuft oder widerrufen wird." → "Im Dashboard neuen Proxy-Schlüssel erzeugen, im Hosting eintragen, dann den alten widerrufen."

Step group: Goals

no_conversion_goal

No conversion goal yet. Fix: Choose at least one goal such as “Form submitted” or “Meeting booked”.

On screen: "Noch kein Conversion-Ziel angelegt." → "Wähle mindestens ein Ziel wie „Formular abgeschickt“ oder „Termin gebucht“."

goal_not_seen

Goal not reached in the last 24 h. Fix: Trigger the action once in a test visit.

On screen: "Ziel in den letzten 24 h nicht erreicht." → "Im Testbesuch die Aktion einmal auslösen."

event_blocked

The event {event} is blocked and dropped. Fix: Unblock it under Settings → Events.

On screen: "Das Ereignis {event} ist blockiert und wird verworfen." → "Unter Einstellungen → Ereignisse wieder freigeben."

plugin_off

The plugin “{plugin}” is off – the event is not measured. Fix: Switch the plugin on under Settings → Tracking.

On screen: "Das Plugin „{plugin}“ ist aus – das Ereignis wird nicht gemessen." → "Plugin unter Einstellungen → Tracking einschalten."

Step group: Ad platforms

composio_unavailable

Ad accounts are not set up on this installation (Composio is missing). Fix: Ask your administrator to set the Composio variables.

On screen: "Werbekonten sind auf dieser Installation nicht eingerichtet (Composio fehlt)." → "Bitte deine:n Administrator:in, die Composio-Variablen zu setzen."

platform_unavailable

The Composio configuration for {platform} is missing. Fix: Create the platform's auth config in Composio.

On screen: "Für {platform} fehlt die Composio-Konfiguration." → "Auth-Konfiguration für die Plattform in Composio anlegen."

connection_initiated

Sign-in at {platform} not completed. Fix: Click “Reconnect”.

On screen: "Anmeldung bei {platform} nicht abgeschlossen." → "„Erneut verbinden“ klicken."

connection_expired

The connection to {platform} has expired. Fix: Click “Reconnect”.

On screen: "Die Verbindung zu {platform} ist abgelaufen." → "„Neu verbinden“ klicken."

connection_failed

The connection to {platform} failed. Fix: Click “Reconnect” and complete the sign-in.

On screen: "Die Verbindung zu {platform} ist fehlgeschlagen." → "„Neu verbinden“ klicken und die Anmeldung vollständig abschließen."

no_accounts_selected

Connected, but no ad account selected yet. Fix: Select accounts – the sync starts afterwards.

On screen: "Verbunden, aber noch kein Werbekonto ausgewählt." → "Konten auswählen – danach startet die Synchronisierung."

sync_running

The sync is running. Fix: Wait a few minutes.

On screen: "Die Synchronisierung läuft." → "Ein paar Minuten warten."

sync_error

The last sync failed ({errorKind}). Fix: Reconnect or start the sync again.

On screen: "Die letzte Synchronisierung ist fehlgeschlagen ({errorKind})." → "Neu verbinden oder die Synchronisierung erneut starten."

no_spend

Synced, but no spend in 30 days. Fix: Normal for paused accounts – otherwise select the right account.

On screen: "Synchronisiert, aber keine Ausgaben in 30 Tagen." → "Für pausierte Konten normal – sonst das richtige Konto auswählen."

utm_partial

Only {coverage} % of ad clicks carry UTM parameters. Organic Facebook links also carry fbclid; small gaps are normal. Fix: Add the UTM template to every campaign.

On screen: "Nur {coverage} % der Anzeigenklicks haben UTM-Parameter. Organische Facebook-Links tragen auch fbclid; kleine Lücken sind normal." → "UTM-Vorlage in allen Kampagnen eintragen."

utm_missing

Most ad clicks carry no UTM parameters ({coverage} %). Fix: Copy the UTM template and enter it in the platform.

On screen: "Die meisten Anzeigenklicks haben keine UTM-Parameter ({coverage} %)." → "UTM-Vorlage kopieren und in der Plattform eintragen."

no_paid_traffic

No ad clicks in the last 14 days. Fix: Coverage appears here once ads are running.

On screen: "Keine Anzeigenklicks in den letzten 14 Tagen." → "Sobald Anzeigen laufen, erscheint die Abdeckung hier."

unmatched_utms

{count} UTM combinations match no campaign. Fix: Review them under Ad costs → Unmatched.

On screen: "{count} UTM-Kombinationen passen zu keiner Kampagne." → "Unter Werbekosten → Nicht zugeordnet prüfen."

Step group: Conversions

no_definitions

No conversion definition yet – nothing is reported. Fix: Apply a suggestion from your goals.

On screen: "Noch keine Conversion-Definition – es wird nichts gemeldet." → "Vorschlag aus deinen Zielen übernehmen."

destination_disabled

{provider} is switched off. Fix: Switch the destination on.

On screen: "{provider} ist ausgeschaltet." → "Ziel einschalten."

secrets_missing

Credentials missing: {names}. Fix: Enter the credentials or “Use the connection from step 4”.

On screen: "Zugangsdaten fehlen: {names}." → "Zugangsdaten eintragen oder „Verbindung aus Schritt 4 verwenden“."

settings_invalid

Invalid setting: {path}. Fix: Correct the field and save.

On screen: "Einstellung ungültig: {path}." → "Feld korrigieren und speichern."

mode_dry_run

Dry run – nothing is sent to the platform. Fix: Switch to test mode or live after testing.

On screen: "Trockenlauf – es wird nichts an die Plattform gesendet." → "Nach dem Test auf Testmodus oder Live stellen."

mode_test

Test mode – does not count at the platform. Fix: After a successful test, go live now.

On screen: "Testmodus – zählt nicht bei der Plattform." → "Nach erfolgreichem Test jetzt live schalten."

test_missing

Not tested yet. Fix: Click “Send test event”.

On screen: "Noch nicht getestet." → "„Test-Event senden“ klicken."

test_failed

The last test failed ({status}). Fix: Read the platform's answer, check the credentials and test again.

On screen: "Der letzte Test ist fehlgeschlagen ({status})." → "Antwort der Plattform lesen, Zugangsdaten prüfen und erneut testen."

upload_errors

{count} uploads failed in 30 days, none succeeded. Fix: Look at the error under Conversions and check the credentials.

On screen: "{count} Uploads in 30 Tagen fehlgeschlagen, keiner erfolgreich." → "Unter Conversions die Fehlermeldung ansehen und Zugangsdaten prüfen."

Step group: Claude and permissions

no_mcp_calls

Claude is set up but has not called X3 yet. Fix: Paste the prompt “Test the connection” into Claude.

On screen: "Claude ist eingerichtet, hat X3 aber noch nicht aufgerufen." → "Prompt „Verbindung testen“ in Claude einfügen."

mcp_errors

Every Claude call of the last 30 days failed. Fix: Read the error in the function test; renew the key or sign-in.

On screen: "Alle Claude-Aufrufe der letzten 30 Tage sind fehlgeschlagen." → "Fehlertext im Funktionstest lesen; Schlüssel oder Anmeldung erneuern."

no_permission

You lack the permission for this. Fix: Ask an admin to complete this step.

On screen: "Dafür fehlt dir die Berechtigung." → "Bitte eine:n Admin, diesen Schritt zu erledigen."