🧪 Beta — Available today; the interface contract may still change.
Let an agent call your APIs on a user's behalf
By the end of this guide you will be able to:
- Have an agent call your own APIs as "on behalf of one specific user" instead of through a shared service account
- Know, on the API side, exactly who authorized this, which agent is executing it, and what it is allowed to do
- Put every one of those calls into the audit chain, traceable after the fact
Prerequisites
- You have completed First delegation in 30 minutes (this guide is its production-grade version)
- Your API validates, or is about to validate, access tokens issued by GenAuth (checklist: Protect your APIs)
- Your access key lives in server-side environment variables
The complete chain
First, get the four responsibilities clear:
| Party | What it does in this chain |
|---|---|
| Your application | Starts the delegation, handles the callback, exchanges for an access token, hands the token to the agent |
| The user | Makes the decision on the consent page |
| GenAuth | Validates, issues the delegate token, performs token exchange |
| The agent | Calls your API with the access token |
Step 1 · Start the delegation (your app → GenAuth)
import { Qoni, AnimaScopes } from "@eazo/anima";
const anima = new Qoni({
host: "https://api.eak.eazo.ai",
accessKey: process.env.ANIMA_ACCESS_KEY!,
secretKey: process.env.ANIMA_SECRET_KEY!,
});
const { data } = await anima.delegateToken({
mode: "interactive",
agent: "report-agent",
scopes: [AnimaScopes.WEB_SEARCH_RUN, AnimaScopes.WEB_SEARCH_READ],
redirectUri: "https://yourapp.example.com/eak/callback",
state: signedState, // put signed business context here
user: { id: currentUser.id },
expiresIn: 7200, // seconds, 60-86400. Size it to the task, not to the max
});
redirect(data.authorizationUrl);How to pick scopes: the smallest set the current task needs. Prefer read over run; prefer a single scope over a bundle. The user sees exactly the list you request on the consent page — the broader you ask, the more they hesitate.
Step 2 · The user consents (user → GenAuth)
The user sees the agent, the scope and the lifetime on the consent page and makes a decision. You do nothing here, but you must handle both outcomes: approval (which carries a code) and rejection or timeout.
You must verify state
The state coming back on the callback must match the value you sent, otherwise reject the request. This is basic hygiene against authorization hijacking.
Step 3 · Exchange for a delegate token (your app → GenAuth)
// GET /eak/callback?grantId=...&code=...&state=...
verifySignedState(query.state); // verify state first
const { data: grant } = await anima.completeDelegateToken({
grantId: query.grantId,
code: query.code,
state: query.state,
});
// grant.token / grant.expiresIn / grant.grantId / grant.auditId / grant.grantedScopesThe grantedScopes you get back may be smaller than the scopes you asked for — trust what was actually granted, never assume a request is granted in full.
Step 4 · Exchange for an access token (agent or your app → GenAuth)
A delegate token cannot reach a resource; its aud only accepts the token exchange endpoint. Exchange it for an access token bound to the target resource:
// Use the SDK's raw request channel to call the token exchange endpoint
const { data: exchanged } = await anima.request<{
token: string;
tokenType: string;
expiresIn?: number;
}>({
method: "POST",
path: "/api/v3/eak/token-exchange",
body: {
subjectToken: grant.token,
resource: "webagent",
scopes: [AnimaScopes.WEB_SEARCH_RUN],
},
});
// exchanged.token / exchanged.tokenType / exchanged.expiresIncurl -X POST https://api.eak.eazo.ai/api/v3/eak/token-exchange \
-H "Authorization: <AK/SK signature>" \
-H "Content-Type: application/json" \
-d '{
"subjectToken": "<delegate token>",
"resource": "webagent",
"scopes": ["webagent.web_search:run"]
}'The scopes in an exchange request must be a subset of what the delegate token already grants — this is where the attenuation rule is enforced (how it works).
Step 5 · Call your API (agent → your API)
The agent puts the access token in Authorization: Bearer <token> and calls your API. What your API side must check:
- Verify the signature
- Verify
audis your resource - Read
subfor the user being represented, and run your business authorization against it - Read
act(act.type === "eak_delegation") for the executing agent, and log it - Verify the scope covers this operation
Full checklist and pseudocode: Protect your APIs. Token field structure: Token and claim reference.
Verify
Three checkpoints, one at a time:
// (1) The delegate token really is valid, and the authorization facts match expectations
const { data: info } = await anima.genauth.introspectDelegationToken({ token: grant.token });
console.assert(info.active === true);
console.assert(info.sub === currentUser.id);
console.assert(info.agent_id === "report-agent");(2) Overreach must fail: use a read-only token against a write endpoint — your API must reject it. If this passes, your resource-side validation is not doing its job.
(3) The audit chain is complete: look up grant.auditId and you should see three kinds of event — authorization, exchange and access (see Audit and accountability chain).
FAQ
Can I skip user consent? You can use organization-level consent (trusted server-side integration), but that is a different authorizing party with extra hardening requirements — read Consent and approval and Security considerations first.
Can a delegate token be cached and reused? Within its lifetime, yes — but store it isolated per user and start a new delegation on a token.expired error. Never reuse across users; that breaks the delegation boundary.
What if the user revokes mid-task? The next call fails with the corresponding error code. Your application should catch AnimaPermissionDeniedError / AnimaTokenExpiredError and guide the user through re-authorization (error types: SDK reference).
Next steps
- Protect your APIs: resource-side integration — what to check once the token reaches your API.
- Revocation and incident response — how to recall authority early.
- Delegate Token and attenuation — the rules behind this chain.