# How delivery works

A destination receives your attributed installs. You set each one up by pasting the platform’s own credentials: there is no “connect” sign-in. Every destination starts in test mode, shows you the exact payload it would send, and goes live only when you say so.

| Destination | Status | What it receives | You provide |
| --- | --- | --- | --- |
| [Google Analytics 4](https://clitrail.com/docs/ga4) | Available | A `cli_install_completed` or `cli_first_run` event on the visitor's GA4 client ID | Measurement ID, API secret |
| [Google Ads](https://clitrail.com/docs/google-ads) | Available | An offline click conversion through the Data Manager API | Service account key (JSON), customer ID, conversion action, consent signals |
| [Meta](https://clitrail.com/docs/meta) | Available | A Conversions API event with `fbc` and `fbp` | Pixel ID, access token |
| [TikTok](https://clitrail.com/docs/tiktok) | Available | An Events API web event with `ttclid` and `ttp` | Pixel code, access token |
| [X Ads](https://clitrail.com/docs/x-ads) | Available | A Conversions API conversion with `twclid` | Pixel ID, event ID, consumer key and secret, access token and secret |
| [Webhooks](https://clitrail.com/docs/webhooks) | Available | A signed JSON event for every install, attributed or not | An https URL |

## Plans

Destinations are part of the Standard and Enterprise plans. On Free they cannot be created, tested or switched live (`402`, `plan_required`), and nothing reported while on Free is ever sent later. A website can have up to 10 destinations on Standard and 20 on Enterprise. After a downgrade, destinations over the new limit are paused, whatever their own mode, until the owner picks which to keep; nothing is deleted. See [Plans and limits](https://clitrail.com/docs/plans).

## Modes

| Mode | What happens |
| --- | --- |
| Test | The default. Each new install gets its payload prepared and listed under **Install events** with the status `dry_run`. Nothing is sent. |
| Live | New installs are queued and sent. |
| Paused | New installs are not queued. Anything already queued waits until the destination is live again. |

Going live applies to installs recorded from then on. Test-mode payloads and installs from before the destination existed are never sent later.

## Send a test

**Send test** checks the credentials you saved without sending a real install:

- **GA4** sends a sample event to GA4's validation server. GA4 checks the event format but does not confirm API secrets.
- **Google Ads** sends a validate-only request, which checks access to the account and conversion action. Nothing is recorded.
- **Meta** and **TikTok** need the test event code from the platform's Events Manager and send an event named `CLItrailConnectionTest`.
- **X Ads** sends a signed request with an empty list of conversions: X checks the credentials, and nothing can be recorded. X has no test events, so read the prepared requests under **Install events** in test mode.
- **Webhooks** send a signed `test.ping` and show the status code your endpoint returned.

## Go live

**Go live** is refused until the destination can deliver:

| Requirement | Applies to | Error when missing |
| --- | --- | --- |
| A Standard or Enterprise plan, within its destination limit | All | `plan_required`, `plan_limit` |
| Every credential saved | All; a webhook’s signing secret is generated for you | `destination_missing_credentials` |
| You confirm the platform's terms and your notice and consent duties. The time and your email are recorded. | Google Ads, Meta, TikTok, X Ads | `consent_confirmation_required` |
| The website's primary domain is [verified](https://clitrail.com/docs/domain-verification) | Google Ads, Meta, TikTok, X Ads | `domain_verification_required` |

## Which installs are sent

- **Events.** Each destination sends the event types you tick. Ad platforms default to completed installs; GA4 and webhooks also send first runs. `install_started` is an attempt, not a conversion: only webhooks receive it, and the other destinations refuse it (`unsupported_event`).
- **Resolution.** GA4 and the ad platforms receive matched installs. They receive [reconstructed](https://clitrail.com/docs/reconstruction) (probable) installs only when you turn on **Include reconstructed installs** on that destination. Webhooks receive every install with its resolution.
- **Global Privacy Control.** Google Ads, Meta, TikTok and X Ads never receive a touch recorded under [GPC](https://clitrail.com/docs/sdk#global-privacy-control).
- **Identifiers.** Each platform needs its own ID on some touch of the journey: a GA4 client ID for that measurement ID, a Google click ID or session attributes, `fbc` or `fbp`, `ttclid` or `ttp`, a `twclid`. Without one, nothing is queued for that platform. Each platform follows your [attribution policy](https://clitrail.com/docs/attribution-policies): its most recent identifier under latest visit, its earliest under first visit. A click is dated by its own evidence (the visit whose URL carried it, the `_gcl_aw` or `_fbc` cookie time, Google's session start or the TikTok click date), so a cookie re-read on a later visit never outranks a newer click on another domain.

## Retries and time limits

| Destination | Per request | Retries | Expires | Deduplicated on |
| --- | --- | --- | --- | --- |
| GA4 | 1 | Never. An unknown outcome becomes `uncertain`. | 72 hours after the install | not applicable |
| Google Ads | up to 2,000 | Up to 8 attempts | 30 days after the install, or 90 days after the click | `transactionId` |
| Meta | up to 1,000 | Up to 8 attempts, within 48 hours | One hour short of 7 days after the install | `event_id` and `event_name`, 48 hours |
| TikTok | up to 50 | Up to 8 attempts, within 48 hours | 7 days after the install | Pixel, event name and `event_id`, 48 hours |
| X Ads | up to 500 | Up to 8 attempts, within 48 hours | 90 days after the install | `conversion_id` against your X pixel, 48 hours |
| Webhooks | 1 | Up to 10 attempts | 3 days after the install | `webhook-id` |

Retries wait 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours, 12 hours, then 24 hours, or longer when the platform sends `Retry-After` (up to a day). Every retry reuses the same event ID, so Google Ads, Meta, TikTok and your webhook endpoint can ignore a copy they already have. Meta and TikTok retries stop before their 48-hour deduplication window closes, checked when each batch is sent; a resend that could arrive at 48 hours or later is marked `uncertain` instead of risking a double count. X Ads retries stop at the same 48 hours, but X documents deduplication only against its pixel, not between two API sends, so X Ads resends only requests X answered with an error or never answered.

- When a platform refuses the whole request, CLItrail resends the events one at a time so one bad event cannot block the others (Google Ads, Meta, TikTok, X Ads).
- When a platform refuses a credential, the events stay queued within the platform's own time limit and are tried about once an hour; refusals do not use up attempts or start the deduplication window. Replacing the credential or correcting the account or pixel resumes delivery at once. The destination's health shows the error.
- A send interrupted by a restart or crash is retried for Google Ads, Meta, TikTok and webhooks, which deduplicate it, and for X Ads within its 48 hours, where such a resend can count twice; it becomes `uncertain` for GA4. A Node server that is shutting down starts no new batch and waits for the requests in flight.
- Destinations are delivered side by side, each one run at a time, so a slow endpoint or a large backlog holds up only its own queue. An endpoint that does not answer holds its queue for a minute, or its `Retry-After`.
- A webhook that answers `410 Gone` pauses its destination; `401`, `403`, `404`, `408`, `429` and `5xx` are retried. The change is recorded in the audit log.
- Redirects are never followed; a 3xx answer counts as a failed delivery.

X Ads follows its own rules, on the [X Ads](https://clitrail.com/docs/x-ads) page.

## Delivery statuses

| Status | Meaning |
| --- | --- |
| `dry_run` | Prepared in test mode. Never sent. |
| `pending` | Queued for a live destination. |
| `sending` | Being sent now. |
| `retry` | A temporary failure; another attempt is scheduled. |
| `submitted` | The platform accepted the request. For GA4 this is an HTTP success, not confirmed processing. |
| `rejected` | The platform refused it. The error is shown. |
| `uncertain` | The outcome is unknown and a resend could count twice (GA4, or past a platform's deduplication window), so it is not resent. |
| `expired` | Too old for the platform before it could be sent. |
| `failed` | All attempts used on temporary failures, or the queued event could not be read. A refused credential never uses up attempts. |
| `cancelled` | The destination was removed before sending. |

Each destination card shows its health (`ok`, `error`, `auth_error`, or `never` before the first delivery), the last error with any secret removed, and counts per status for the last 30 days.

## Managing destinations

- One destination per target: one per GA4 measurement ID, Meta pixel, TikTok pixel, X pixel event, Google Ads conversion action or webhook URL (`duplicate_destination`).
- Members create and edit destinations; admins and owners remove them. See [roles](https://clitrail.com/docs/organisations).
- Credentials are write-only and kept in a separate secret store. See [Security and secrets](https://clitrail.com/docs/secrets).
- Every request sent to a platform is recorded in [Logs](https://clitrail.com/docs/logs) with its exact body, credentials redacted.
- Removing a destination deletes its credentials and cancels everything it had not sent.
- Every change is recorded in the website's audit log in **Settings**, without secret values.
