API reference
Everything the dashboard does, it does through this HTTP API — so anything you see in the console you can automate. JSON in, JSON out, session-based auth.
Base URL
https://avatarx.net/api
The API and the app are served from the same origin, so requests from a signed-in browser session are same-site — there is no CORS configuration to maintain. From a server or script, call it directly.
Interactive docs
A generated OpenAPI browser is available at/docs when the server runs with debug enabled. This page is the hand-written summary.Authentication
Sign in once and you get a server-side session. The response sets an HttpOnly session_id cookie and returns the same id in the body, so scripts that cannot hold cookies can send it as a bearer token instead. Both are accepted.
curl -X POST https://avatarx.net/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"••••••••"}'
# {
# "session_id": "…",
# "token_type": "bearer",
# "expires_at": "2026-10-21T09:12:00",
# "user": { "id": "…", "email": "you@example.com", "is_superuser": false }
# }curl https://avatarx.net/api/agents/ \ -H 'Authorization: Bearer YOUR_SESSION_ID'
Sessions are revocable server-side, so there is no refresh flow: a 401 means the session is gone and you should sign in again.
/api/auth/registerCreate an account/api/auth/loginStart a session/api/auth/meCurrent user/api/auth/logoutEnd this session/api/auth/logout-allEnd every session/api/auth/change-passwordRotate the password/api/auth/sessionsList active devices/api/auth/sessions/{session_id}Revoke one device/api/auth/google/urlGoogle consent URL/api/auth/google/completeFinish a Google flowConventions
- Bodies are JSON unless the endpoint takes a file, in which case it is
multipart/form-data. - Ids are UUID strings. Everything is scoped to the caller: another account's agent returns
404, never403. - Errors carry a
detailfield. Validation errors return422with an array of field problems. - Deletes answer
204with no body. Long jobs (indexing, crawling, avatar prep) answer202and you poll for status. - On
PATCH, an omitted key is left alone and an explicitnullclears the column — that is how you remove a word cap or a topic list.
{ "detail": "Agent not found" }Agents
/api/agents/List your agents/api/agents/Create one/api/agents/{agent_id}Fetch one/api/agents/{agent_id}Update/api/agents/{agent_id}Delete/api/agents/templatesStarter templates/api/agents/{agent_id}/effective-promptComposed prompt + temperatureCreating an agent
Only name and system_prompt are required. Send behaviour as structured fields — role, personality, topics to avoid and the word cap are composed into the prompt server-side at call time, so they stay readable and editable instead of being baked into prose.
{
"name": "Support desk",
"system_prompt": "You help visitors with billing and account questions.",
"personality": "Friendly and Professional",
"agent_role": "Customer support specialist",
"language": "en",
"voice_id": "db6b0ed5-d5d3-463d-ae85-518a07d3c2b4",
"musetalk_avatar_id": "ava",
"llm_provider": "openai",
"llm_model": "gpt-4o",
"creativity": 0.4,
"knowledge_mode": "strict",
"greeting": "Hi! How can I help today?",
"conversation_starters": ["Where is my order?", "How do I cancel?"],
"topics_to_avoid": ["competitor pricing"],
"max_response_words": 80,
"is_public": true
}| Field | Type | Notes |
|---|---|---|
creativity | 0–1 | Maps to model temperature; effective_temperature is returned. |
knowledge_mode | "strict" | "hybrid" | Alias for restrict_to_knowledge. |
topics_to_avoid | string[] | null | ≤ 20 items, ≤ 80 chars each. |
conversation_starters | string[] | null | ≤ 4 items, ≤ 120 chars each. |
max_response_words | int | null | 10–500, or null for no cap. |
pronunciations | { word, say_as }[] | ≤ 50 entries. |
is_public | bool | Must be true for the embeddable widget to load. |
Text chat
/api/agents/preview/chatTry an unsaved prompt/api/agents/{agent_id}/chatChat with a real agentThe agent endpoint composes the prompt, retrieves from the knowledge base and matches media, returning the reply plus whatever it decided to show. The preview endpoint persists nothing and is rate-limited to 20 messages a minute per user.
{ "messages": [{ "role": "user", "content": "do you ship to Spain?" }] }
# {
# "reply": "Yes — standard delivery to Spain takes 3–5 days.",
# "media": { "id": "…", "kind": "image", "title": "Shipping rates", "url": "/api/media/…/file" },
# "used_knowledge": true
# }Knowledge
/api/knowledge/agents/{agent_id}/summaryCounts per source/api/knowledge/agents/{agent_id}/bootstrapText + URLs in one call/api/knowledge/agents/{agent_id}/search?q=&k=What the agent would retrieve/api/knowledge/agents/{agent_id}/kb/List knowledge bases/api/knowledge/agents/{agent_id}/kb/Create one/api/knowledge/agents/{agent_id}/kb/{kb_id}Rename/api/knowledge/agents/{agent_id}/kb/{kb_id}Delete/api/knowledge/agents/{agent_id}/kb/{kb_id}/documentsUpload a file (multipart)/api/knowledge/agents/{agent_id}/kb/{kb_id}/documents/textAdd pasted text/api/knowledge/agents/{agent_id}/kb/{kb_id}/documentsList documents/api/knowledge/agents/{agent_id}/kb/{kb_id}/documents/{doc_id}/contentRead full text/api/knowledge/agents/{agent_id}/kb/{kb_id}/documents/{doc_id}/contentEdit + re-embed/api/knowledge/agents/{agent_id}/kb/{kb_id}/documents/{doc_id}Rename only/api/knowledge/agents/{agent_id}/kb/{kb_id}/documents/{doc_id}/reindexRe-run indexing/api/knowledge/agents/{agent_id}/kb/{kb_id}/documents/{doc_id}Delete a document/api/knowledge/agents/{agent_id}/kb/from-urlAttach a website/api/knowledge/agents/{agent_id}/kb/from-url/statusCrawl status/api/knowledge/agents/{agent_id}/kb/{kb_id}/recrawlRe-crawlbootstrap is the one to use when setting an agent up: it creates the default knowledge base if absent, stores pasted text, queues a crawl per URL, and reports each part separately.
{
"text": "We ship worldwide. Returns accepted within 30 days.",
"urls": ["https://example.com/faq"],
"restrict_to_knowledge": true
}
# 202 { "kb_id": "…", "created": { "text": true, "urls": ["https://example.com/faq"] }, "errors": [] }Uploads accept PDF, TXT, MD and DOCX up to 20 MB. Documents return pending → processing → ready; poll the document list while anything is in flight.
Media
/api/agents/{agent_id}/mediaList items/api/agents/{agent_id}/mediaUpload a file, or JSON for a link/api/agents/{agent_id}/media/{media_id}Edit title, keywords, order/api/agents/{agent_id}/media/{media_id}Delete (also unlinks the file)/api/media/{media_id}/fileStream the file — public for a public agentcurl -X POST https://avatarx.net/api/agents/AGENT_ID/media \ -H 'Authorization: Bearer YOUR_SESSION_ID' \ -F 'file=@spring-flyer.png' \ -F 'title=Spring flyer' \ -F 'trigger_keywords=flyer,offers,discount' \ -F 'show_by_default=false'
PNG, JPG, WEBP, GIF, SVG, PDF, MP4 and WEBM are accepted, up to 25 MB and 50 items per agent.
Tools
/api/toolsSystem + custom tools/api/toolsCreate a webhook tool/api/tools/{tool_id}Update (custom only)/api/tools/{tool_id}Delete (custom only)/api/tools/{tool_id}/testDry-run against the real endpoint/api/agents/{agent_id}/toolsWhat this agent runs with/api/agents/{agent_id}/toolsReplace the attached set{
"name": "check_order_status",
"description": "Look up an order by its number. Call this whenever a caller asks where their order is.",
"method": "POST",
"url": "https://api.example.com/orders/status",
"auth_type": "bearer",
"auth_config": { "token": "•••" },
"timeout_seconds": 10,
"parameters": {
"type": "object",
"properties": { "order_number": { "type": "string" } },
"required": ["order_number"]
}
}auth_config is write-only: it is stored encrypted and never returned. Responses carry auth_type and a has_credentials flag instead. System tools appear with system: ids and cannot be edited or deleted.
Outbound guard
Tool URLs must be HTTPS and must not resolve to loopback, private, link-local or CGNAT ranges — checked again after redirects, with at most two hops, a 32 KB response cap and a 30 second ceiling.CRM & scheduling
/api/agents/{agent_id}/crm/settingsCapabilities + scheduling rules/api/agents/{agent_id}/crm/settingsUpdate them/api/agents/{agent_id}/crm/summaryCounters/api/agents/{agent_id}/crm/leadsList leads/api/agents/{agent_id}/crm/leadsCreate manually/api/agents/{agent_id}/crm/leads/{lead_id}Edit status or fields/api/agents/{agent_id}/crm/leads/{lead_id}Delete/api/agents/{agent_id}/crm/leads/export/csvCSV export/api/agents/{agent_id}/crm/appointmentsList bookings/api/agents/{agent_id}/crm/appointmentsBook manually/api/agents/{agent_id}/crm/appointments/{appt_id}Cancel / complete / no-show/api/agents/{agent_id}/crm/availabilityFree slots the agent would offer{
"enable_lead_capture": true,
"enable_appointments": true,
"enable_human_handoff": true,
"meet_link": "meet.google.com/abc-defg-hij",
"appointment_config": {
"timezone": "Europe/Madrid",
"slot_minutes": 30,
"buffer_minutes": 10,
"min_notice_minutes": 60,
"max_days_ahead": 14,
"business_hours": {
"mon": [["09:00", "13:00"], ["14:00", "17:00"]],
"tue": [["09:00", "17:00"]],
"sat": [], "sun": []
}
}
}Avatars & voices
/api/avatars/catalogueEvery avatar you can pick/api/avatars/mineYour uploads and their status/api/avatars/Upload one (multipart)/api/avatars/{avatar_id}/previewPreview image/api/avatars/{avatar_id}Delete/api/voices/Available voices/api/voices/preview?url=Stream a voice sampleCustom avatars are not finished
Uploads are stored and tracked, but there is no preparation pipeline yet: photo avatars settle as ready and video uploads come back failed with an explanation. The gallery avatars work today.Conversations & analytics
/api/conversations/sessionsList conversations/api/conversations/sessions/{session_id}One conversation + messages/api/conversations/sessionsOpen a session/api/conversations/sessions/{session_id}/messagesAppend a message/api/conversations/sessions/{session_id}/endClose it/api/analytics/overviewHeadline numbers/api/analytics/timeseriesVolume over time/api/analytics/agentsPer-agent breakdown/api/credits/{agent_id}/balanceRemaining minutes/api/credits/{agent_id}/topupAdd minutesLive calls are brokered by POST /api/livekit/join, which checks credits, issues a room token and dispatches the agent worker. It answers 402 with credits_exhausted when the balance is gone.
Public endpoints
These need no session — they are what the embedded widget calls from a visitor's browser.
/api/widget/{agent_id}/configName, greeting, starters, media, avatar, voice/api/widget/{agent_id}/onboardingWhether to show the pre-chat form/api/widget/{agent_id}/onboardingSave visitor answers/api/media/{media_id}/fileMedia file for a public agent/healthService healthThe config endpoint only serves agents that are public and active, and never returns the system prompt or any account data.
Settings & support
/api/settings/smtpOutbound email config (password never returned)/api/settings/smtpUpdate it/api/settings/smtp/testSend a test message/api/settings/smtpRemove it/api/settings/googleCalendar connection status/api/settings/googleDisconnect/api/ticketsYour support tickets/api/ticketsOpen one/api/tickets/{ticket_id}/replyReply/api/tickets/{ticket_id}/closeCloseAdministrator accounts have a further /api/admin/* surface (accounts, agents, sessions, audit log, health, tickets) which always requires a real superuser session.
End-to-end example
Sign in, create an agent, give it knowledge, and check what it retrieves.
BASE=https://avatarx.net/api
# 1. sign in
SID=$(curl -s -X POST $BASE/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"••••••••"}' | jq -r .session_id)
AUTH="Authorization: Bearer $SID"
# 2. create the agent
AID=$(curl -s -X POST $BASE/agents/ -H "$AUTH" -H 'Content-Type: application/json' \
-d '{
"name":"Support desk",
"system_prompt":"You help visitors with billing and account questions.",
"knowledge_mode":"strict",
"greeting":"Hi! How can I help today?",
"is_public":true
}' | jq -r .id)
# 3. give it something to know
curl -s -X POST $BASE/knowledge/agents/$AID/bootstrap -H "$AUTH" \
-H 'Content-Type: application/json' \
-d '{"text":"Refunds are issued within 30 days of purchase.","urls":[]}'
# 4. see what it would retrieve (indexing takes a moment)
curl -s -G $BASE/knowledge/agents/$AID/search -H "$AUTH" --data-urlencode 'q=refund window'
# 5. talk to it
curl -s -X POST $BASE/agents/$AID/chat -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"can I get a refund after 3 weeks?"}]}'Then embed it — see Embedding the widget.