Leads, pipeline and revenue import
Every lead has a pipeline status. Moving a lead to appointment or customer can report a conversion with value to Meta, Google, LinkedIn or TikTok, so the platforms learn which campaigns bring paying customers – not just form fills.
Pipeline
| Status | Meaning |
|---|---|
new | Arrived, nobody has looked at it yet (the New counter in the dashboard). |
contacted | Somebody reached out. |
appointment | Appointment booked (appointmentAt optional). Can report the Appointment conversion. |
customer | Became a customer (value = revenue, optional). Can report the Customer conversion with value. |
lost | Not interested (lostReason optional). |
PATCH /v1/projects/{project}/leads/{id}/pipeline
{ "status": "customer", "value": 2400 }Scope leads:status; idempotent. The answer lists reported – the platforms that will receive a
conversion. Reporting uses the conversion definitions Appointment and Customer (trigger
{ "kind": "lead_status", "status": "appointment" | "customer" }); they are created switched off and
turned on in the reporting settings. If you do not want staff with leads:status to report revenue,
leave those definitions off. A report can be undone while it is still pending.
MCP: leads_update_pipeline.
Notes
GET|POST /v1/projects/{project}/leads/{id}/notes (scope leads:status, text up to 2,000
characters). The list also shows automatic entries – arrived, reported, status changes – which are
derived, not stored. MCP: leads_notes_list, leads_notes_create. Notes and status changes are
written to the audit log without their content.
Reporting a lead by hand
The lead page's card „An Werbeplattformen gemeldet“ shows per platform: „nicht beteiligt“ (the person never came through it), „nicht gemeldet · <reason>“, „ausstehend“, „gemeldet am …“ or „Testlauf (dry run)“ – a send with a test event code counts as a test run too. Rows that are not reported offer „An Meta melden“ (no conversion yet) or „Erneut senden“ (a conversion that ran as a dry run, was skipped or reached only the test events).
The dialog first shows the dry run – which definition, the event time and id, which match keys the
platform gets (e-mail, phone, Meta click …), until when it accepts the event (Meta and TikTok 7 days
minus an hour, Google and LinkedIn 89 days) and warnings („geht auch an Google Ads“, „im Parallelbetrieb
evtl. schon gemeldet“, „älter als 48 Stunden: kein Abgleich mit dem Pixel“, test mode) – then „Jetzt
melden“ sends. A new conversion uses the lead definition, else the manual-contact definition of the
lead's channel (event id lead:<lead>:<definition>); an existing one is re-sent to that platform only.
A platform that already has a real upload of the lead never gets a second one; too old events, a
destination that is off or in dry run and the counting rule block the send. Needs conversions:write
and leads:read (viewers see the state only); every send is in the audit log (lead.reported).
REST: POST /v1/projects/{project}/leads/{id}/report with {"provider": "meta"} returns the preview,
{"provider": "meta", "confirm": true} sends (409 conflict with blocked and text when it may not).
Marking a test lead
Your own test inquiries (for example a test of a lead magnet or a form) can be marked afterwards, also
when the lead has no visit (a server-side form). In the lead profile, project admins choose „Als Test
markieren“ under „Technische Details“; the reason is optional. With the API:
POST /v1/projects/{project}/leads/mark-test with leadIds (up to 200) and a reason; with Claude:
the MCP tool leads_mark_test.
- One-way and idempotent; nothing is deleted. Leads that already were test leads come back in
alreadyTest. - The lead no longer counts: lead figures, Pulse, portfolio, experiments, reports and the inbox (shown again with „Spam & Testdaten“ › „Testdaten anzeigen“, with the badge „Test“).
- Its visit becomes test traffic too, unless another real lead belongs to that visit (
visitsKept). The daily figures of the affected days are recomputed. - Open conversions of the lead (pending or held) are cancelled and never sent to an ad platform.
conversions.alreadySentlists the ones a platform already received – X3 cannot recall them. - CRM: open pushes are skipped like for every test lead; a lead already in the CRM stays there
(
crmLinked). Notifications are not sent again. - Every call is audited with the reason (
leads.marked_test).
What a lead looks like without contact data
Members and keys with leads:summary (every dashboard role has it) see a lead without contact data:
display.name– first name and initial ("Sandra K."), or "Anfrage #14" / "Inquiry #14" when no first name is stored;display.numberis a running number per project;display.answersShort– the quiz answers in one line;origin,firstTouch,visits,devices– where the lead came from (Pulse);contact– only whether e-mail and phone exist, never the values.
E-mail, phone, last name, company, message and free-text answers need leads:read (and, for dashboard
members, the personal-data flag). The dashboard never receives contact data in a page; it fetches it
on demand through POST …/leads/{id}/reveal-contact, which is audited as lead.pii_viewed.
Revenue import
Bring outcomes from your CRM or practice software in bulk – up to 500 rows per call, idempotent per row:
POST /v1/projects/{project}/leads/revenue
{
"rows": [
{ "leadId": "1b0e…", "status": "appointment", "ref": "crm-1001" },
{ "emailSha256": "5d41…", "status": "customer", "value": 2400, "at": "2026-10-14T10:00:00Z", "ref": "crm-1002" },
{ "phoneSha256": "9f86…", "status": "lost", "ref": "crm-1003" }
]
}Match a row by leadId or by the SHA-256 of the normalized e-mail (lower case, trimmed) or phone
(E.164). Each row comes back with result:
| Result | Meaning |
|---|---|
updated | Status (and value) changed; reported lists the platforms. |
unchanged | The lead already had this status and value. |
not_found | No lead matches. |
ambiguous | The hash matches more than one lead – send leadId. |
invalid | The row is not valid (message says why). |
value_too_high | The value is above valueLimit (100 × the project's leadValue.default, else 1,000,000) – check it and send "confirm": true. |
would_update | Dry run only: the row would change the lead (see preview). |
Send "dryRun": true to see first what would happen: nothing is written or reported, and every row
comes back with preview – the status change (from, to), the value and the providers that would
receive a conversion. Spam leads never match, and a spam lead cannot be set to appointment or
customer (422) – nothing about spam reaches the ad platforms.
Scope leads:status. MCP: leads_revenue_import – there dry_run is true by default; call it again
with dry_run: false after the user agreed to the preview.
Lead value
The cockpit's lead value uses real revenue where known plus open leads × estimated value × close rate.
Set the estimate per project (leadValue.default, optionally per quiz answer in leadValue.byAnswer)
and the close rate (closeRate, default 0.2) in the project settings.
Lead goal and projection
Set a target per month („Leadziel pro Monat“) and X3 shows whether you are on course – in the Leads KPI card on Pulse (a bar with the leads so far, the projected range hatched and the target as a tick) and under Leads › Ziel und Hochrechnung.
GET /v1/projects/{project}/lead-goal?month=2026-10
PUT /v1/projects/{project}/lead-goal {"month": "2026-10", "target": 25, "showInReport": true}
GET /v1/projects/{project}/lead-goal/projection?month=2026-10- The month counts every lead of the Leads list in that month, not only measured days. If leads existed before the measuring start without being counted, the copy says „seit …“ instead of „im Oktober“ until the first full month.
- Projection = leads so far + remaining days × rate. The rates of the last 7 and the last 14 days
give the range (
low,high), the midpoint is the „etwa“ value: „etwa 19 (17 bis 21)“. The answer also names what is needed per day for the target (neededPerDay,missing). - Never a projection without its range and never from fewer than 10 measured days in the month
(
available: false,availableFrom). target: nullremoves the goal. Reading needsanalytics:read, setting itconfig:write; MCPlead_goal_get,lead_goal_set(only with a number the user named) andleads_projection.
The same numbers show as a bar in the Pulse card „Leads“ (only with a target), as one line in the weekly report when „Im Wochenbericht zeigen“ is on („Im Oktober bisher 10 Anfragen, Ziel 25.“), and in the Leads card of the X3 App.
Waiting for an answer
Leads › Wartet auf Antwort lists inquiries that nobody has answered yet
(GET /v1/projects/{project}/leads-awaiting, leads:read or leads:summary; MCP leads_awaiting).
An inquiry waits when it is older than 24 hours (at most 30 days) and has
- no status after „Neu“ – set in the dashboard, in the app, through Claude, by a revenue import or by a connected CRM (every status change is written to the status history), and
- no note from your team (notes the CRM brings in count; a note the visitor left does not).
The first answer is whichever came first. GET /v1/projects/{project}/leads-response-time returns the
median time to the first answer and how many were answered the same day (last 30 days). The status
history starts when this feature was released; earlier answer times are not counted.
The page shows the inquiries oldest first with type, source, arrival, waiting time and grade, never names (they stay in the lead profile). Kontaktiert sets the status. Two reminders:
- Pulse shows the slot „Wartet auf Antwort“ (counts, type, grade, waiting time) while at least
one inquiry waits – per person and project, on by default (
preferences.pulseAwaiting). - Push after 24 hours, at most once a day per project (X3 App category
lead_waiting, off by default; only the count, never names).
Pulse and push stay silent until the project shows any answer (a status after „Neu“ or a team note in the last 90 days) – a project that never sets a status would otherwise see every inquiry as waiting.
Biggest gap on the way to an enquiry
GET /v1/projects/{project}/behavior/biggest-gap (MCP behavior_biggest_gap) picks the step with the
largest absolute loss among the quiz and form funnels with at least 100 visits on step 1 – for
example „Von 496 Besuchen, die das Quiz gesehen haben, haben nur 31 die erste Frage beantwortet.“ –
split by device and with a tip. Days with a data-quality annotation (for example a broken form) make
the steps after the first answer no benchmark; the answer names them in qualityNotes_de.
Related
- Traffic quality – seriously interested visits and leads per source
- Conversion definitions
- Privacy and GDPR