Skip to content
Docs

Delegated Identity & Auto Create Users

Most integrators hit the same wall: their product calls AgentWatch on behalf of many employees, but their backend cannot hold one API key per person. Delegated identity is the answer — one service credential, per-request attribution to a real, governed employee.

Because a shared key collapses everyone into a single row. Usage Insights would show one actor, budgets would be pooled, and a DLP finding could not be traced to a person. The alternative — one ak_ key per employee — gives perfect attribution but forces your backend to store and transport N secrets, which most security reviews refuse.

Delegated identity is the third option: the credential carries authority, the header carries identity.

Mark an API key as a service key, then name the end user on each request:

POST /v1/chat/completions
Authorization: Bearer ak_<your one service key>
X-Zscaler-User: john.doe@acme.com ← who this call is for

AgentWatch resolves that address to an employee inside the service key’s own organization and attributes the request to them. The header name is configurable (USER_HEADER); X-AgentWatch-User is always accepted as an alias so an API integration never has to send a vendor-branded header.

Your product asserts who the user is, and AgentWatch trusts that assertion because your backend is the authenticated party holding the service key. This is the same trust model as a Zscaler or Netskope proxy stamping the corporate user on outbound traffic — which is why the header is named the way it is. Keep the service key server-side; never ship it to a browser or desktop client.

Two guarantees hold regardless of what the header says:

  1. The organization comes from the key, never from the header. A delegated request is resolved strictly inside the service key’s organization, so one customer’s key can never attribute usage to another customer’s employee.
  2. Delegation moves the whole governance stack, not just the label. Rate limits, budgets, team and DLP policy for a delegated request are the named employee’s, because every downstream component reads the same resolved tenant.
Key type Behavior
Personal (default) The key is one employee. It cannot act for anyone else — naming a different address returns 403 and raises an audit alert
Service — collect all May act for employees. With no header, the request is attributed to the service account itself, so telemetry is never dropped
Service — require identity May act for employees, and rejects any request that does not name one (401)
Service — Auto Create Users As above, and creates the named employee on first sight (see below)

Every key is personal until an administrator changes it, so adopting this changes nothing about an existing integration until you ask it to.

By default a service key can only act for employees who already exist; an unknown address is refused, because attributing usage to someone with no record would produce data nothing downstream can govern.

That is the right default and the wrong one when your product is the system of record for who the users are. With Auto Create Users enabled, the first time a service key names a new address, AgentWatch creates that person as a real employee — their own gateway key, budgets, rate limits, team and policy — and serves the request attributed to them. No pre-enrolment step, no second secret.

It also completes an account that could already sign in but was never given a gateway identity, which is common for users provisioned by SSO before a gateway rollout.

It is off by default and deliberately bounded, because the identity header is an arbitrary string on a credential your whole backend shares:

Bound Behavior
Opt-in A key that has not enabled it behaves exactly as before
Domain allowlist Only addresses on a listed domain, or its subdomains. An empty list creates nothing
Service keys only A personal key can never reach this path
Organization from the key A created employee always lands in the service key’s organization
Team inherited They join the service account’s own team, falling back to your default team — so their spend rolls up where that integration’s spend already does
Plan seat limit Enforced exactly as in the employee wizard; the gateway is not a side door around your user limit
Rate cap 50 new employees per organization per hour; beyond that, 429
Never administrators An address resolving to an admin or super-admin login is refused
Never pending SSO users Someone an administrator deliberately left awaiting approval stays pending
No login created The account has no usable password — it is a governed gateway identity, not a sign-in. Reset the password if that person should also get console access

Every creation is written to the audit log, and refusals name the specific bound that was hit rather than a generic error, so an administrator is not left guessing.

Yes, if you need it. Your end users will not all share one domain — contractors, consultants, agency staff and personal addresses legitimately appear in a tenant’s traffic, and they cannot always be enumerated in advance. A tenant administrator can select Auto Create Users (any domain) for a key in their own organization, which removes the domain allowlist and nothing else: every other bound above still applies.

This is the same authority you already hold over your own employee list — an SSO provider configured to auto-create users with no domain restriction provisions anyone who authenticates through your identity provider. Setting the option on a key in a different organization still requires a platform super-admin.

1. Create a service account. In the console, go to Employee Management and click Add Service Account Key. Give it a name and an email-shaped identifier (svc-acme-prod@yourcompany.com). It appears as an ordinary row whose key is already marked as a service key, so it has the same rotate / suspend / resume lifecycle as any employee.

Or over the API — first get a tenant-admin token:

Terminal window
curl -s -X POST https://<your-agentwatch-host>/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@yourcompany.com","password":"…","slug":"<your-org-slug>"}'
# → {"token":"eyJhbGciOi…", …}

2. Copy its API key from the API Key column (the copy button reveals the full ak_… value) and give it to your backend. This is the only secret your product ever holds.

3. Classify the key. Use the Key Type dropdown on that row, or:

Terminal window
curl -s -X PUT https://<your-agentwatch-host>/admin/keys/identity-policy \
-H "Authorization: Bearer <tenant-admin JWT>" \
-H 'Content-Type: application/json' \
-d '{
"tenantId": "<the service account row id>",
"keyType": "service",
"attributionMode": "attribute-to-key",
"label": "acme-prod backend"
}'

4. Send a request on someone’s behalf.

Terminal window
curl -s -X POST https://<your-agentwatch-host>/v1/chat/completions \
-H "Authorization: Bearer ak_<service key>" \
-H "X-Zscaler-User: john.doe@acme.com" \
-H 'Content-Type: application/json' \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"hello"}]}'

5. Confirm the attribution. Open Usage Insights → Audit Trail and filter by that employee. The row should show john.doe@acme.com as the actor, with tag chips identity_mode=delegated and identity_via=<service key id>. If it shows the service account instead, the header did not arrive — see the table below.

Set the Key Type dropdown to Service — Auto Create Users, and enter the domains you own when prompted. The allowed domains appear as an editable chip on the row, so what a key may create is visible without opening a config file.

Terminal window
curl -s -X PUT https://<your-agentwatch-host>/admin/keys/identity-policy \
-H "Authorization: Bearer <tenant-admin JWT>" \
-H 'Content-Type: application/json' \
-d '{
"tenantId": "<the service account row id>",
"keyType": "service",
"attributionMode": "attribute-to-key",
"unknownIdentity": "auto-create",
"autoCreateDomains": ["acme.com", "acme.co.uk"]
}'

For users on domains you cannot list, use "autoCreateAnyDomain": true instead of autoCreateDomains (see above). Send a request naming a brand-new address, then look in Employee Management — that person now exists, with their own ak_ key and the service account’s team.

To review what every key in your organization is allowed to do:

Terminal window
curl -s https://<your-agentwatch-host>/admin/keys/identity-policy \
-H "Authorization: Bearer <tenant-admin JWT>"

Every rejection names the specific cause. This table is the fastest path from a response body to the fix:

Status code Cause Fix
403 identity_not_permitted The key is still personal and named someone else Classify it as a service key (step 3)
401 identity_unknown The named user is not provisioned in this organization Provision them, or enable Auto Create Users
401 identity_required The key is require-identity and no header was sent Send the identity header, or switch to attribute-to-key
403 identity_cross_org The named user belongs to a different organization Attribution never crosses organizations — check the key
403 identity_not_provisioned The user can sign in but has no gateway identity (common for admin and SSO-only accounts) Provision them, or enable Auto Create Users
403 identity_suspended The named user is suspended or disabled Re-enable them, or stop sending their traffic
403 identity_auto_create_refused + reason: not-allowlisted Auto Create Users is on, but that domain is not listed Add the domain, or use any-domain mode
403 identity_auto_create_refused + reason: seat-limit Your plan’s user limit is reached Upgrade the plan, or remove unused employees
403 identity_auto_create_refused + reason: privileged The address resolves to an administrator Administrators are never auto-created — provision explicitly
429 identity_auto_create_refused + reason: rate-limited More than 50 new employees in an hour Bulk-provision with SCIM or CSV, then retry
400 AUTO_CREATE_REQUIRES_DOMAINS auto-create was sent with no domain list Name the domains, or set autoCreateAnyDomain: true

Rejections are also recorded in Settings → Audit Logs and in the identity alert feed:

Terminal window
curl -s https://<your-agentwatch-host>/admin/alerts/identity \
-H "Authorization: Bearer <tenant-admin JWT>"

Almost always the identity header is not reaching the gateway. Check, in order:

  1. Is the key a service key? A personal key ignores the header entirely (and returns 403 if it names anyone else).
  2. Is the header name right? It is X-Zscaler-User unless your deployment set USER_HEADER. X-AgentWatch-User is always accepted — try it as a control.
  3. Is something stripping it? A CDN, load balancer or SDK that forwards only known headers will drop a custom one. Confirm by sending the same request directly to the gateway.
  4. One address per request. A comma-separated list is a single string and will never resolve.

Every delegated request is tagged so Usage Insights can answer “everything my backend sent” without any custom reporting:

  • identity_mode — personal, service, or delegated
  • identity_via — the service key that made the call
  • identity_auto_created — present on the first request of an auto-created employee

Rejected assertions — impersonation attempts, unknown or cross-organization addresses, suspended users — are recorded durably in Settings → Audit Logs and surfaced in the identity alert feed, with a throttled webhook for the security events.

Can different agents get different DLP or guardrail policies?

Section titled “Can different agents get different DLP or guardrail policies?”

Yes. Policy belongs to the organization, and a service key only ever acts inside its own organization — so create one tenant per policy group and issue each group’s agents a service key from its tenant. Step by step: Can different users or agents have different DLP and guardrail policies?