Developers › Ingestion API

Ingestion errors & limits

Developer Reference Ingestion errors & limits Every refusal the ingestion, identity and profiles endpoints return, and the rate limits behind the 429. Every error is RFC 9457 problem+json with a stable code: branch on the code, never on the sentence. OAuth and MCP refusals use a different dialect and live on their own page.

Error format

Error format Every error is RFC 9457 problem+json (RFC 9457 obsoletes RFC 7807 — same media type, current number). 422 response Content-Type: application/problem+json { "type": "https://evas.live/problems/processor-unsupported", "title": "Processor Unsupported", "status": 422, "code": "PROCESSOR_UNSUPPORTED", "detail": "Field 'processor' must be a strict integer, one of: 0 (AUTOMATION_ONLY), 1 (IDENTITY_ONLY), 2 (REALESTATE), 3 (LIFE_INSURANCE_US). Strings and floats are not accepted.", "supportedProcessors": [ { "mode": 0, "label": "AUTOMATION_ONLY" }, { "mode": 1, "label": "IDENTITY_ONLY" }, { "mode": 2, "label": "REALESTATE" }, { "mode": 3, "label": "LIFE_INSURANCE_US" } ], "requestId": "req_… " } Read supportedProcessors, not the sentence On PROCESSOR_REQUIRED and PROCESSOR_UNSUPPORTED the body carries supportedProcessors — every mode this server accepts, as {mode, label} , generated from the same registry the validator uses. Branch on that array and you cannot drift out of step with us when a new line of business registers. The detail sentence above is an example , not a fixture: it is generated per request and grows a new mode the day one is registered. Branch on code , never on detail . The status and the code are the contract; the sentence is written for a human reading a log and is rewritten whenever it can be made clearer. Status code When 401 UNAUTHENTICATED Missing, malformed, or unknown API key. 403 TENANT_SUSPENDED The key or workspace is suspended. 403 PLAN_UPGRADE_REQUIRED Processor 0 or 1 requested without the Unlimited plan. 403 IDENTITY_API_NOT_ENABLED Identity resolution is not switched on for this workspace. 415 UNSUPPORTED_MEDIA_TYPE Body wasn't application/json . 422 ENVELOPE_INCOMPLETE sessionId missing, empty, too long, or has disallowed characters. 422 PROCESSOR_REQUIRED / _UNSUPPORTED processor missing, or not one of the modes this workspace is offered. The 422 body carries supportedProcessors — branch on that, never on this sentence. 429 RATE_LIMITED Too many notifications for this key — honour Retry-After . 404 SESSION_NOT_FOUND Status requested for a sessionId this WORKSPACE hasn't notified — the scope is the workspace, not the key, so any active key can poll any of its sessions. Also returned once a failed session passes its retention window and is deleted, so a 404 is never proof the session was never delivered. 401 KEY_EXPIRED The key was valid and has passed its expiry. 403 TENANT_MISMATCH The key does not belong to the workspace that owns the resource. 400 MALFORMED_BODY The body was not parseable JSON. 413 PAYLOAD_TOO_LARGE The request exceeded the size cap for this endpoint. Identity and Profiles add their own, on top of every row above: Status code When 403 PROFILES_API_NOT_ENABLED The Customer Profiles API is not switched on for this workspace. 404 UNKNOWN_PROFILE The profileId tuning profile does not exist. 404 PROFILE_MERGE_NOT_FOUND Status polled for a merge requestId this WORKSPACE did not create — the scope is the workspace, not the key, so any active key can poll any of its requests. 422 SESSION_ID_MISSING A current or candidates[n] entry has no non-empty sessionId. 400 INSUFFICIENT_EVIDENCE_FOR_LOOKUP Identity lookup only: you sent nothing searchable. An integration bug — not the verdict INSUFFICIENT_EVIDENCE , which is a real answer about a real comparison. 422 MALFORMED_EVIDENCE An evidence field is present with the wrong shape, too long, or carrying control or injection characters — detail names the exact path. 422 CANDIDATES_NOT_ARRAY candidates was not an array. 422 TOO_MANY_CANDIDATES More candidates than the endpoint accepts — 50 on identity v1 , 20 on v2 , 50 on a profile merge. Bounding and recency-ordering the set is the caller's job. Two 403s that look alike and are not PLAN_UPGRADE_REQUIRED means the capability exists and your plan does not carry it — an upgrade fixes it. IDENTITY_API_NOT_ENABLED and PROFILES_API_NOT_ENABLED mean the API is off for your workspace regardless of plan and someone at LeadLogiX has to switch it on. Retrying either is pointless; they are configuration, not capacity.

Rate limits

Rate limits Per API key, not per workspace — two keys get two budgets. Endpoint Default limit On refusal POST /api/v1/sessions 60 per minute 429 RATE_LIMITED with Retry-After in seconds Identity resolve, v1 and v2 120 per minute 429 RATE_LIMITED with Retry-After POST /api/v1/identity/lookup , GET /api/v1/leads/{leadId}/context and GET /api/v1/profiles/{profileId} 120 per minute, SHARED 429 RATE_LIMITED — one budget across all three, separate from resolve. A returning-customer handshake is a lookup plus one read-back, so plan for 60 handshakes per minute, not 120. POST /mcp 600 per minute Per authorization — see OAuth & MCP errors Polling counts against the same budget as notifying A status poll is a request. Sixty per minute is comfortable for one session polled every couple of seconds, and it is not comfortable for thirty sessions polled that way at once — that is 900 requests a minute. Poll each session on its own backoff, or notify a batch and sweep their statuses on a slower interval. Honour Retry-After rather than retrying immediately. It carries the remaining window in seconds, so a client that waits for it recovers on its first attempt instead of spending its next budget on refusals.