SDK reference (@eazo/anima)
The official SDK is Node.js / TypeScript (@eazo/anima). Other languages call the HTTP API directly.
Why domains and endpoints still say eak
SDK symbols are unified under the Anima prefix (Qoni, AnimaScopes, Anima*Error). Domains and endpoints such as api.eak.eazo.ai and /api/v3/eak/... keep the legacy identifier eak — they all refer to the same Qoni infrastructure.
This page is verified line by line against the type definitions of the published @eazo/anima v0.2.1. After upgrading the SDK, the type definitions of that version are authoritative.
Install and initialize
npm install @eazo/animaimport { Qoni } 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!,
});Common AnimaOptions:
| Option | Type | Notes |
|---|---|---|
host | string | Gateway address. The SDK discovers per-service addresses through runtime-config |
accessKey / secretKey | string | Workspace access key. Server-side only — never ship it to a browser or client (see Security considerations) |
timeoutMs | number | Request timeout |
sseMaxRetries | number | Automatic reconnect attempts for SSE streams (default 5, resumes from last-event-id; 0 disables) |
fetch | typeof fetch | Custom fetch implementation |
Deprecated initialization options
accessKeyId / accessKeySecret are @deprecated — use accessKey / secretKey. The direct-address options eakBaseUrl / genauthBaseUrl and friends are @deprecated — use host and let runtime discovery do the work.
delegateToken — start a delegation
Two modes, two return shapes. Overloads:
delegateToken(input: DelegateTokenInteractiveInput): Promise<AnimaResponse<InteractiveDelegationResponse>>;
delegateToken(input: DelegateTokenSilentInput): Promise<AnimaResponse<DelegateTokenSilentResponse>>;interactive (user-level consent — the path this documentation leads with)
import { AnimaScopes } from "@eazo/anima";
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: "opaque-business-state",
user: { id: "usr_demo_0001" },
});
// data: InteractiveDelegationResponse
// { mode: "interactive", authorizationUrl, grantId, grantState, state, requestedScopes? }Send the user to authorizationUrl to give consent, then exchange the callback for a token with completeDelegateToken.
silent (organization-level consent — the trusted server-side path)
const { data } = await anima.delegateToken({
agent: "report-agent",
scopes: [AnimaScopes.WEB_SEARCH_RUN],
user: { id: "usr_demo_0001" },
expiresIn: 7200, // seconds, 60-86400
});
// data: DelegateTokenSilentResponse (contains the token immediately)Where silent mode ends
Silent mode gives consent on behalf of the organization instead of asking the user. It only belongs in a hardened, trusted server-side integration. Read the hard-constraint checklist in Security considerations and the positioning in Consent and approval before you use it.
Input fields (DelegateTokenInput)
| Field | Type | Required | Notes |
|---|---|---|---|
agent | string | ✓ | Agent identifier |
scopes | string[] | ✓ | Requested scopes (use the AnimaScopes constants) |
user | { id: string, ... } | ✓ for silent / optional for interactive | The delegating user |
mode | "silent" | "interactive" | defaults to silent | Consent mode |
redirectUri / state | string | ✓ for interactive | Callback address and your business state |
expiresIn | number | string | optional | Token lifetime (seconds, 60-86400) |
idempotencyKey | string | optional | Idempotency key |
Deprecated: the top-level userId (use user: { id }) and the delegateAgent() alias (use delegateToken()). Migration mapping: Glossary and migration. The server-side compatibility risk is covered in Security considerations.
completeDelegateToken — finish an interactive consent
const { data } = await anima.completeDelegateToken({ grantId, code, state });
// data: DelegateTokenResponseDelegateTokenResponse fields:
| Field | Type | Notes |
|---|---|---|
token | string | The delegate token. This is the SDK's renamed field — over HTTP it is called delegationToken (see the comparison below). The SDK also keeps the @deprecated aliases delegateAgentToken and delegationToken |
tokenType | "Bearer" | Token type |
expiresIn | number | Lifetime in seconds |
grantId / auditId | string | Grant record ID / audit chain ID |
grantedScopes | string[]? | Scopes actually granted |
mode | "silent" | "interactive" | How it was issued |
SDK field names ≠ HTTP field names
The SDK renames and enriches the backend response. Mixing the two returns undefined:
| HTTP response (API reference) | SDK response (this page) | |
|---|---|---|
| Delegate token | delegationToken (+ delegateAgentToken, same value) | token |
| Granted scopes | not present | grantedScopes? |
| Error codes | eak.* dotted codes (e.g. eak.delegation.agent_not_allowed) | merged codes + typed error classes (below) |
Use this page when you use the SDK; use the API reference when you call HTTP directly.
Namespaces
anima.genauth:introspectDelegationToken({ token })(online delegate-token validation),userInfo({ accessToken }),jwks(),discovery(),users.list / get / getBatch / create / createBatch / update / deleteBatch(management plane, requires an admin token).anima.eak:workspaces.list / get / create / update,credentials.list / create / rotate / update(workspace and access-key management, matching the workspace endpoints in the API reference).- Product namespaces:
anima.gumem,anima.webSearch,anima.doAnything,anima.track,anima.deepResearch— product capability calls that accept atoken(delegate token or runtime token); see the respective product docs.
Other useful methods
| Method | Purpose |
|---|---|
anima.currentUser({ accessToken }) | Resolve the signed-in user from their access token — this is how application code decides "on whose behalf" |
anima.resolveAnyBoundUser() | Return any user ID from the bound user pool. Demos and smoke tests only — production code should use currentUser |
anima.request({ method, path, body?, query?, headers?, token? }) | Raw request channel for endpoints that have no named wrapper yet (such as token exchange at POST /api/v3/eak/token-exchange); signing is handled for you |
anima.unstableRequest(...) | Same, for unstable or experimental endpoints |
Scope constants
AnimaScopes (formatted service.capability:action) and AnimaScopeBundles (common combinations):
AnimaScopes.WEB_SEARCH_RUN // "webagent.web_search:run"
AnimaScopes.DO_ANYTHING_RUN // "webagent.do_anything:run"
AnimaScopes.GUMEM_MEMORY_READ // "gumem.memory:read"
AnimaScopeBundles.WEB_SEARCH // run + read
AnimaScopeBundles.AGENT_DO_ANYTHING_BASIC // run + read + stop + control
AnimaScopeBundles.GUMEM_READONLY // memory:read + profile:readPick for least privilege: prefer a single scope over a bundle, and read over run.
Error handling
Every error extends AnimaError (carrying code, status and meta.requestId):
| Error class | Typical code | When it happens |
|---|---|---|
AnimaAuthError | auth.failed | Bad signature or key |
AnimaDelegationRequiredError | delegation.required | Delegate token missing |
AnimaPermissionDeniedError | permission_denied | Insufficient scope, or whitelist rejection |
AnimaTokenExpiredError | token.expired | Token expired |
AnimaValidationError | validation.failed | Request validation failed |
AnimaRateLimitError | rate_limit.exceeded | Rate limited |
AnimaUpstreamError | upstream.failed, eak.token_exchange.upstream_failed | Upstream service failure |
AnimaTimeoutError | timeout | Timed out |
import { AnimaTokenExpiredError } from "@eazo/anima";
try {
await anima.genauth.introspectDelegationToken({ token });
} catch (err) {
if (err instanceof AnimaTokenExpiredError) {
// start a new delegation
}
throw err;
}Next steps
- Get started: First delegation in 30 minutes
- Reference: API reference, Token and claim reference