Skip to main content

REST, rich results, and moderation

Use EstuaryHttpClient for character management, conversation history, memories, shares, and text-only turns over HTTP. It uses the same EstuaryConfig as your live character connection. Run its methods with StartCoroutine.

Authentication and errors​

using Estuary;
using Estuary.Models;
using UnityEngine;

public class CharacterHistory : MonoBehaviour
{
[SerializeField] private EstuaryConfig config;
[SerializeField] private string characterId;
[SerializeField] private string playerId;

void Start()
{
var http = new EstuaryHttpClient(config) { PlayerId = playerId };
StartCoroutine(http.GetMessages(characterId, playerId,
page => Debug.Log($"Loaded {page.Messages.Length} messages"),
error => Debug.LogError(error), limit: 50, page: 1));
}
}

Configure an API key or config.TokenProvider for Firebase authentication. If a token provider fails, the request fails without falling back to the API key. http.PlayerId sends X-Player-Id; http.OrgId sends X-Org-Id. The SDK also sends its name and version on API requests.

The error callback includes the HTTP status and server response body. Missing API-key scopes remain visible in the response detail. The SDK does not retry rejected mutations or rate-limited turns automatically.

Available methods​

Each method takes success and error callbacks; optional filters follow them. Response models live in Estuary.Models. Character writes take a JObject with the character API schema, including nested persona, llm, tts, and stt sections. A character's display name is persona.name on writes and AgentResponse.Name on reads.

AreaMethods
CharactersListCharacters, GetCharacter, CreateCharacter, UpdateCharacter, DeleteAgent, TransferCharacter
Private motivesGetCharacterMotive, SetCharacterMotive, ResetCharacterMotive
Tool configurationTestApiEndpoint
Images/modelsUploadImageToCharacter, GenerateModel, GetModelStatus, PollModelStatus, UploadAvatar, UploadModel, DownloadGlb
ConversationsListConversations, GetConversationStats, GetConversation, GetMessages, DeleteConversation
MemoriesGetMemories, GetMemoryTimeline, GetMemoryStats, GetCoreFacts, GetMemoryGraph, SearchMemories, CreateMemory, UpdateMemory, DeleteMemory, DeleteAllMemories
SharesCreateShare, OpenShare, ExchangeShare, ListShares, RevokeShare, RevokeSharesByApiKey
HTTP turnsTurn, StreamTurn

ListCharacters is paginated; GetAgents preserves its older unpaginated list behavior. DeleteConversation removes messages and memories for the pair. DeleteAllMemories requires an explicit confirm: true. Memory search uses POST but only requires memories:read.

CreateShare supports a permanent share or a TTL of 24h, 7d, 30d, or 90d. OpenShare and ExchangeShare redeem links anonymously, without sending your API key, Firebase token, player header, or organization header. For isolated shares, pass a stable recipientId to ExchangeShare. Use the returned ShareSession.SessionToken as the API key with its CharacterId, PlayerId, and ServerUrl. Listing and revoking shares requires a Firebase TokenProvider.

Streaming HTTP turns​

HTTP turns return text and actions without starting a voice connection. Reuse a playerId to preserve that player's relationship and history.

var http = new EstuaryHttpClient(config);
StartCoroutine(http.StreamTurn(characterId,
new CharacterTurnRequest { Message = "Hello", PlayerId = playerId },
evt =>
{
if (evt.Type == "text_delta") Debug.Log((string)evt.Data["text"]);
if (evt.Type == "api_endpoint_result")
{
var result = evt.Data.ToObject<ApiEndpointResult>();
Debug.Log($"Received {result.Media.Length} media items");
}
},
reply => Debug.Log(reply.Text),
error => Debug.LogError(error)));

CharacterTurnEvent preserves the event type and JSON data. Events arrive in order; onCompleted follows turn_completed. An incomplete or malformed stream calls onError. Turn uses the same request model for a single complete response. Set Remember = false to skip long-term memory extraction for a turn.

Rich results and moderation​

These events are available on EstuaryClient, EstuaryManager, and EstuaryCharacter, and run on Unity's main thread:

EventPayload and behavior
OnDelegationUpdateDelegationUpdate: task progress, invocation/message IDs, results, errors, optional AuthorizationUrl
OnApiEndpointResultApiEndpointResult: tool call ID, operation, status, message ID, Media and Citations
OnModerationWarningModerationWarning: Level, Message, and IsTerminated
OnModerationFlagModerationFlag: MessageId, replacement Message, Action, and Categories
OnServerErrorServerError: machine-readable Code and human-readable Message; OnError remains available

Rich media and citations are separate from character prose. The SDK passes URLs as data. Your app decides whether to show an authorization link; validate remote media URLs and fetch them without attaching Estuary credentials.

On termination, the SDK stops local audio and microphone capture and suppresses automatic reconnection. Resume with an explicit Connect after user intent. For a redaction, it stops matching audio, rejects late content for that message, and updates the current partial response only if the ID matches. Your UI must replace historical text and remove media associated with the flagged message ID. The SDK's BasicChatDemo includes an example of text replacement by ID.

Rigged models and imported clips​

Call GenerateModel(characterId, onSuccess, onError, rigged: true) to request a rigged humanoid. Polling exposes the posing, rig checking, rigging and animation stages. ModelStatusResponse.Rigged and Animations describe the result. rig_failed or animation_failed with a usable ModelUrl is a partial success: you can still display the static model.

With glTFast installed, add EstuaryClipPlayer beside EstuaryCharacter and EstuaryModelLoader. It binds the loaded model's skeletal clips to typed actions: an exact name wins, otherwise a unique colon-delimited suffix is accepted (wave matches preset:biped:wave). Ambiguous or absent clips emit OnClipNotFound. Idle, walk and run loop; other actions return to idle. Use SetModel(GameObject) for a custom importer and PlayAction(string) for explicit playback. Audio2Face is not implemented by this adapter.

Audio completion and LiveKit state​

For WebSocket voice, BotVoice.IsFinal marks the end of server audio, including an empty terminator. EstuaryAudioSource waits until those samples have rendered locally, then emits OnMessagePlaybackComplete(messageId) and acknowledges the same ID to the server. Buffer underruns do not count as completion. If you feed raw PCM with EnqueueAudio(byte[], sampleRate, messageId), call MarkAudioComplete(messageId) after the final chunk.

OnBotSpeakingStateChanged on the manager and character exposes BotSpeakingState with State, IsSpeaking, MessageId, and ParticipantIdentity. Advanced integrations can read CurrentBotSpeakingState through the optional ILiveKitBotStateSource interface. Older servers may supply an empty message ID. These attributes describe server speaking state; use your local audio playback clock for precise animation timing.