Skip to main content

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.

note

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.