Errors
Every error response of the REST API is an RFC 9457 problem details document with content type
application/problem+json:
{
"type": "https://api.3x.example/v1/docs/api/errors#insufficient_scope",
"title": "Insufficient scope",
"status": 403,
"detail": "this key lacks conversions:write",
"code": "insufficient_scope",
"required": ["conversions:write"],
"requestId": "0f6c2d0e-…"
}- Branch on
code– it is stable.titleanddetailare for humans and may change. typelinks to the matching section of this page.requestIdis also sent as thex-request-idheader; quote it when you report a problem. You may send your ownx-request-id(8–64 characters[A-Za-z0-9_-]) to correlate logs.- Validation errors carry
errors: [{path, message}].
Two endpoints are deliberately different: the tracking endpoint POST /v1/e and the conversion
beacon POST /v1/cb answer with bodyless status codes (204, 400, 403, 413, 429), because
browsers send them with sendBeacon and never read a body. See Ingest protocol.
Codes
bad_request
400. The request is syntactically wrong in a way validation cannot describe – malformed JSON, an invalid cursor, contradictory parameters. Fix the request; retrying the same request will fail again.
validation_error
422. Parameters, query string or body do not match the schema of the endpoint. errors lists
every problem with its path (body.scopes.0, query.preset). Unknown query parameters are
validation errors too, so a typo never silently widens a report.
unauthorized
401. No credentials were sent, or they were sent in the wrong place. Send
Authorization: Bearer <key>; the response carries WWW-Authenticate: Bearer. On /mcp the header
also names the OAuth resource metadata, which is how Claude starts its sign-in.
invalid_token
401. The credential is malformed, has a wrong checksum, is unknown, expired or revoked. Create a new key; if the key was rolled, switch to the new value.
forbidden
403. The credential is valid but the action is not allowed for this kind of principal – for
example a secret key calling a user-only endpoint (POST /v1/orgs), a derived key creating keys, or
a narrowed key requesting org:admin.
insufficient_scope
403. The credential has access to the organization or project but lacks a scope. required
lists the missing scopes. Create a key with these scopes (Scopes and roles) or,
for OAuth, reconnect and approve the additional permission.
sudo_required
403. A dashboard session tried a sensitive action (keys, destinations, members, deleting a
project) without re-authenticating in the last 15 minutes. Call POST /v1/auth/sudo with the
password (or a fresh magic link) and repeat the request. Secret keys are never asked for sudo.
not_found
404. The resource does not exist or you have no access to it – 3x does not reveal the existence of organizations and projects you cannot see. Also returned for unknown routes.
conflict
409. The request collides with the current state: a slug that is already taken, a resource that
was changed concurrently, an Idempotency-Key reused with a different body.
last_owner
409. The change would leave the organization without an owner (demoting or removing the last owner). Make someone else owner first.
gone
410. The resource existed but cannot be used any more – an accepted, revoked or expired invitation, a used magic link, an ad-platform connect link older than ten minutes.
payload_too_large
413. The body exceeds the endpoint's limit: 16 KB for tracking batches, 4 KB for conversion beacons and 64 KB for MCP requests.
unsupported_media_type
415. Wrong content-type. JSON endpoints need application/json; the tracking endpoint accepts
text/plain (to avoid CORS preflights).
rate_limited
429. Too many requests. Wait for the number of seconds in Retry-After. JSON endpoints that
apply a limit also send RateLimit and RateLimit-Policy headers. Limits: Rate limits.
not_configured
503. The deployment lacks something the endpoint needs – no database URL, no Composio API key for ad connections, no SMTP for e-mails. The operator has to set the environment variable.
db_unavailable
503. The database is unreachable or waking up (Neon cold start). Retry with backoff. 3x never answers 401 in this case, so OAuth clients such as Claude keep their connection.
upstream_error
502. An external service failed: Composio, an ad platform, or the site fetched by
setup_verify_installation. detail names the service; provider error texts are sanitized.
not_implemented
501. The endpoint exists in the API contract but is not available on this deployment (for example a feature disabled by the operator).
internal
500. An unexpected error. Nothing about it is leaked in the response; the operator finds it in
the logs by requestId. Retrying is safe for GET requests and for requests with an
Idempotency-Key.