Delivery status of campaigns, ad sets and ads

Every synced campaign, ad set and ad carries the platform's own status (Meta effective_status, Google ENABLED/PAUSED/REMOVED, LinkedIn, TikTok). X3 normalizes it per level:

StatusLabelExamples of platform values
activeAktivMeta ACTIVE, Google ENABLED, TikTok ENABLE
pausedPausiertMeta/Google PAUSED
paused_parentPausiert (Kampagne / Anzeigengruppe)Meta CAMPAIGN_PAUSED, ADSET_PAUSED; an active ad in a paused campaign
in_reviewIn PrüfungMeta PENDING_REVIEW, IN_PROCESS, PREAPPROVED
rejectedAbgelehnt / eingeschränktMeta DISAPPROVED, WITH_ISSUES; Google approval DISAPPROVED, limited
learning / learning_limitedLernphase / Lernphase eingeschränktMeta learning stage, Google primary status LEARNING
endedBeendetbudget used up, schedule over, LinkedIn COMPLETED
draftEntwurfnot started yet
deletedGelöscht / archiviertMeta ARCHIVED/DELETED, Google REMOVED
unknownUnbekanntanything new

Paused applies to children: the effective status of an ad in a paused campaign is paused_parent with the level that paused it. Deleted parents make children deleted.

Where you see it

The ads report (GET /v1/projects/{project}/ads/report, MCP ads_performance) returns per row status (own), effective_status, status_parent_level, status_since and status_raw (the platform's value, shown in the tooltip). GET /v1/projects/{project}/ads/entities returns statusNorm, effectiveStatus, statusParentLevel, statusSince and statusRaw (status stays the raw value). GET /v1/projects/{project}/ads/status-history lists every change (newest first, filter by platform, level, entity).

Filters

status (repeat or comma-separate effective statuses), quick and hide_deleted:

quickkeeps
active_or_spendactive entities plus every paused one with spend in the period (the dashboard default – „inkl. 6 pausierte mit Ausgaben“)
activeactive, learning and in review
with_spendeverything with spend in the period
alleverything

hide_deleted defaults to on whenever a quick filter is given. Totals never change with the filter: spend and leads of paused or deleted entities stay in the sums. The report answers pausedWithSpend – the number of rows kept only because of their spend.

Freshness

Statuses update with every entity sync (every 6 hours) and with the lightweight status check ads.status every hour (read-only status queries; never a change at the platform). Pausing, removing or a rejection writes an annotation of kind ad_status into the time series and re-checks the project's tasks.

Why a status is „Unbekannt“

Rows and entities carry status_reason (report) / statusReason (entities) when X3 cannot say more:

ReasonMeaning
no_permissionthe platform refused the status read (403) – every entity of the account shows „Unbekannt“ until the account is reconnected with read access
missing_fieldthe platform sends no status for this level
not_listedthe last complete status listing no longer returned the entity – it counts as deleted, its history and numbers stay
not_connectedthe campaign is only known from spend or links of an ad account X3 is not connected to (it is labelled as such, never shown by id)
check_failedthe last status check failed (rate limit, 5xx, timeout) – the last known status is shown

The default quick filter active_or_spend keeps unknown rows: X3 cannot tell that they stopped.

The hourly status check

ads.status lists campaigns, ad sets and ads per account with status fields only (Meta effective_status, ad set learning_stage_info and end_time; Google status, primary_status, primary_status_reasons and the ad's policy_summary; LinkedIn campaign group, campaign and creative status with the creative review; TikTok switch and secondary status), at most 25 list pages per account and 60 per project and run. Entities missing from a listing are only marked deleted when the listing reached its end within that budget. Each account keeps the outcome of its last check (statusCheck in GET /v1/projects/{project}/ad-accounts: ok, rate_limited, error, no_permission, expired, with retryAt); after a failure the account is checked again after 1 h, 2 h, 4 h and then every 6 h – never sooner than the platform's Retry-After. Statuses that only changed because a parent changed (an ad of a paused campaign) write no own annotation; a campaign that runs again writes „wieder aktiv“.

Background runs of ad accounts

Connecting an account starts one ads.backfill run per account, „Jetzt abgleichen“ (POST /v1/projects/{project}/ads/sync) one ads.sync_now run per account (runs in the answer; see background runs). The run follows the account's sync windows („Abgleich läuft · 2 von 5 Zeiträumen“), is idempotent while it is active and ends with connection_expired („Meta-Verbindung abgelaufen · neu verbinden“) when the platform refuses the token. Account rows name who connected them (connectedBy, connectedVia, connectedAt); the internal connection id is only part of the details.