AvA API
REST · JSON · scoped API keys

Build assistants without the console

Everything the AvA Console does to assistants, plugins and knowledge bases, it does through these endpoints. Same API, same auth, same account — one scoped key and you can provision a whole tenant from your own CI.

Quickstart
Getting started

One base URL, one header

Every endpoint on this page is JSON over HTTPS and takes a single Authorization header. There is no SDK to install and no handshake to complete.

Base URL
https://avaai.pathor.in/api/v1
Auth
Authorization: Bearer ava_ai_…
Content type
application/json

Except the knowledge-base file upload, which is multipart.

Your first call
# Never inline the key. Read it from the environment.
export AVA_API_KEY="ava_ai_live_xxxxxxxxxxxxxxxxxxxx"

curl 'https://avaai.pathor.in/api/v1/kb' \
  -H "Authorization: Bearer $AVA_API_KEY"
Several list endpoints carry a path segment like /:sessionId or /:userId. They are vestigial — the caller is resolved from the bearer and the segment is ignored. Send any non-empty value; the console sends the literal string :dummyData.
Authentication

An API key is a non-human teammate

A key carries the same per-module capability map a person on your team does. You decide, per module, whether it can read, write or manage — so the string sitting in somebody's CI config can only do the one job it was issued for.

01
Mint it

Console → API keys → Create. Pick a preset or set each module by hand.

02
Scope it down

Assistants & knowledge covers everything on this page. Leave campaigns and WhatsApp at No access unless you need them.

03
Set an expiry

Defaults to Never. A dated key is one you find out about before somebody else does.

A key cannot mint, read or revoke API keys, manage your team, change your profile, switch accounts, take a payment or reach any platform-admin route — at any scope. Those are refused outright rather than being a checkbox, because a key that can issue itself a wider key makes revoking a leaked one pointless.
Scopes & errors

Read the 403, it tells you what to fix

Every endpoint on this page names the module and level it needs. When a key falls short the response says which module, what was required and what the key actually holds — you do not have to guess.

StatuscodeWhat happened
401invalid bearer tokenThe key does not exist, or the header was not sent as Bearer <key>.
403key_revokedThe key was revoked in the console. Revoking takes effect on the next request — reissue rather than trying to un-revoke.
403key_expiredThe key passed its expiry. The message carries the date it lapsed.
403route_forbiddenNo key may ever reach this route — key management, team, profile writes, payments, platform admin. Not a scope you can grant yourself out of.
403scope_deniedThe key is valid but holds a lower level in this module than the request needs. The body names module, required and held.
403route_not_in_scope_tableA scoped key hit a route with no rule. Scoped keys fail closed by design: a route added tomorrow is not inherited by every key issued today.
403 scope_denied
{
  "auth": false,
  "success": false,
  "code": "scope_denied",
  "module": "assistants",
  "required": "manage",
  "held": "edit",
  "message": "This API key has \"edit\" access to Assistants and this request needs \"manage\"."
}
Levels, lowest to highest:Assistants·ReadAssistants·Read & writeAssistants·Full control— higher includes everything below it.
Quickstart

Empty account to grounded assistant

Four calls, in this order. The ordering is the part worth copying: an assistant can be attached to a knowledge base only after both exist, and the retrieval query at the end is how you check it is actually being shown what you think it is.

quickstart.sh
# 1 — a knowledge base
curl -X POST 'https://avaai.pathor.in/api/v1/kb' \
  -H "Authorization: Bearer $AVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Orion product docs"}'
# → { "status": "ok", "kbId": "3f6b1c22-…" }

# 2 — put something in it
curl -X POST 'https://avaai.pathor.in/api/v1/kb/3f6b1c22-…/ingest-text' \
  -H "Authorization: Bearer $AVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Returns policy","text":"Orion accepts returns within 30 days…"}'

# 3 — an assistant (voice id from GET /users/get_voice_clone)
curl -X POST 'https://avaai.pathor.in/api/v1/persona/enterprise/createPersona' \
  -H "Authorization: Bearer $AVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Orion Support",
    "customPersonaId":"orion-support",
    "isPublic":false,
    "voice":"66f1c0a4e2b1a90012ab34cd",
    "tags":["Support"],
    "base_sys_prompt":"Answer billing and delivery questions from the attached knowledge base."
  }'

# 4 — wire them together
curl -X POST 'https://avaai.pathor.in/api/v1/kb/3f6b1c22-…/personas/orion-support' \
  -H "Authorization: Bearer $AVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"grounding_mode":"supplemental","enabled":true}'

# check what it will actually retrieve
curl -X POST 'https://avaai.pathor.in/api/v1/kb/3f6b1c22-…/query' \
  -H "Authorization: Bearer $AVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"how long do I have to return something","topK":3}'

Assistants

Create, configure and retire the personas that do the talking.

An assistant is a prompt, a voice, a set of plugins and a set of knowledge bases. Everything else in this reference hangs off one — plugins give it hands, knowledge bases give it facts.

POST

List assistants

/persona/enterprise/getAllPersonas/{sessionId}

Every assistant the key's account can see, paginated. This is the call the console's Assistants grid makes.

POST

Search assistants

/persona/enterprise/searchPersonas

The same shape as the list, filtered by a free-text term across name and title.

POST

Get one assistant

/persona/enterprise/getPersona/{sessionId}/{personaId}

The full document, including the system prompt, attached knowledge bases and plugin ids.

POST

Create an assistant

/persona/enterprise/createPersona

Mints a new assistant on the key's account. customPersonaId is unique platform-wide and is how every other call addresses it.

PUT

Update an assistant

/persona/enterprise/updatePersona

Partial update. Send only the fields you are changing — but read the array rule before you send one.

DELETE

Delete an assistant

Destructive
/persona/enterprise/deletePersona/{sessionId}/{personaId}

Permanent. The assistant, its share links and its knowledge-base attachments go with it.

POST

Mint a share-link token

/persona/enterprise/sharePersona/{customPersonaId}

Returns a con_sh_… token that lets someone talk to this one assistant without an account.

POST

Transfer ownership

Destructive
/persona/enterprise/transferOwnership/{personaId}

Hands the assistant to another account by email.

GET

Voice catalog

/users/get_voice_clone

Every voice available to the account, cloned ones included. You need an _id from here before you can create an assistant at all.

Plugins

Executable Python your assistants can call mid-conversation.

A plugin is a function definition the model is shown plus a Python body that runs when it decides to call it. Authoring one is a write; deleting one can break every assistant referencing it, so it takes Full control.

POST

List plugins

/users/enterprise/getallplugin/{userId}

The plugin catalog visible to the key's account — your own plugins plus anything published platform-wide.

POST

Get one plugin

/users/enterprise/getPlugin

One plugin, addressed by its unique title.

POST

Search plugins

/users/enterprise/searchPlugin

Free-text search across plugin titles, names and descriptions.

POST

Create a plugin

/plugin/enterprise/createGlobalPlugin

Publishes a new plugin to the account's catalog. Every field in the required list must be present — the server checks for presence, not truthiness, so send false and [] explicitly rather than omitting them.

PUT

Update a plugin

/plugin/enterprise/updateGlobalPlugin

Partial update, addressed by title.

DELETE

Delete a plugin

Destructive
/plugin/enterprise/deleteGlobalPlugin

Removes the plugin from both catalogs. Assistants that referenced it lose the capability on their next turn.

Knowledge Base

Ingest documents, then retrieve against them.

A knowledge base is a set of documents, chunked and embedded, that an attached assistant retrieves from at answer time. You can query one directly — the fastest way to find out whether an assistant is being shown what you think it is.

Knowledge Base needs a scoped key. Keys minted before scoping existed show as “Unrestricted” on the keys page and are judged by an older path allowlist that /kb was never on — every /kb call from one is a 403 route_forbidden. Reissue with a scope map and it works.
GET

List knowledge bases

/kb

Every knowledge base on the account, with its document count and size.

POST

Create a knowledge base

/kb

Returns the kbId every other call in this section takes. It is a UUID, not a Mongo _id.

DELETE

Delete a knowledge base

Destructive
/kb/{kbId}

The base, every document in it and every vector. Permanent.

POST

Ingest text

/kb/{kbId}/ingest-text

The workhorse. You extract the text, this chunks and embeds it. Use it for anything you can already read as a string.

POST

Ingest a URL

/kb/{kbId}/ingest-url

The server fetches the page, extracts its text and ingests that.

POST

Upload a file

/kb/{kbId}/ingest

multipart/form-data with a `file` part and an optional `title`. PDF, DOCX, TXT and MD.

GET

List documents

/kb/{kbId}/documents

Everything in the base, with per-document chunk counts and tier.

DELETE

Delete a document

Destructive
/kb/{kbId}/documents/{docId}

Drops the document and its vectors. The rest of the base is untouched.

POST

Query a knowledge base

/kb/{kbId}/query

Semantic search over the base — the same retrieval an assistant runs, exposed directly. Use it to check what your assistant would actually be shown.

GET

Knowledge base stats

/kb/{kbId}/stats

Counts and sizes for one base — cheap enough to poll after an ingest.

GET

Bases attached to an assistant

/kb/persona/{personaId}

Which knowledge bases one assistant can retrieve from, and how.

POST

Attach a base to an assistant

/kb/{kbId}/personas/{personaId}

Gives the assistant retrieval over this base from its next turn onward.

PATCH

Update an attachment

/kb/{kbId}/personas/{personaId}

Change grounding mode, or switch a base off for one assistant without detaching it.

DELETE

Detach a base

/kb/{kbId}/personas/{personaId}

Removes the link. The base and its documents survive.

PATCH

Schedule a re-crawl

/kb/{kbId}/auto-refresh

Re-fetch every URL-sourced document in the base on an interval.

POST

Re-crawl now

/kb/{kbId}/refresh-now

Kicks the refresh immediately rather than waiting for the schedule. Returns as soon as the job is queued.

For AI agents & LLMs

Machine-readable copies of this page

This page is a disclosure UI — fetch its HTML and you get thirty summaries with the detail still collapsed. If you are an agent, or you are handing this reference to one, use one of these instead. Both are generated from the same catalog, so they can never fall behind what is rendered above.

Addressing this page directly

Every section and every endpoint has a stable fragment. Link straight to the one that matters instead of asking someone to scroll.

/api-docs#knowledge-baseA whole section
/api-docs#kb.queryOne endpoint, arriving already expanded
/api-docs?q=ingestA search — shareable as a result list
/api-docs?try=kb.queryThe playground, open on that endpoint
/api-docs?expand=allEvery endpoint expanded, for scraping or printing

Something missing?

Campaigns, WhatsApp and journeys have API-key scopes of their own, but they place calls and message real people, so they are not documented on a page with a Send button beside every request. Ask us and we will walk you through them.