Token and claim reference
GenAuth's delegation model uses two kinds of token. Their roles are different and so are their field shapes — confusing the two is the single most common integration mistake:
| Delegate token | Access token (after exchange) | |
|---|---|---|
| What it is | An authorization record: who delegated what to which agent, and for how long | An access credential: what the agent actually presents to a resource |
| Who consumes it | GenAuth (for token exchange and validation) | Your target resource service |
| Where it comes from | Issued after consent completes (see Quickstart) | Exchanged from a delegate token (RFC 8693) |
Has act? | No | Yes (a GenAuth extension — see below) |
Both are JWTs (RFC 7519) and are verified the standard way.
Table 1: complete delegate token fields
A JWT whose token_type is eak_delegation_token. Thirteen top-level fields:
| Field | Type | Meaning | Contract level |
|---|---|---|---|
token_type | string | Always eak_delegation_token | Implementation detail — fine for recognizing the token kind, but the value may change across versions; do not hard-code an equality check as your only test |
iss | string | Issuer | Stable contract |
sub | string | The delegating user's ID — the subject of a delegate token is always a person | Stable contract |
azp | string | The access key ID that started the delegation | Stable contract |
agent_id | string | The agent the authority was delegated to | Stable contract |
scope | string[] | Granted scopes (formatted service.capability:action, e.g. webagent.web_search:run) | Stable contract |
aud | string | Always genauth:token-exchange — a delegate token is only accepted by the token exchange endpoint | Stable contract |
eak_tenant_id | string | The owning workspace ID | Stable contract |
genauth_userpool_id | string | The user pool the user belongs to | Implementation detail — this field is known to be subject to a rename; resource-side code must not depend on it. Treat the version noted on this page as authoritative |
resource_bindings | object | Product resources bound to the workspace (e.g. { gumem: { project_id }, webagent: { tenant_id } }) | Implementation detail |
jti | string | Unique token ID | Stable contract |
grant_id | string | The grant record ID (spans the authorization lifecycle) | Stable contract |
audit_id | string | Audit chain ID (see Audit and accountability chain) | Stable contract |
A delegate token has no act claim
act only appears in the exchanged access token. If you are looking for act in a delegate token, you are holding the wrong token.
Table 2: extra fields on the exchanged access token
The access token returned by token exchange (POST /api/v3/eak/token-exchange) carries these GenAuth extension fields in addition to the target resource's standard claims:
| Field | Type | Meaning |
|---|---|---|
act | object | The actor: { "type": "eak_delegation", "agent_id": "...", "grant_id": "...", "access_key_id": "..." } |
exchange_id | string | Unique ID for this exchange (enters the audit chain) |
product_resource | object | The bound target resource, shaped { "type": "gumem_project" | "webagent_tenant", "id": "..." }. This is an object, not a string — do not compare it directly against "gumem" |
act is a GenAuth extension, not the RFC 8693 shape
RFC 8693 defines act as a nested sub ("act": { "sub": "..." }). GenAuth's act is an extension object with a type discriminator — resource-side code must explicitly check act.type === "eak_delegation" rather than parsing the standard nested shape. The full resource-side checklist is in Protect your APIs.
The sub of the access token is still the user — "on whose behalf" always points to a person, and "who is acting" is expressed by act. That is where delegation semantics land inside the token.
Migration and the compatibility window
Two shapes are deprecated on the SDK side (marked @deprecated as of @eazo/anima v0.2.1):
| Old | New |
|---|---|
delegateAgent({ ... }) | delegateToken({ ... }) |
top-level userId: "usr_x" | user: { id: "usr_x" } |
A two-track compatibility window
The server-side API still accepts the top-level userId during the compatibility window. That means: even after your own code has fully migrated to user: { id }, anyone holding your access key can still start a delegation with the old parameter. Treat access key custody and rotation as your first line of defense — threat analysis in Security considerations.
Validating a token
- Online validation (recommended):
POST /api/v3/eak/delegations/introspectwith{ "token": "..." }. A valid token returns{ "active": true, ...all claims }; anything invalid or expired returns{ "active": false }. The SDK equivalent isanima.genauth.introspectDelegationToken({ token }). - Local verification: after standard JWT signature verification, always check
aud(alwaysgenauth:token-exchangefor a delegate token) and expiry. For access tokens, the resource side must also check the scope intersection (see Protect your APIs).
Next steps
- Reference: API reference
- Guide: Protect your APIs: resource-side integration
- Security: Security considerations