Skip to main content

Characters API

Create and manage AI characters. Characters define the persona, voice, language model, and actions for a conversational AI agent.

Base path: /api/v1/characters


Authentication & Player Scoping​

All Characters API requests authenticate with the X-API-Key header. Optionally supply X-Player-Id to scope operations to a single end user.

HeaderRequiredDescription
X-API-KeyYesDeveloper API key (est_...)
X-Player-IdNoOpaque player identifier. When present, scopes operations to characters owned by this player. Estuary trusts the API key holder to provide verified player IDs.
X-Org-IdNoOrganization id. When present, creates and lists resources owned by that organization (you must be an active member). Absent means your personal space. Applies to single create, batch create and knowledge documents.

Player Scoping Rules​

  • No X-Player-Id -- full developer access (all characters owned by the API key account).
  • With X-Player-Id -- operations are restricted to characters whose player_id matches.
  • GET /api/v1/characters/{id} returns 404 on player mismatch (anti-enumeration -- the same response as a non-existent character).
  • PUT and DELETE return 403 on player mismatch.
  • Players cannot modify or delete developer-created characters (those with player_id = null).

Create a Character​

POST /api/v1/characters

All configuration sections are optional -- sensible defaults are applied.

Request Body​

{
"persona": {
"name": "Axiom",
"tagline": "Curious marine researcher",
"personality": "You are Axiom, an enthusiastic axolotl and marine researcher.",
"background": "Axiom has spent three years studying bioluminescent organisms."
},
"llm": {
"model": "gpt-5-mini",
"temperature": 0.8
},
"tts": {
"voice": "MF3mGyEYCl7XYWbV9V6O"
},
"actions": [
{ "name": "follow_user", "description": "Start following the user" },
{ "name": "stop_following_user", "description": "Stop following the user" }
]
}

persona object​

FieldTypeDefaultDescription
namestring"New Character"Display name (1-255 chars)
taglinestringnullShort description (max 500 chars)
personalitystringnullHow the character speaks and behaves (max 5000 chars)
backgroundstringnullBackstory and lore (max 32000 chars)
system_promptstringnullFull system prompt override -- replaces the auto-generated prompt

llm object​

FieldTypeDefaultDescription
providerstring"openai"LLM provider
modelstring"gpt-5-mini"Model name
temperaturenumber0.7Sampling temperature (0-2)
max_tokensinteger2048Max response tokens (1-4096)
verbositystring"medium"Response length: "short", "medium", "long"

tts object​

FieldTypeDefaultDescription
providerstring"inworld"TTS provider
modelstring"inworld-tts-2-flash"TTS model
voicestringnullProvider-specific voice ID
speednumber1.0Playback speed (0.5-2.0)
delivery_modestringnullInworld TTS-2 family only (inworld-tts-2, inworld-tts-2-flash): "STABLE", "BALANCED", or "CREATIVE". The quality knob those models use instead of temperature; ignored by other models. null = provider default.

stt object​

FieldTypeDefaultDescription
providerstring"deepgram"STT provider
languagestring"en"Language code (ISO 639-1)

vad object​

FieldTypeDefaultDescription
sensitivitynumber0.5VAD sensitivity (0-1)
min_speech_duration_msinteger250Min speech duration in ms (50-2000)

vlm object​

FieldTypeDefaultDescription
modelstring"gpt-4.1-mini"Vision-language model for image understanding

Top-level fields​

FieldTypeDefaultDescription
text_onlybooleanautoSkip TTS. Auto-detected from voice config if omitted
verbosebooleanfalseEnable verbose logging
is_publicbooleanfalseWhether the character is publicly listed
avatarstringnullAvatar identifier or URL
actionsarraynullActions the character can invoke
document_idsarraynullIDs of already-ingested documents to attach as a knowledge bank. See Knowledge Documents for adding documents by URL.

actions array​

Each action object:

FieldTypeRequiredDescription
namestringYesAction identifier (snake_case)
descriptionstringNoWhat the action does
parametersarrayNoParameters the action accepts

Each parameter object:

FieldTypeDefaultDescription
namestringrequiredParameter name
typestring"string""string", "number", or "boolean"
requiredbooleanfalseWhether the parameter is required
descriptionstringnullParameter description

Response​

Status: 201 Created

Returns a Character object.

Examples​

curl -X POST https://api.estuary-ai.com/api/v1/characters \
-H "X-API-Key: est_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"persona": {
"name": "Sage",
"personality": "A wise and patient mentor who speaks in calm, measured tones."
},
"tts": { "voice": "MF3mGyEYCl7XYWbV9V6O" },
"llm": { "temperature": 0.6 }
}'
const res = await fetch("https://api.estuary-ai.com/api/v1/characters", {
method: "POST",
headers: {
"X-API-Key": "est_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
persona: {
name: "Sage",
personality: "A wise and patient mentor who speaks in calm, measured tones.",
},
tts: { voice: "MF3mGyEYCl7XYWbV9V6O" },
llm: { temperature: 0.6 },
}),
});
const character = await res.json();
console.log(character.id); // use this as character_id

List Characters​

GET /api/v1/characters

Returns a paginated list of your characters, ordered by creation date (newest first).

ParameterTypeDefaultDescription
limitinteger20Results per page (1-100)
offsetinteger0Number of results to skip (minimum 0)
searchstringnoneCase-insensitive substring match on the character's name, tagline or ID (max 100 characters). It narrows total as well as the returned page.

Response​

{
"characters": [ /* Character objects */ ],
"total": 42,
"limit": 20,
"offset": 0
}

Example​

curl "https://api.estuary-ai.com/api/v1/characters?limit=10" \
-H "X-API-Key: est_your_api_key"
# narrow the list to characters matching "sage"
curl "https://api.estuary-ai.com/api/v1/characters?search=sage&limit=10" \
-H "X-API-Key: est_your_api_key"

Get a Character​

GET /api/v1/characters/{character_id}

Example​

curl https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000 \
-H "X-API-Key: est_your_api_key"

Response​

Returns a Character object.


Update a Character​

PUT /api/v1/characters/{character_id}

Only provided fields are updated -- omitted fields keep their current values. The request body accepts the same fields as Create.

Example​

curl -X PUT https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000 \
-H "X-API-Key: est_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"persona": { "name": "Sage v2" },
"llm": { "temperature": 0.9 }
}'

Response​

Status: 200 OK

Returns the updated Character object.


Delete a Character​

DELETE /api/v1/characters/{character_id}

Permanently deletes a character and all associated data (conversations, messages, documents).

Example​

curl -X DELETE https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000 \
-H "X-API-Key: est_your_api_key"

Response​

{
"deleted": true,
"id": "550e8400-e29b-41d4-a716-446655440000"
}

Replace the Avatar​

PUT /api/v1/characters/{character_id}/avatar

Upload a new avatar image. The request is multipart/form-data with a single image file part: JPEG, PNG or WebP, 10 MB maximum.

Requires edit access to the character. When X-Player-Id is present, only the owning player may update the avatar; anyone else gets 403.

:::info Pointing at an existing URL To set the avatar to an image you already host, send the avatar field on PUT /api/v1/characters/{character_id} instead of uploading bytes here. :::

Example​

curl -X PUT https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/avatar \
-H "X-API-Key: est_your_api_key" \
-F "image=@avatar.png"

Response​

Status: 200 OK

Returns the updated Character object, with avatar resolved to a URL. Validation failures (wrong format, oversized file) return 400.


Replace the 3D Model​

PUT /api/v1/characters/{character_id}/model-file

Upload a 3D model for the character. The request is multipart/form-data with a single model file part: a binary glTF .glb file, 150 MB maximum. The file is validated (extension, glTF magic bytes, size) before anything is stored, so an invalid file returns 400 and changes nothing.

Requires edit access to the character. When X-Player-Id is present, only the owning player may update the model; anyone else gets 403.

Uploading a model consumes no model-generation quota. That quota applies only to POST /api/v1/characters/{character_id}/model.

Example​

curl -X PUT https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/model-file \
-H "X-API-Key: est_your_api_key" \
-F "model=@character.glb"

Response​

Status: 200 OK

Returns the updated Character object, with model_status set to "completed" and model_url resolved to a URL.


Create Characters in Bulk​

POST /api/v1/characters/batch

Create up to 100 characters in one call, each with an optional knowledge bank fetched from URLs you supply. The characters exist as soon as the request returns. Document download and ingestion continue in the background, so the endpoint returns a batch that you poll until it is finished.

Request Body​

{
"requests": [
{
"customId": "npc-blacksmith",
"persona": { "name": "Torvald", "tagline": "Gruff village smith", "personality": "..." },
"tts": { "voice": "yj30vwTGJxSHezdAGsv9" },
"documents": [
{ "url": "https://cdn.example.com/lore/blacksmith.pdf", "name": "Blacksmith lore" },
{ "url": "https://files.example.com/private/guild-rules.pdf",
"headers": { "Authorization": "Bearer eyJ..." } }
]
},
{
"customId": "npc-innkeeper",
"persona": { "name": "Mara" },
"documents": [{ "url": "https://cdn.example.com/lore/blacksmith.pdf" }]
}
]
}

Each entry in requests accepts every field of Create a Character plus:

FieldTypeDescription
customIdstringRequired. Your handle for this character: 1-64 characters, letters, digits, _ and -, unique within the batch. Results are matched by customId, never by position.
documentsarrayUp to 10 documents to fetch and ingest. See document object.
document_idsarrayIds of documents you already ingested, for example ids returned by Knowledge Documents. Each must be ready and owned by you (or by the organization named in X-Org-Id). A document that is already attached to another character is copied.

Unknown fields are rejected with 422.

Document object​

FieldTypeDescription
urlstringRequired. https:// or http:// URL reachable from the public internet, up to 2,048 characters, without embedded credentials. Presigned S3 or GCS links work. Addresses on private networks, loopback, link-local and similar ranges are rejected, including after redirects, as are localhost and .local / .internal names.
namestringDisplay name, up to 200 characters. Defaults to the file name in the URL, or the file name the origin sends once fetched.
typestring"pdf" (default and the only supported type today). json, text and markdown are reserved and rejected.
headersobjectRequest headers to send when fetching a private URL, for example {"Authorization": "Bearer ..."}. At most 8 headers, 4 KB in total, 2 KB per value; hop-by-hop names such as Host, Content-Length, Proxy-* and X-Forwarded-* are refused. Encrypted at rest, dropped on cross-origin redirects, deleted once the document finishes, and never returned.

Two items that reference the same URL share one download: the first item fetches it and the other receives a copy, flagged with a document_copied warning. If the first item fails to be created, the copy fetches on its own.

X-Player-Id, when sent, is stamped on every character in the batch.

Send Idempotency-Key (any string up to 255 characters) to make a network retry safe: the same key returns the same batch for 24 hours.

Response​

Status: 202 Accepted, with Location: /api/v1/characters/batch/{id} and Retry-After: 5.

{
"id": "cb_01j9x4k2m7qz8f3e2d1c0b9a8765",
"status": "processing",
"orgId": null,
"counts": { "total": 2, "queued": 0, "processing": 2, "succeeded": 0, "failed": 0, "cancelled": 0 },
"documentCounts": { "total": 2, "queued": 2, "fetching": 0, "processing": 0, "ready": 0, "failed": 0, "cancelled": 0 },
"createdAt": "2026-09-05T18:02:11.418223Z",
"completedAt": null,
"expiresAt": "2026-10-05T18:02:11.418223Z",
"retryOf": null
}
statusMeaning
processingDocuments are being fetched and ingested.
cancellingA cancel was requested; fetches already in flight are finishing.
completedEvery item finished. Each item may have succeeded or failed; check counts.
cancelledStopped by a cancel request.
failedProcessing stalled. Remaining documents were marked failed with code executor_error; use retry.

A batch with no documents comes back already completed.

Poll a batch​

GET /api/v1/characters/batch/{batch_id}

Returns the batch with every item. The response carries Retry-After: 5 while status is processing or cancelling. Stop polling when status is completed, cancelled or failed. completed means every item finished, not that every item succeeded.

{
"id": "cb_01j9x4k2m7qz8f3e2d1c0b9a8765",
"status": "completed",
"orgId": null,
"counts": { "total": 2, "queued": 0, "processing": 0, "succeeded": 2, "failed": 0, "cancelled": 0 },
"documentCounts": { "total": 2, "queued": 0, "fetching": 0, "processing": 0, "ready": 1, "failed": 1, "cancelled": 0 },
"createdAt": "2026-09-05T18:02:11.418223Z",
"completedAt": "2026-09-05T18:05:40.102911Z",
"expiresAt": "2026-10-05T18:02:11.418223Z",
"retryOf": null,
"items": [
{
"customId": "npc-blacksmith",
"index": 0,
"status": "succeeded",
"characterId": "b275203f-91d2-4c8e-a6f1-3e2d1c0b9a87",
"documents": [
{ "name": "Blacksmith lore", "url": "https://cdn.example.com/lore/blacksmith.pdf", "documentId": "d1c0...", "status": "ready", "chunkCount": 128, "totalTokens": 33120 },
{ "name": "guild-rules.pdf", "url": "https://files.example.com/private/guild-rules.pdf", "documentId": "e2d1...", "status": "failed",
"error": { "code": "fetch_http_error", "message": "Origin returned 403", "httpStatus": 403 } }
],
"error": null,
"warnings": [
{ "code": "document_failed", "message": "1 of 2 documents failed; character was created without them", "path": "documents[1]" }
]
}
]
}

Item fields​

FieldDescription
customIdThe handle you supplied.
indexPosition in requests, counting from 0.
statusqueued, processing, succeeded, failed or cancelled. A document problem never fails the item: the character is created and the problem is reported under documents and warnings.
characterIdThe created character. Set as soon as the batch is accepted; null only when the item itself failed.
errornull unless the item failed. { "code": "executor_error", "message": "..." } means the character could not be created; retry re-creates it.
documents[]One entry per document you supplied, in order: name, url, documentId and status, plus chunkCount and totalTokens once ready, and error on failure.
documents[].statusqueued, fetching, processing, ready, failed or cancelled.
documents[].urlAlways shortened to scheme, host and path. Query strings (such as presigned signatures) are never echoed.
documents[].error{ "code", "message" }, plus httpStatus for fetch_http_error. Codes below.
warnings[]{ "code", "message", "path" } entries for things that did not go to plan without failing the item. Codes below.

Document error codes​

CodeMeaning
fetch_blockedThe URL, one of its DNS answers, or a redirect target pointed at a private, loopback, link-local or otherwise blocked address. Not retryable.
fetch_dns_failedThe host name did not resolve.
fetch_connection_errorThe connection failed or was reset.
fetch_timeoutConnecting took longer than 10 s or the whole download longer than 120 s.
fetch_http_errorThe origin answered with a non-2xx status, carried in httpStatus.
fetch_too_largeThe file is over 50 MB.
fetch_too_many_redirectsMore than 3 redirects.
unsupported_typeThe bytes are not a PDF (the content type header alone is not trusted), or type named a reserved format.
pdf_unreadableThe PDF could not be opened, for example because it is corrupt or encrypted.
too_many_pagesMore than 500 pages.
too_much_textMore than 2,000,000 characters of text. Split the file.
no_extractable_textNo text layer, for example a scanned image. Estuary does not run OCR.
ingest_failedIndexing or storage failed after the download. Usually transient; retry.
cancelledThe batch was cancelled before this document ran.
executor_errorProcessing stalled or hit an unexpected error. Retry.

Warning codes​

CodeMeaning
document_failedOne or more documents failed to ingest; path lists them (documents[1], for example). The character was created without them. Also used when a document_ids entry turned out to be in failed state.
document_cancelledOne or more documents were cancelled.
document_copiedThe document shares a URL with another item, or the document_ids entry was already attached to another character, so it was copied rather than fetched or indexed again.
text_only_inferredNo voice was configured, so text_only was set to true.
document_not_found, document_not_ready, document_link_failedA document_ids entry could not be attached while the character was being created. Submit-time validation normally catches these with 422; they appear only if the document changed in between or the copy failed.

List, retry and cancel​

GET /api/v1/characters/batch?limit=20&offset=0
POST /api/v1/characters/batch/{batch_id}/retry
POST /api/v1/characters/batch/{batch_id}/cancel

List returns { "batches": [...], "total": n, "limit": 20, "offset": 0 }, newest first, without items. Batches are scoped to your personal space or, with X-Org-Id, to that organization. Batches past their expiresAt are omitted.

Retry starts a new batch whose retryOf points at the original. It contains every failed item (and every cancelled item that never received a character), re-created from the stored request, plus, for every item that has a character, a document-only entry that re-fetches that item's failed or cancelled documents onto the same character (characterId unchanged). The optional body { "customIds": ["npc-blacksmith"], "headers": { "https://files.example.com/private/guild-rules.pdf": { "Authorization": "Bearer ..." } } } narrows the retry to some items and re-supplies headers for private URLs. Headers are deleted when a document finishes, so a retry of a private URL needs them again, keyed by the shortened URL shown in results; without them the URL is fetched anonymously. A retry counts against the same daily quota, hourly submit limit and running-batch limit as a new submit. Returns 409 while the original batch is still running and 400 when there is nothing to retry.

Cancel marks documents that have not started as cancelled (error code cancelled). Fetches already in flight finish, and every character that was created stays. The batch moves to cancelling and then cancelled. Returns 409 once the batch is already finished.

Limits and errors​

LimitValue
Items per batch100
Request body10 MB
Documents per item10
Distinct document URLs per batch100
Documents per character10
Document size50 MB, 500 pages, 2,000,000 extracted characters
Fetch10 s to connect, 120 s in total, 3 redirects
Characters created per day500 per account, counting batch submits and retries
Batch submits per hour20, counting retries and single document additions
Running batches1 per account (or per organization)
Result retention30 days, then the batch returns 404 and disappears from the list

Error bodies use the usual { "detail": ... } envelope.

StatusWhen
422The body failed validation: duplicate customId, more than 100 items, more than 100 distinct URLs, more than 10 documents on an item, a rejected URL, header or type, or an unknown field. detail is the standard validation array; each entry's loc points at the field (for example ["body", "requests", 3, "documents", 0, "url"]) and msg names the reason: duplicate_custom_id, too_many_items, too_many_documents, too_many_documents_per_item, unsupported_type, url_scheme_not_allowed, url_blocked_host, url_userinfo, url_too_long, headers_forbidden, headers_too_many, headers_too_large or headers_invalid. A document_ids entry that is missing, not yours or not ready returns detail: { "message", "errors": [{ "customId", "path", "code", "message" }] } with code document_not_found, document_not_ready or document_failed. Nothing is created.
413Body over 10 MB.
429The daily character quota would be exceeded: detail: { "message", "remaining", "limit" }, with Retry-After set to the seconds until midnight UTC. Or more than 20 submits in the last hour (Retry-After in seconds). Or a batch is already running for this account or organization: detail: { "message", "runningBatchId" }, Retry-After: 30.
409The same Idempotency-Key is being submitted right now.
404Unknown batch id, a batch older than 30 days, or a batch that belongs to another account or organization.
403X-Org-Id names an organization you are not an active member of, or the request used a share session token (sst_...), which cannot manage characters.
503Batch creation is temporarily disabled.

PDFs must contain extractable text; scanned images fail with no_extractable_text (no OCR). At conversation time, a knowledge bank of up to about 30,000 tokens in total is given to the character in full on every turn, while a larger one is searched each turn and only the relevant passages are supplied. Many small documents therefore add cost to every turn; a few large ones are answered from search.

Example​

curl -X POST https://api.estuary-ai.com/api/v1/characters/batch \
-H "X-API-Key: est_your_api_key" \
-H "Idempotency-Key: 6b1f0d2e-3c1a-4f7e-9b2d-0a5c8d1e2f34" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{ "customId": "npc-blacksmith", "persona": { "name": "Torvald" },
"documents": [{ "url": "https://cdn.example.com/lore/blacksmith.pdf" }] },
{ "customId": "npc-innkeeper", "persona": { "name": "Mara" } }
]
}'

# then poll
curl https://api.estuary-ai.com/api/v1/characters/batch/cb_01j9x4k2m7qz8f3e2d1c0b9a8765 \
-H "X-API-Key: est_your_api_key"

Knowledge Documents​

Add PDFs to a character's knowledge bank by URL, watch them ingest, download the stored copy, and remove them.

POST /api/v1/characters/{character_id}/documents
GET /api/v1/characters/{character_id}/documents
DELETE /api/v1/characters/{character_id}/documents/{document_id}
GET /api/v1/characters/documents/{document_id}
GET /api/v1/characters/documents/{document_id}/view-url

Adding or removing a document needs edit access to the character; listing, polling and downloading need read access. The player scoping rules apply, so a player cannot change a developer-created character's documents.

Add a document​

The body is one document object. Ingestion runs in the background, so the response is 202 Accepted with Location: /api/v1/characters/documents/{documentId}, Retry-After: 5, and the document id to poll:

{
"documentId": "d1c0a8e2-7f3b-4c5d-9e1f-2a3b4c5d6e7f",
"batchId": "cb_01j9x4k2m7qz8f3e2d1c0b9a8765",
"status": "queued",
"name": "Blacksmith lore",
"url": "https://cdn.example.com/lore/blacksmith.pdf"
}

batchId is a one-document batch, so retry and cancel work on it; it does not count toward the running-batch limit. A character may hold at most 10 documents that are queued, in progress or ready (400 above that). Each call counts toward the 20 submits per hour shared with batch creation (429), and returns 503 while batch creation is disabled.

Poll or list documents​

{
"id": "d1c0a8e2-7f3b-4c5d-9e1f-2a3b4c5d6e7f",
"agentId": "b275203f-91d2-4c8e-a6f1-3e2d1c0b9a87",
"filename": "blacksmith.pdf",
"status": "ready",
"contentType": "application/pdf",
"fileSize": 482113,
"chunkCount": 128,
"totalTokens": 33120,
"pageCount": 48,
"sourceUrl": "https://cdn.example.com/lore/blacksmith.pdf",
"error": null,
"createdAt": "2026-09-05T18:02:11.418223Z",
"updatedAt": "2026-09-05T18:03:40.220481Z"
}

status moves through queued, fetching and processing and ends at ready, failed or cancelled. On failure error holds { "code", "message" } (plus httpStatus for fetch_http_error) using the document error codes. sourceUrl is the shortened URL; fileSize, chunkCount, totalTokens and pageCount are filled in as ingestion progresses. Polling one document carries Retry-After: 5 until it finishes. The list endpoint returns { "documents": [...], "total": n } in every status, oldest first.

A ready document's id can be reused in document_ids when creating another character; that character receives a copy.

Download a document​

GET /api/v1/characters/documents/{document_id}/view-url

Returns a short-lived link to the stored file, available once the document is ready:

{
"url": "https://...",
"filename": "blacksmith.pdf",
"contentType": "application/pdf",
"fileSizeBytes": 482113,
"expiresInSeconds": 900
}

The link expires after 15 minutes and the response is sent with Cache-Control: no-store, so request a fresh one each time instead of storing it.

Remove a document​

DELETE returns 204 and removes the document, its search index, and the stored file when no other character references it. Returns 409 while the document is still being ingested; cancel its batch first.

Example​

curl -X POST https://api.estuary-ai.com/api/v1/characters/b275203f-91d2-4c8e-a6f1-3e2d1c0b9a87/documents \
-H "X-API-Key: est_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "url": "https://cdn.example.com/lore/blacksmith.pdf", "name": "Blacksmith lore" }'

# poll until status is ready or failed
curl https://api.estuary-ai.com/api/v1/characters/documents/d1c0a8e2-7f3b-4c5d-9e1f-2a3b4c5d6e7f \
-H "X-API-Key: est_your_api_key"

Character Object​

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Sage",
"tagline": "A wise mentor",
"personality": "A wise and patient mentor...",
"background": null,
"appearance": "A silver-haired figure in a deep blue robe",
"avatar": null,
"source_image_url": null,
"system_prompt": null,
"tts_provider": "inworld",
"tts_model": "inworld-tts-2-flash",
"tts_voice": "MF3mGyEYCl7XYWbV9V6O",
"generated_voice_id": null,
"player_id": null,
"tts_speed": 1.0,
"tts_delivery_mode": "BALANCED",
"vlm_model": "gpt-4.1-mini",
"llm_provider": "openai",
"llm_model": "gpt-5-mini",
"llm_temperature": 0.6,
"llm_max_tokens": 2048,
"llm_verbosity": "medium",
"stt_provider": "deepgram",
"stt_language": "en",
"vad_sensitivity": 0.5,
"vad_min_speech_duration_ms": 250,
"text_only": false,
"verbose": false,
"actions": [
{
"name": "wave",
"description": "Wave at the user"
}
],
"is_public": false,
"is_active": false,
"launch_status": "stopped",
"created_at": "2026-01-15T10:30:00",
"updated_at": "2026-01-15T10:30:00"
}
FieldTypeDescription
idstringCharacter UUID -- use as character_id in SDK connections and as agent_id in Conversations/Memories APIs
namestringDisplay name
player_idstring | nullOwning player ID. null means the character was created by a developer (not by a player).
taglinestring | nullShort description
personalitystring | nullPersonality description
backgroundstring | nullBackstory
appearancestring | nullVisual description of the character, used when generating a 3D model
avatarstring | nullAvatar URL
source_image_urlstring | nullThe image this character was generated from, when it was created with character generation
system_promptstring | nullSystem prompt override
tts_providerstringTTS provider
tts_modelstringTTS model
tts_delivery_modestring | nullInworld TTS-2 delivery mode (STABLE / BALANCED / CREATIVE), null when unset
tts_voicestring | nullTTS voice ID
generated_voice_idstring | nullVoice created for this character by voice generation, null when the character uses a stock voice
tts_speednumberPlayback speed
vlm_modelstringVision model
llm_providerstringLLM provider
llm_modelstringLLM model
llm_temperaturenumberSampling temperature
llm_max_tokensintegerMax tokens
llm_verbositystringResponse length preference
stt_providerstringSTT provider
stt_languagestringLanguage code
vad_sensitivitynumberVAD sensitivity
vad_min_speech_duration_msintegerMin speech duration
text_onlybooleanWhether TTS is disabled
verbosebooleanVerbose logging
actionsarrayConfigured actions
is_publicbooleanPublicly listed
is_activebooleanCurrently running
launch_statusstring"stopped", "starting", or "running"
created_atstringISO 8601 timestamp
updated_atstringISO 8601 timestamp