Ephemeral tokens
Short-lived, scoped tokens for handing inference access to a browser, agent run, or end-user session.
Your workspace API key can call anything, forever, until you revoke it. That's the wrong shape to hand to a browser tab, a sandboxed agent run, or a per-customer session — any of those could leak the key, and a leaked long-lived key is a standing liability. An ephemeral token is minted on demand from your backend, expires on its own within hours, and can be restricted to exactly the routes and models that one session needs. It cannot outlive or exceed the purpose it was minted for.
Minting a token
Minting happens on your backend, authenticated with your workspace's personal access token (PAT) — never inside the untrusted client:
curl https://api.aisetu.ai/graphql \
-H "Authorization: Bearer $AI_SETU_PAT" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation Mint($input: MintEphemeralTokenInput!) { mintEphemeralToken(input: $input) { token jti expiresAt scopes } }",
"variables": {
"input": {
"parentKeyId": "'"$PARENT_KEY_ID"'",
"scopes": ["chat", "model:openai/gpt-4o-mini"],
"ttlSeconds": 900,
"budgetMicros": 500000
}
}
}'Hand the returned token (prefixed tt_ev_) to the client; it's used
exactly like a workspace key:
import { AiSetu } from '@ai-setu/client';
const client = new AiSetu({ apiKey: ephemeralToken });
const res = await client.chat.completions.create({
model: 'openai/gpt-4o-mini',
messages: [{ role: 'user', content: 'Hello' }],
});See the Agent Quickstart for the full
walkthrough of wiring this into a runtime agent, including how to layer a
budgetMicros cap on top.
What a scope can restrict
| Input | Restricts |
|---|---|
chat | POST /v1/chat/completions |
embeddings | POST /v1/embeddings |
messages | POST /v1/messages (Anthropic-native) |
model:<id> | Narrows the token to specific models. With no model: scope, any model is allowed on the granted route(s); with one or more present, the request's model must match one of them. |
A token with no scopes at all is rejected outright — there is no "grants
everything" ephemeral token. A scope can never exceed what the parent key
(parentKeyId) itself is authorized for: requesting chat or messages
requires the parent to hold chat:write, and embeddings requires embed.
A model:<id> scope is always allowed to request (it can only narrow, never
widen). Minting fails validation if any requested scope exceeds the parent's
grant.
Lifetime
ttlSeconds is required and capped at 24 hours — the mint call rejects
anything longer. The cap is also enforced a second time on the verifying
side (the gateway), independent of what a token's own claims say, so a
future minting bug can't produce a token that outlives the platform-wide
ceiling.
The gateway verifies a token statelessly: an HMAC-SHA256 signature check plus an expiry check, no database or cache round trip on the request path. That's what makes ephemeral tokens cheap enough to mint per session or even per request.
Revocation
Verification alone can't kill a token before it expires, so revocation is layered on top via a shared denylist your backend writes to directly:
mutation RevokeOne {
revokeEphemeralToken(jti: "...")
}
mutation RevokeAllFromKey {
revokeEphemeralTokensForKey(parentKeyId: "...")
}- Revoke one — denies that single
jtiimmediately. - Revoke all for a parent key — stamps a cutoff so every token minted from that key with an earlier issue time is denied; anything minted after the call is unaffected.
A revoked token is rejected on the next request with 401 /
wesence.revoked_key. Regardless of whether you ever call revoke, every
ephemeral token is bounded by its own ttlSeconds — the denylist only
matters for killing a token before it would have expired on its own.
Optional per-token limits
Beyond scope and TTL, a mint call can attach:
budgetMicros— a spend cap in USD micros for just this token. Once hit, further calls return402/wesence.credential_cap_exceeded; nothing else in your workspace is affected.rateLimits— request/token frequency limits scoped to this token alone.
Error codes on a scoped call
| Code | HTTP status | Meaning |
|---|---|---|
wesence.invalid_key | 401 | Token missing, malformed, expired, or ephemeral tokens aren't enabled on this deployment |
wesence.revoked_key | 401 | The token (or its parent key, via a revoke-all) was revoked |
wesence.scope_denied | 403 | The token is valid, but its scope doesn't permit this route or model |
wesence.credential_cap_exceeded | 402 | The token's own budgetMicros cap is exhausted |
Full reference: Errors.
Deployment note
Ephemeral tokens require a signing key configured on both the API (which
mints) and the gateway (which verifies) — if your deployment hasn't set one
up, tt_ev_ tokens are rejected as an unknown credential (401 /
wesence.invalid_key). Ask your platform operator if minting fails
unexpectedly.