What end_user_id is
end_user_id is a required, top-level field that tells each::labs which of your end users a request is made for. Send it on every request that runs a model or workflow. It lets each::labs act on that one end user instead of your whole organization.
Send a stable, pseudonymous ID for one human end user: your internal user ID or a hash of it, never a new random ID per request. Use the same value every time you act for that person, and a different value for every other person. There are exactly two exceptions: a shared ID for one business customer that refuses to give you per-user IDs, and a job ID for an automated job with no person behind it. Never put an email address, a name, a phone number or an IP address in it, hashed or not. Code samples use usr_4f9a2c to stand for your real user’s ID; never hard-code it.
How to choose the value for your business and what happens when it is wrong: End-user ID requirements.
Where to send it
Sendend_user_id on every request that runs a model or a workflow, including automated jobs. In a JSON body it is always top-level, never inside input, messages or another request object. Transcriptions take it as a form field (see the table).
each::sense (
eachsense-agent.core.eachlabs.run) does not accept end_user_id yet. Do not send it there.
OpenAI’s user and safety_identifier fields are never read as end_user_id, and POST /v1/responses rejects both. Send end_user_id itself.
Format and limits
- 1 to 256 characters, counted after leading and trailing whitespace is trimmed. Keep the whole value, including any prefix of your own, to 240 characters or fewer: that is the safe maximum.
- Valid UTF-8, with no control characters such as tabs, line breaks or null bytes. Spaces and non-ASCII letters are allowed.
- JSON
null, an empty string or a whitespace-only string counts as missing, not as invalid. Sending any of these is the same as omitting the field. Never do it on purpose. - The string
"null"passes this check, but it is a placeholder: never send it (see Never send). - Any other non-string value, or an invalid string, is refused with
400and the messageend_user_id must be 1-256 printable characters. OnPOST /v1/prediction,detailsis the message string itself for this error, not an object with acode. On workflow triggers the body is{"error": "end_user_id must be 1-256 printable characters"}. OnPOST /v1/chat/completionsand the audio endpoints it iserror.message. OnPOST /v1/responsesandPOST /v1/decisions,error.messageisThe request is invalid.On all of themerror.codeisinvalid_request_error.
end_user_id.
When an end user or an organization is blocked
END_USER_BLOCKED(403) refuses every request that would create an execution (run a model or a workflow step) for that end user. Other end users in the same organization are unaffected.- An end-user block matches only requests that carry that
end_user_id. Sending a blocked user’s requests without the field, or under another ID, is an attempt to get around our child-safety checks: see What happens when it goes wrong. ORGANIZATION_BLOCKED(403) refuses every request that would create an execution in the organization, with or without anend_user_id. An organization block takes precedence over an end-user block.- A refused request does not run and is not billed.
- Both blocked codes return the same fixed message:
This request is not allowed under the each::labs usage policy.It never identifies the end user, the organization, or the reason. - When the platform recorded the refused execution, the response includes its
execution_id. - A block applies to new requests only. An audio stream that is already open is not cut when a block lands.
- A blocked request is terminal: retrying it does not clear the block. If you believe we blocked the wrong end user or organization, write to [email protected].
403 ORGANIZATION_BLOCKED.
Workflows
The workflow engine storesend_user_id on the run and copies it onto every step’s prediction, so a refusal on any step refers to the same end user.
A workflow trigger, bulk trigger or public workflow trigger creates a workflow execution, but no model execution. It is never refused for a block or a missing end_user_id: it returns its normal 202 response with the workflow execution_id, and the check runs when each step creates its model execution:
- The refused step fails. Every attempt in that run carries the same
end_user_id, so a retry or fallback of the step is checked the same way. - The workflow execution does not carry the refusal code. The failed step in
step_outputs, and the failed workflow execution, carry anerror_causethat names the class of refusal, not its exact cause:workflow prediction RPC PermissionDenied: the step was not allowed, for example because the end user or the organization is blocked. Treat it as final.workflow prediction RPC InvalidArgument: the step’s request was refused as invalid, for example becauseend_user_idis missing for an organization that requires it, or because a content-safety check blocked it. Check that the trigger carried a validend_user_idand that the workflow’s models are current; if both are fine, treat it as final.workflow prediction RPC Unavailable: a service, such as the safety controls, was temporarily unavailable. Trigger the workflow again later.
end_user_id is different: the trigger itself returns 400 (see Format and limits).
When safety controls are unavailable
SAFETY_BLOCKS_UNAVAILABLE (503) and SAFETY_PREFLIGHT_UNAVAILABLE (503) mean the safety controls could not answer. The request was not executed. Retry with exponential backoff; these are temporary outages, not blocks.
Missing end_user_id
Always send it, and treat a missing end_user_id as a bug in your code. Requests without it are not refused today, but any of them can be refused at any time with 400 END_USER_ID_REQUIRED: enforcement is switched on per organization, without notice. A workflow trigger is still accepted, and the step fails instead (see Workflows).
When the refusal is returned, the message is end_user_id is required for this organization.
Never send a placeholder such as anonymous, guest, unknown, null, none, 0, user, default or test: it identifies no one, and the End-user ID requirements treat it like a random ID.
Add end_user_id to every endpoint that creates an execution, not only the main prediction call: prediction requests, workflow triggers, the OpenAI-compatible endpoints and Decisions.
Content safety refusals
Content safety checks can refuse a request before any model runs. A blocked request returns422 SAFETY_PREFLIGHT_BLOCKED, usually with the message This request was blocked by Eachlabs content safety checks. A temporary outage returns 503 SAFETY_PREFLIGHT_UNAVAILABLE with Eachlabs content safety checks are temporarily unavailable. Please retry.
See the Error Reference for the response envelopes on each endpoint.
Handling a refusal
The codes on this page are part of the API contract now. The checks behind them are switched on without notice, so handle them today. Branch on the code, never on the message text. On prediction creation, readdetails.code; when details is a plain string (the invalid-value 400 in Format and limits), there is no code: fix the value. On POST /v1/decisions and the OpenAI-compatible endpoints, read error.code. In a workflow, read the failed step’s error_cause (see Workflows).
END_USER_ID_REQUIRED: addend_user_idand resend the request.END_USER_BLOCKED,ORGANIZATION_BLOCKED,SAFETY_PREFLIGHT_BLOCKED: do not retry.SAFETY_BLOCKS_UNAVAILABLE,SAFETY_PREFLIGHT_UNAVAILABLE: retry with backoff.