Skip to main content

Shares API

Mint shareable links to your characters. Two flavors:

  • Expiring shares -- time-limited, optionally exchange-capped. Good for product trials, link sharing, embedded widgets.
  • Permanent shares -- never expire. Designed for physical carriers (NFC tags, QR codes, printed cards). Defended by per-link rate limits and dashboard revocation.

Base path: /api/v1/share


Authentication

The Shares API supports two auth modes depending on the endpoint:

AuthHow to sendNotes
API keyAuthorization: Bearer est_...Used for create. Stamps created_via_api_key_id for forensics.
Firebase ID tokenAuthorization: Bearer <firebase-id-token>Used for list, revoke, and bulk-revoke.

The POST /api/v1/share/{id}/open endpoint is unauthenticated -- the share_id itself is the credential, protected by rate limits.


Create a Share

POST /api/v1/share

Mint a new share, either expiring (default) or permanent.

Request Body

{
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"permanent": false,
"ttl": 3600,
"memorySharing": "isolated",
"maxExchanges": 100,
"maxInteractions": 50
}
FieldTypeRequiredDescription
characterIdstringYesUUID of the character to share. Must be owned by the caller.
permanentbooleanNoWhen true, mints a permanent share. ttl and maxExchanges are ignored. Default: false.
ttlintegerNoLifetime in seconds for an expiring share.
memorySharingstringNo"isolated" (default) or "shared". Controls whether shared sessions write to the owner's memory.
maxExchangesintegerNoCap on total redeemers (expiring shares only).
maxInteractionsintegerNoCap on total interactions (chat turns) across all redeemers.

Response: Permanent

{
"token": "<share id>",
"shareUrl": "https://share.estuary-ai.com/s/<share id>",
"anchorUrl": "https://share.estuary-ai.com/sa/<share id>",
"expiresAt": null
}

The anchorUrl uses the short sa/ path -- this minimizes NDEF bytes when encoded onto NTAG213 NFC tags (144 byte user memory).

Response: Expiring

{
"token": "<share token>",
"shareUrl": "https://share.estuary-ai.com/s/<share token>",
"expiresAt": "2026-05-09T18:00:00Z"
}

Errors

StatusCause
404Character does not exist or is not owned by the caller
429Permanent share limit reached (MAX_ACTIVE_PERMANENT_PER_USER = 200)

Examples

# Permanent share (NFC / QR / print)
curl -X POST https://api.estuary-ai.com/api/v1/share \
-H "Authorization: Bearer est_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"permanent": true
}'
# Expiring share (1-hour link)
curl -X POST https://api.estuary-ai.com/api/v1/share \
-H "Authorization: Bearer est_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"ttl": 3600,
"maxInteractions": 50
}'

Open a Permanent Share

POST /api/v1/share/{share_id}/open

Mint a fresh, short-lived session token for a permanent share. This endpoint is unauthenticated -- the share_id is the credential. Each call increments the share's open_count and updates last_opened_at.

:::caution Rate limits This endpoint is rate-limited per share_id to prevent quota drain:

  • 10 requests per minute
  • 100 requests per hour
  • 500 requests per day

Excess requests return 429 Too Many Requests with the offending bucket in the detail field (e.g., "Rate limit (min)"). :::

Response

{
"sessionToken": "sst_a1b2c3d4...",
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"playerId": "player_abc123",
"serverUrl": "https://api.estuary-ai.com",
"character": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Sage",
"tagline": "A wise mentor",
"avatar": "https://...",
"modelUrl": "https://.../final.glb",
"modelPreviewUrl": "https://.../preview.glb",
"modelStatus": "ready",
"sourceImageUrl": "https://.../source.jpg"
}
}
FieldTypeDescription
sessionTokenstringOpaque sst_<32-char> token. Use as the api_key field in the SDK auth payload. 1-hour TTL.
characterIdstringCharacter to connect to. Pass as character_id to the SDK.
playerIdstringPlayer ID to use for this session.
serverUrlstringEstuary server URL the SDK should connect to.
characterobjectPublic character metadata (name, avatar, 3D model URLs).

Errors

StatusCause
404Share not found, revoked, or not a permanent share
429Rate limit exceeded

:::info Compatible with SDK auth sessionToken (the sst_<32-char> string) is interchangeable with an API key in the SDK auth payload. SDKs do not need any special code path to consume share-issued tokens. :::

Example

curl -X POST https://api.estuary-ai.com/api/v1/share/abc123/open

List Shares for a Character

GET /api/v1/share/character/{character_id}

List all non-revoked shares (permanent and expiring) for a character. Firebase ID token only -- API keys cannot list shares.

Response

{
"shares": [
{
"id": "abc123",
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2026-05-01T10:00:00Z",
"expiresAt": null,
"memorySharing": "isolated",
"maxExchanges": null,
"maxInteractions": null,
"openCount": 12,
"lastOpenedAt": "2026-05-08T12:34:00Z",
"shareUrl": "https://share.estuary-ai.com/s/abc123",
"anchorUrl": "https://share.estuary-ai.com/sa/abc123"
}
]
}

Revoke a Share

DELETE /api/v1/share/{share_id}

Revoke a share (permanent or expiring). Soft-deletes the row, deletes Redis forward maps, and disconnects active sessions across all server pods. Firebase ID token only.

Response

{
"revoked": true,
"killedSessions": 2,
"removedSessionBlobs": 2
}
FieldTypeDescription
revokedbooleanAlways true on success
killedSessionsnumberNumber of active SDK sessions that were disconnected
removedSessionBlobsnumberNumber of session token blobs invalidated in cache

Bulk Revoke by API Key

DELETE /api/v1/share/by-api-key/{api_key_id}

Revoke every share that was created via a specific API key. Useful for rotating credentials or recovering from a compromised device. Firebase ID token only.

The endpoint revokes shares matching:

  • created_via_api_key_id = api_key_id
  • owner_user_id = caller
  • revoked_at IS NULL

Each share is run through the full kill-switch (DB soft-delete, Redis cleanup, cross-pod session kill).

Response

{
"revoked": 7,
"killedSessions": 12,
"removedSessionBlobs": 12
}

Notes

  • Permanent shares have expires_at = NULL and max_exchanges = NULL.
  • Permanent share IDs are either 32-char base64url strings (legacy) or 36-char UUIDs (new).
  • The Estuary backend caps each user at 200 active permanent shares.
  • Session tokens issued by /open last 1 hour. After expiry, the SDK must reconnect by calling /open again.