LeadRails

Error types

Every non-2xx response from the LeadRails v1 API is an RFC 9457 problem document with a stable type URL. The type URLs are the machine-readable identifier — pin against them in your client code. The title field is human-readable copy and may change.

Shape of a problem response

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "https://docs.leadrails.dev/errors/not-found",
  "title": "Resource not found",
  "status": 404,
  "detail": "No source with id src_01J5Z7N9X3M2VWPQ9YTBN2F0HR in this workspace.",
  "instance": "/v1/sources/src_01J5Z7N9X3M2VWPQ9YTBN2F0HR",
  "request_id": "req_01J5Z7T7M3M2VWPQ9YTBN2F0HR"
}

Always include request_id when reporting an issue. It is the single fastest way for us to find your request in the logs.

All problem types

Each row is anchored — link directly to https://docs.leadrails.dev/errors/<slug> and you'll land on the right row.

Slug / status When Fix
Invalid API key
invalid-api-key
HTTP 401
The Authorization header is missing, malformed, or carries a revoked / unknown key. Confirm the header looks like `Authorization: Bearer lr_live_<rest>`. If it does, the key is revoked or wrong — generate a new one at Settings → API keys.
Resource not found
not-found
HTTP 404
The resource ID is well-formed but does not exist in your workspace. Cross-workspace IDs look identical to missing IDs and resolve here. Verify the ID by listing the parent collection. If the ID came from another workspace, you cannot use it here.
Invalid pagination cursor
invalid-cursor
HTTP 400
The `cursor` query parameter could not be decoded. Cursors are opaque — only pass back what the API returned in `next_cursor`. Drop the `cursor` parameter to start from the beginning, or pass the exact value from a previous `next_cursor` response.
Invalid query parameter
invalid-parameter
HTTP 400
A query parameter failed validation — out of range, wrong type, or in an unexpected format. Check the parameter against the schema in the API reference. The `detail` field names the offending parameter.
Invalid reference
invalid-reference
HTTP 400
A POST/PATCH body references an ID (source_id, destination_id, ...) that exists but belongs to a different workspace, or does not exist at all. Re-fetch the IDs from the corresponding list endpoint. Make sure every referenced resource lives in the same workspace as the API key.
Invalid destination adapter type
invalid-adapter-type
HTTP 400
The `adapter_type` field on a destination create/update is not one of the known adapter slugs. Pick a supported `adapter_type` from the API reference (`slack_webhook`, `generic_webhook`, `n8n_webhook`, `housecall_pro`, `gohighlevel`, ...).
Idempotency-Key header required
idempotency-key-required
HTTP 400
POST or PATCH was called without an `Idempotency-Key` header. The header is required on every state-changing request. Generate a fresh UUID per logical operation and send it as `Idempotency-Key: <uuid>`. Retries with the same key + body return the original result.
Idempotency-Key too long
idempotency-key-too-long
HTTP 400
The `Idempotency-Key` header exceeds 255 characters. Use a UUID (36 chars) or a short opaque token. Keys longer than 255 bytes are rejected to bound storage.
Idempotency-Key conflict
idempotency-conflict
HTTP 422
The same `Idempotency-Key` was reused with a different request body. The first body is held for 24 hours; a second request with that key must match byte-for-byte. Generate a new `Idempotency-Key` for the changed request. Reuse the original key only when retrying the exact same body.
Plan required
plan-required
HTTP 403
The endpoint requires a Pro+ plan and the API key belongs to a Starter workspace. `/v1/events` is the current Pro+ gate. Upgrade in the dashboard under Billing. The same data is available in the in-app Events view on every plan.
Rate limit exceeded
rate-limit-exceeded
HTTP 429
Per-key rate limit was hit. The `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` response headers describe the window. Back off until `RateLimit-Reset` seconds have passed, then retry. Honor `Retry-After` on 429s. For sustained throughput, contact us — we can lift the limit per key.
Test-mode key not available
test-mode-not-available
HTTP 501
A key with the `lr_test_` prefix was used. The `lr_test_` namespace is reserved for a future test-mode tier that has not shipped. Use a live key (`lr_live_<rest>`) from your workspace. Test-mode arrives in a future release; this slug is the forward-compat placeholder.
Unsafe outbound URL
unsafe-url
HTTP 422
A destination config (webhook_url, callback_url, ...) points at a private network, an unsupported scheme, or otherwise fails the safe-outbound-URL check. The check runs at write time AND at delivery time. Use a public HTTPS URL. Private IPs (10.x, 192.168.x, 127.x, etc.), link-local addresses, and non-HTTP(S) schemes are all rejected.
Internal server error
internal-error
HTTP 500
The API hit an unexpected exception. The `request_id` field in the response is the key to look up the request in logs. Retry with backoff. If it persists, email support@leadrails.dev with the `request_id` — that's the fastest path to a root cause.

Versioning

The type URLs are stable identifiers and will not change for the v1 surface. New problem types may be added; existing slugs will not be repurposed. If a slug is ever retired, this page will keep redirecting to a successor and the API will emit the successor going forward.