Authentication

The project key, the acting user, and how the platform keeps each project's and user's conversations apart.

Every request carries the project's key and the user it acts for:

Authorization: Bearer <project key>
X-User-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
X-User-Role: admin            (optional)

The client sends these for you from api_key and user_id (apiKey and userId in TypeScript).

The acting user is required

Unlike some services, the Agent Platform rejects a request with no X-User-Id, and the value must be a UUID. It becomes the owner of every thread created with it, so use the stable ID from your own user table, never an email address or a display name.

X-User-Role is optional and passed through to the agent's policies.

One client per user

The conversation half of the client is the LangGraph SDK, which fixes its headers when the client is built. So:

  • acting_as (Python) doesn't change the user for threads, runs, assistants or crons.
  • Build an AgentPlatformClient for each user you act for. Construction is cheap and makes no network call.
def agent_for(user) -> AgentPlatformClient:
    return AgentPlatformClient(
        base_url=settings.AICE_AGENT_URL,
        api_key=settings.AICE_API_KEY,
        user_id=str(user.id),
    )

How conversations are isolated

The platform stores every thread, run and saved item under an identity it computes itself from the verified key and header:

identity = "{project id}:{user id}"

Nothing in a request body can change it. The consequences:

  • threads.search() returns only the calling user's threads. There's no parameter to widen it.
  • Reading another user's thread returns 404, not 403. The platform won't even confirm that the ID exists.
  • The same user ID in two projects is two different people, even in the same organization. Agents, skills and usage are per project too.

What the platform checks

CheckOn failure
Authorization: Bearer … is present401 missing bearer token
the key's signature verifies against the console's public keys (RS256 only)401 invalid token
issuer matches, and the key's services include the Agent Platform401 invalid token
the key hasn't expired (30 s clock leeway)401 token expired
it's a project key, naming a project and an organization401 not a project key / 401 invalid token
X-User-Id is present and a UUID401 missing X-User-Id / 401 naming the header
the console's public keys could be fetched503: the check fails closed

Only 401 token expired is worth handling at runtime: regenerate the key in the console. If every call is 401 invalid token, check that the project enables the Agent Platform and that the key was made after it was enabled. Every other 401 means the key or headers are wrong, and retrying won't fix it.

Call it from your backend

Because the key opens everything in its project, it must never reach a browser. A web or mobile chat talks to your backend, which authenticates your user in its own way and then calls the platform with the key and that user's ID. Build a chat UI shows the full pattern.

A useful side effect: server-to-server calls don't involve CORS, so there's nothing to configure on the platform for your frontend's origin.

On this page