End-user client tokens
Keep your Estuary developer API key on your trusted backend. Your application can use its own identity provider: verify the signed-in user, selected profile, and character access there, then issue restricted Estuary credentials. Clients connect directly to Estuary using those short-lived credentials.
This feature requires a gateway release with the client-token endpoints. Python 0.8.0 is available on PyPI; it and the current Swift SDK support token providers. TypeScript, Unity, Lens Studio and Unreal implementations are pending.
Issue credentials from your backend
Call POST /api/v1/client-tokens with your server-only X-API-Key:
{
"playerId": "your-verified-profile-id",
"characterIds": ["your-approved-character-id"],
"operations": ["text", "voice", "characters:read", "memories:read"],
"allowPlayerCharacters": false,
"expiresIn": 300
}
Estuary binds each key to a persistent application owned by your personal account or organization. Replacement keys for that application preserve the same player identities. Separate applications keep their players isolated, including when they belong to the same developer. Keys cannot delegate permissions or resources they do not possess. Never accept an unverified profile or character list from a client.
The 201 response contains clientToken, applicationId, playerId, sessionId,
expiresAt (Unix seconds), expiresIn, characterIds, and operations. Return
only the client credential fields needed by the application; keep the developer
key private. Use the returned canonical playerId unchanged. Device IDs should
remain separate from person/profile identity.
Expiration is 60–900 seconds. Operations are text, voice, vision,
characters:read, characters:write, memories:read, memories:write, and
generation:write. allowPlayerCharacters additionally allows definitions
assigned to this profile, including newly created ones. Explicit shared
definitions are read-only; memories, facts and motives remain scoped to each
character-player pair.
Renew and revoke
Rotate your server key
In the dashboard's API Keys page, choose Replace on the existing key. The replacement keeps its application and permissions. Copy the new key into your backend's secret configuration, deploy and verify it, then revoke the previous key. The previous key remains active until you revoke it; keep that overlap short. Revoking it also invalidates the client credentials it issued. Your backend can renew with the replacement while preserving player and session identity.
Creating an unrelated new key creates a separate application. To add a key to
an existing application through the key-management API, supply its
applicationId from the key list/create response. You must be authorized in
that application's workspace. Existing keys retain their player identities
when upgrading to the release that supports replacement keys.
Renew client credentials
Your backend retains the sessionId and supplies it on subsequent issuance for
an active call. renew_authentication acknowledges renewal of the same
application/player/session and permitted character. Operation changes require
reconnecting. An inactive session expires after 30 minutes without renewal; a
trusted backend can issue a fresh one after verifying authorization again.
For logout, account removal or device unlinking, your backend calls
DELETE /api/v1/client-sessions/{sessionId} with its server key. Every token in
that session becomes invalid. New requests fail immediately and active sockets
and voice terminate during the server's periodic authorization check. SDKs
release local voice after rejected renewal or terminal authorization errors.
Python
from estuary_sdk import ClientCredential, EstuaryClient, EstuaryConfig
async def token_provider():
# Your authenticated backend request; use its own cookies or auth headers.
data = await my_backend.request_client_credentials()
return ClientCredential(data["clientToken"], data["playerId"], data["expiresAt"])
client = EstuaryClient(EstuaryConfig(
server_url="https://api.estuary-ai.com",
api_key="", character_id="your-approved-character-id", player_id="",
token_provider=token_provider,
))
await client.connect()
facts = await client.memory.get_core_facts()
The SDK caches credentials, shares them between socket and REST requests, and
renews before expiry. A profile change requires a new client. Handle auth_error
by updating your UI and obtaining fresh application authorization.
Swift
var config = EstuaryConfiguration(
credential: .clientToken(""), characterID: "your-approved-character-id", playerID: ""
)
config.tokenProvider = {
let data = try await myBackend.requestClientCredentials()
return ClientCredential(token: data.clientToken, playerID: data.playerId, expiresAt: data.expiresAt)
}
let client = EstuaryClient(configuration: config)
try await client.connect()
let rest = ClientDataClient(tokenProvider: config.tokenProvider!)
let facts = try await rest.coreFacts(characterID: "your-approved-character-id")
Supply your application's authenticated URLSession/cookies/headers in the callback. The SDK does not depend on your login provider. It rejects profile changes within a client and cleans up voice when authorization ends.
Supported REST routes
REST uses Authorization: Bearer <clientToken> and the canonical player.
Supported grants cover character list/create/read/update/delete,
image-to-character generation, model status/generation, character-player memory
routes (including search/core facts), and per-player motive reads/writes.
Share minting, encounters, management and other routes are outside this data
plane. Existing trusted developer-key integrations remain supported.