Tasks – everything X3 asks you to do
Alerts („Braucht Dich“, the bell), Pulse recommendations, personal notices, the digest and the MCP
tools share one model: a task is a condition about a subject. X3 keeps at most one task per
subject and key (for example budget_over:project:2026-10, connect_costs:meta_ads,
reporting_failed:meta) and renders its text from the current numbers on every read – a task
never shows a figure that was true an hour ago.
States
| State | Meaning | Comes back when |
|---|---|---|
open | the condition holds | – |
auto_resolved | the condition stopped holding – with reason, time and who triggered it („Von selbst erledigt: Budget auf 4.000 € angepasst · 14:05“) | the condition holds again → open, „Erneut aufgetreten“ (occurrence + 1, history kept) |
done | a person marked it done | only when it got materially worse (budget: +10 points) or after 7 days → open, „Trotz Erledigt-Markierung weiter aktuell“. State warnings (lead_unreported, funnel_step_zero, leads_zero, destination_not_live, ad_match_gap, ad_campaign_gap): done means „diesen Stand gesehen“ – they keep the acknowledged evidence (the lead ids, the zero run's first day, the mode, the cause) and open again only when new evidence shows up (another lead, a new zero run after a recovery, another mode or cause; ad_match_gap and ad_campaign_gap also at +20 points), never after the 7 days alone. The list marks them doneMeansSeen |
snoozed | „Später“ until a time (default tomorrow 08:00) | at that time – only if the condition still holds, else it resolves itself |
dismissed | „Passt nicht“ (optional reason) | when the context changes (new month, new campaign) or a person restores it |
Numbers use a band against flickering: budget_over opens at ≥ 103 % of the budget's own threshold and
resolves below 101 %; cpl_spike opens at +50 % and resolves below +40 % after a day. Reopening within
the 7-day cooldown sends no second mail.
Reading and acting
| Call | Scope | What |
|---|---|---|
GET /v1/tasks | analytics:read | your projects, organizations and personal tasks; scope=project|org|mine, state=open (default) |snoozed|done|auto_resolved|dismissed|resolved|all, type, kind, limit, cursor, resolvedSince (what resolved itself since then – with your own sign-in only what your changes triggered) |
GET /v1/projects/{project}/tasks | analytics:read | one project |
PATCH /v1/tasks/{id} | project tasks config:write or ads:write (recommendations ads:write), org tasks config:write, personal tasks you | {"action": "done" | "snooze" | "dismiss" | "restore", "until"?, "reason"?, "note"?} – restore checks the condition at once |
GET /v1/tasks/{id}/history | analytics:read | opened, resolved itself (reason, who), done, snoozed, dismissed, occurred again |
MCP: tasks_list and tasks_update; alerts_list / alerts_update remain for the alert part. The
answer of a change that resolves tasks names them – saving the monthly budget returns
resolvedTasks, so the dashboard can say „Damit ist ‚Budget überschritten' erledigt“.
Lists are triage-ordered (problem › attention › opportunity › setup › info, then money at stake,
then age), return counts per state for the bell and the „Erledigt“ tab, and mark each task live
(rendered from the current evaluation) or not (history of a subject that no longer exists).
Types
Alert rules (Recommendations and alerts) are project tasks
with the rule name as type. Recommendations are the type rec (key = the week-independent
recommendation key; „Passt nicht“ lasts until the month of the recommendation changes, „Verbinde
Plattform“ resolves itself as soon as an account of that platform is connected). Personal notices are
type connect_ai (scope person).
Who sees what follows the old rules: agency-internal types (budget, opportunity, connect_costs,
numbers_disputed) need ads:read + config:write; recommendations need ads:read (viewers only when
the project shows them recommendations).
Operational types
| Type | Opens when | Resolves itself when |
|---|---|---|
budget_unallocated | the platform split of this month's budget does not add up (at least 1 € and 0.5 %): „140 € nicht verteilt“ or „Aufteilung 200 € über dem Budget“ | the split adds up or is removed; the budget card and the budget sheet show the rest with „Rest verteilen“ (one platform, evenly, or without a split) |
ad_rejected | an ad of a connected account has the delivery status „abgelehnt“ (one task per ad) | the ad runs again or is removed |
campaign_paused_organic | a campaign has been paused for ≥ 3 days, spends nothing and still brings ≥ 2 leads per day (opens at 2.0, stays open down to 1.5 per day) | the campaign runs again, or fewer leads arrive for a day |
conversion_upload_failed | uploads to one destination failed in the last 24 hours (live or test mode; dry runs never) – covers „Meldungen stehen aus“ of the same provider | a later upload to it succeeds |
connect_google_ads, connect_meta_ads, connect_linkedin_ads, connect_tiktok_ads | paid visits of the platform without an account, or the account was disconnected („Verbinde Google Ads“); hidden while the alert connect_costs of the platform is open | an account of the platform is connected („Von selbst erledigt: Google Ads verbunden“) |
traffic_drop, traffic_spike | yesterday's visits left the band of the last 6 same weekdays (median ± 2.5 MAD, at least 20 expected; a drop also under 70 % of the median) – „Weniger Besuche als üblich: Gestern 62 Besuche, an Dienstagen sonst etwa 140. Am stärksten gefallen: Meta (−70).“ Quiet in the first days after the measuring start and while the tracking is down; off with settings.traffic.alerts=false (Visitors and traffic) | the next day is back inside the band |
setup_tracking, setup_goals, setup_reporting | the setup checklist rows „Einbau“, „Ziele“ and „Meldungen“ are open – so „Später“ and „Passt nicht“ work there too | the first visit arrives, a goal exists, reporting to the platform is set up |
Reporting and funnel warnings
Early warnings for leads that silently miss the ad platforms and for funnel steps that silently stop. All four are problems, send a notification when they open and resolve themselves.
| Type | Opens when | Resolves itself when |
|---|---|---|
lead_unreported | a counted lead of the last 7 days came through an ad platform (its click id or paid channel) whose reporting is switched on, but after 30 minutes it has no conversion or no upload to that platform („Lead von Meta nicht an Meta gemeldet“; one task per platform, the newest lead linked) | every such lead is uploaded (or reported earlier for the same person), or older than 7 days |
destination_not_live | a switched-on destination runs in dry run or test mode – also when the deployment caps it – while its platform spent money in the last 7 days | the destination is live, or the platform stops spending |
funnel_step_zero | a quiz step, CTA/WhatsApp/form/booking or a goal has been at 0 since a day although its baseline (7 days before) makes that improbable (≥ 3 expected, today counted by the share already over) while the step before it – or the visits – stays at ≥ 50 % of normal („Quiz-Antworten: 0 seit 18.10.“) | the step happens again |
leads_zero | no counted lead for days although the 14-day baseline expects ≥ 3 in that time and visits are normal; hides no_leads while open | a lead arrives |
Recommendations never ask you to scale an ad, ad set or campaign that is paused, rejected, ended or deleted: their task resolves itself with „Anzeige pausiert“ and opens again when the entity runs. Demo projects never raise these types.
When tasks are checked
Every change that can change a condition re-checks the affected types right away and records who triggered it: budget saved, ad account connected, confirmed or disconnected, sync finished, conversion uploaded or failed, preset applied, lead status changed, goal saved, ad delivery status changed (sync or the hourly status check), project settings changed. A job checks every active project every 15 minutes as a fallback (time-based conditions such as „no lead for 52 hours“, snoozes that ran out). Budgets and months are measured in the project's time zone.
In the dashboard
- Hinweise (
/cockpit/alerts): one table with the tabs Offen · Später · Erledigt · Abgelehnt, columns type, subject, since, occurrence („erneut aufgetreten“) and state – sortable, filterable, with chosen columns, saved views and CSV export. Erledigt says „Von selbst erledigt: … · 14:05 · ausgelöst von Anna“ or „Von Dir erledigt“; everything closed can be restored. - The bell and the counters refresh right after every write; when your own change resolved a task the dashboard confirms it („Damit ist ‚Budget überschritten‘ erledigt“). Every action has a 10-second undo.
- „Später“ offers tomorrow morning, Monday, in a week or a chosen day; „Passt nicht“ an optional reason and note. Viewers see the tasks without actions („Darum kümmern sich Inhaber und Admins.“).
For module authors
A module adds its own type with registerTaskType(def, facts) (apps/api/src/platform/task-registry.ts)
and fires taskEvent(sql, { projectId, kind, by }) after its writes – it never writes task rows
itself. Rows that still live in a module's own table (the behavior alert rules registered through the
insights insightExtensions hook) keep working: they appear in the task lists, the bell counters and
the Hinweise tabs, and „Erledigt“, „Später“ and „Zurückholen“ answer through PATCH /v1/tasks/{id}.