# Error codes

Errors come back as `{"error":"code"}`, sometimes with details such as the limit reached. The dashboard turns them into plain sentences; this list is for scripts, logs and support.

## Any request

| Code | Status | Meaning |
| --- | --- | --- |
| `json_required` | 415 | Send `Content-Type: application/json`. |
| `invalid_json` | 400 | The body is not a JSON object. |
| `request_aborted` | 400 | The client stopped sending the body. |
| `body_too_large` | 413 | The body is over 16 KB. |
| `rate_limited` | 429 | Too many requests. The body’s `retryAfter` and the `Retry-After` header say how many seconds to wait. See [Limits](https://clitrail.com/docs/limits). |
| `authentication_required` | 401 | Sign in first. |
| `same_origin_required` | 403 | Dashboard writes must come from the service’s own origin. |
| `secrets_require_https` | 403 | Credentials are accepted only over https. See [In transit](https://clitrail.com/docs/secrets#in-transit). |
| `not_found` | 404 | No such endpoint. |
| `service_not_configured` | 503 | The service is missing configuration it needs to run safely. |
| `internal_error` | 500 | An unexpected failure. It is recorded without bodies or secrets. |

## Plans and limits

| Code | Status | Meaning |
| --- | --- | --- |
| `plan_limit` | 402 | A plan limit is reached. The body names it: `{"error":"plan_limit","limit":"websites","max":1,"plan":"free"}`. |
| `plan_required` | 402 | A paid feature on Free: destinations, attribution export, log streams on plans that allow none. |
| `install_path_limit` | 402 | An install path past the plan’s limit, with the same body as `plan_limit`. |
| `organisation_limit` | 409 | You already own 5 organisations or belong to 20. |

## Collection

| Code | Status | Meaning |
| --- | --- | --- |
| `unknown_project` | 404 | No website has this ID. |
| `page_or_origin_not_allowed` | 403 | The page is not on a registered origin, or its URL-ID does not match. |
| `invalid_url` | 400 | The page URL is not an http or https URL. |
| `invalid_receipt` | 400, 403 | The receipt is malformed, unknown, expired, revoked or from another origin. |
| `native_endpoint` | 403 | `/v1/events` was called from a browser. |
| `invalid_event_identity` | 400 | `event_id` or `installation_id` is not 8 to 128 allowed characters. |
| `invalid_event_type` | 400 | Unknown event type. |
| `invalid_receipts` | 400 | `receipts` is not a list of at most 64 receipts. Checked only where attribution runs. |
| `invalid_handoff` | 400 | The handoff token is malformed. |
| `mixed_attribution_evidence` | 400 | Receipts and a handoff token in one report. |
| `event_id_conflict` | 409 | The event ID was already used for another installation or event type. |
| `install_path_not_found` | 404 | The hook address names an install path the website’s organisation does not have. |

## Sign-in and account

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_token` | 401 | The sign-in token (Google or email link) was not accepted: expired, from another project or another sign-in provider, or the account is disabled or its sessions revoked. Sign in again. |
| `auth_unavailable` | 503 | Google’s keys could not be reached. Try again in a minute. |
| `email_required` | 403 | The sign-in carried no email address. |
| `email_not_verified` | 403 | The email address you signed in with is not verified: a Google account without a verified address, or a Firebase password account that never verified its address. Sign in with an email link to that address instead. |
| `account_linked_elsewhere` | 409 | This email already belongs to a CLItrail account that signs in as a different Firebase user. Sign in the way you did before. |
| `recent_sign_in_required` | 403 | Sign in again (Google, or a new email link) to confirm this action. |
| `session_not_found` | 404 | That session has already ended. |
| `transfer_ownership_first` | 409 | You are the last owner of an organisation others belong to. |

## Organisations and members

| Code | Status | Meaning |
| --- | --- | --- |
| `organisation_not_found` | 404 | No such organisation, or you are not in it. |
| `forbidden` | 403 | Your role cannot do this. `required` names the role that can. |
| `invalid_organisation_name`, `invalid_role`, `invalid_email` | 400 | A field is invalid. |
| `last_owner` | 409 | The last owner cannot leave, be removed or lose the role. |
| `last_organisation` | 409 | You cannot delete your only organisation. |
| `member_not_found` | 404 | No such member. |
| `already_member` | 409 | That address is already a member. |
| `invite_not_found` | 404 | The invitation link is not valid. |
| `invite_used`, `invite_revoked`, `invite_expired` | 410 | The link was used, revoked or is older than 7 days. Ask for a new one. |
| `invite_email_mismatch` | 403 | You are signed in with another address than the one invited. |

## Websites and install paths

| Code | Status | Meaning |
| --- | --- | --- |
| `project_not_found` | 404 | No such website in your organisations. |
| `invalid_project_name` | 400 | Names are 1 to 80 characters. |
| `invalid_website`, `invalid_additional_origin`, `invalid_additional_origins` | 400 | A website URL is invalid. |
| `invalid_attribution_policy`, `invalid_reconstruction`, `invalid_providers`, `invalid_retention_days`, `invalid_project_settings` | 400 | A website setting is out of range. |
| `domain_change_requires_paused_destinations` | 409 | Pause live ad-platform destinations before changing the primary website. |
| `invalid_install_path_name`, `invalid_install_path_type` | 400 | An install path’s name or type is invalid. |
| `duplicate_install_path` | 409 | The organisation already has a path with this name. |
| `install_path_required` | 409 | An organisation keeps at least one install path. |

## Destinations

| Code | Status | Meaning |
| --- | --- | --- |
| `destination_missing_credentials` | 400 | Save every credential before going live. |
| `consent_confirmation_required` | 400 | Confirm the platform’s terms before going live. |
| `domain_verification_required` | 409 | [Verify the domain](https://clitrail.com/docs/domain-verification) before going live. |
| `destination_not_ready` | 400 | The destination cannot send yet; the message says what is missing. |
| `unsupported_event` | 400 | `install_started` for a destination other than a webhook. |
| `duplicate_destination` | 409 | A destination for the same target already exists. |
| `destination_not_found` | 404 | No such destination on this website. |
| `invalid_destination_type`, `invalid_destination_config`, `invalid_destination_name`, `invalid_events`, `invalid_mode`, `invalid_include_probable`, `invalid_consent_confirmation`, `invalid_rotation` | 400 | A field of the request is invalid. |
| `invalid_secrets`, `invalid_secret`, `unknown_secret_field`, `secret_is_generated`, `no_generated_secret` | 400 | A credential field is invalid, unknown, or generated by CLItrail and cannot be set. |
| `invalid_measurement_id`, `invalid_session_timeout`, `invalid_ga_secret` | 400 | GA4 settings. |
| `invalid_service_account_key`, `invalid_google_customer_id`, `invalid_conversion_action_id`, `invalid_login_customer_id`, `invalid_linked_customer_id`, `google_consent_choice_required`, `invalid_google_access` | 400 | Google Ads settings. |
| `google_partner_not_configured` | 400 | This service is not a Google data partner, so data partner link access is not offered. Use your own [service account key](https://clitrail.com/docs/google-ads). |
| `google_partner_link_unconfirmed` | 400 | Create the data partner link in Google Ads, then tick **I linked CLItrail as a data partner in Google Ads**. |
| `invalid_pixel_id`, `invalid_meta_access_token`, `invalid_api_version`, `invalid_ldu_option` | 400 | Meta settings. |
| `invalid_pixel_code`, `invalid_tiktok_access_token` | 400 | TikTok settings. |
| `invalid_x_pixel_id`, `invalid_x_event_id`, `x_event_pixel_mismatch`, `invalid_x_click_window`, `invalid_x_consumer_key`, `invalid_x_consumer_secret`, `invalid_x_access_token`, `invalid_x_access_token_secret` | 400 | X Ads settings. |
| `invalid_event_name`, `invalid_conversion_value`, `invalid_currency` | 400 | Event names, value or currency. |
| `invalid_webhook_url`, `webhook_requires_https`, `webhook_private_address`, `invalid_signing_secret` | 400 | Webhook settings. |

Delivery problems do not surface as HTTP errors: they appear on the destination’s health, on each delivery and in [Logs](https://clitrail.com/docs/logs).

## Billing

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_plan` | 400 | Choose `standard` or `enterprise`. |
| `billing_customer_required` | 409 | The billing portal needs a Stripe customer: upgrade first. |
| `subscription_active` | 409 | Cancel the subscription first. |
| `billing_failed` | 502 | Stripe refused the request. |
| `billing_not_configured`, `billing_unavailable` | 503 | Billing is not available right now. Nothing was charged. |

## Logs and log streams

| Code | Status | Meaning |
| --- | --- | --- |
| `log_not_found` | 404 | The record is older than the organisation’s retention, or not in it. |
| `invalid_log_filter`, `invalid_time_range`, `invalid_cursor`, `invalid_export_format` | 400 | A filter, time range, page cursor or export format is invalid. |
| `log_stream_not_found` | 404 | No such log stream in the organisation. |
| `invalid_log_stream_type`, `invalid_log_stream_name`, `invalid_log_stream_status`, `invalid_log_stream_url` | 400 | A log stream field is invalid. |
| `invalid_s3_bucket`, `invalid_s3_region`, `invalid_s3_prefix`, `invalid_s3_endpoint`, `invalid_s3_path_style`, `invalid_s3_access_key_id`, `invalid_s3_secret_access_key`, `log_stream_missing_credentials` | 400 | S3 settings or keys. |

## Support

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_support_topic`, `invalid_support_message`, `invalid_support_diagnostics` | 400 | Choose a topic, write a message, and send diagnostics as text. |
| `support_message_too_long` | 400 | The message is longer than `max` characters (4,000). |
| `support_diagnostics_too_large` | 413 | The diagnostics are larger than `max` bytes (16 KB). |
| `support_request_not_found` | 404 | No such support request. |
