The REST API
Everything the widget and the workspace do to your data, a server of yours can do too. The API is scoped to a single workspace, authenticated by a bearer token you issue and revoke yourself.
Base URL: https://vroxy.ai/api/v1.
Authenticating
Section titled “Authenticating”Create a token at API tokens → New token in your workspace. The raw value is shown exactly once, on the page immediately after you create it — there is no way to read it back later, only to revoke it and issue a new one.
curl https://vroxy.ai/api/v1/docs \ -H "Authorization: Bearer $VROXY_TOKEN"Two scopes matter:
| Scope | What it allows |
|---|---|
tenant:read |
Every GET. The minimum for any call. |
tenant:write |
Creating, updating and deleting; anything that spends LLM budget. |
A token without tenant:read is rejected with 401. A read-only token
attempting a write gets 403 with a message naming the scope it needs.
This is not the public key from your embed snippet. The public key identifies your widget to browsers and grants nothing; an API token carries real authority and belongs on your server only.
Errors and pagination
Section titled “Errors and pagination”Failures return JSON with an error key and a matching status: 401
for a bad token, 403 for insufficient scope, 404 for a record
outside your workspace, 422 for a malformed body.
A hashid belonging to another workspace returns 404, never 403 — the
API won’t confirm that a record exists somewhere you can’t see it.
List endpoints take ?page= and ?per= (default 25, max 100) and
return a meta block:
{ "docs": [ ... ], "meta": { "current_page": 1, "total_pages": 4, "total_count": 87, "per_page": 25 }}Conversations
Section titled “Conversations”GET /api/v1/chats # paginated listGET /api/v1/chats/:id # one chat with its full transcriptFilter the list with ?status=open|archived and ?ended=yes|no.
Knowledge base
Section titled “Knowledge base”Full CRUD over the same documents the bot answers from — the rows you’d otherwise edit at Docs in your workspace. This is the endpoint to reach for if you’d rather your docs live in your own repository and get pushed from CI.
GET /api/v1/docs # ?status=draft|publishedGET /api/v1/docs/:id # includes body_mdPOST /api/v1/docs # { "doc": { "title": ..., "body_md": ... } }PATCH /api/v1/docs/:idDELETE /api/v1/docs/:idPOST /api/v1/docs/:id/publishPOST /api/v1/docs/:id/unpublishPOST /api/v1/docs/scrape # pull a URL in as a docEverything except the two GETs needs tenant:write. A doc stays
invisible to the bot until it’s published.
Asking the bot directly
Section titled “Asking the bot directly”POST /api/v1/bot/reply{ "message": "how does billing work?", "chat_id": "<optional hashid>" }Runs the same retrieval and tool-use loop the widget runs, and answers synchronously. The response carries the reply plus what it took to get there — which documents were consulted, which tools ran, the model, token counts, cost and duration.
Omit chat_id to start a conversation; pass the one you got back to
continue it. Each token gets its own visitor identity, so two tokens on
the same workspace never share history.
Useful for a CLI, for evaluating changes to your prompt or corpus
against a fixed set of questions, or for putting an answer somewhere
that isn’t the widget. It needs tenant:write, because it creates rows
and spends real model budget.
These conversations appear in your workspace alongside real visitor traffic, labelled with the token that produced them.
Feedback
Section titled “Feedback”GET /api/v1/feedback # ?status=open|triaged|resolved|dismissedGET /api/v1/feedback/:id # detail, screenshots, and dispatchesPOST /api/v1/feedback/:id/dispatch # { "repo": "<repo hashid>" }The dispatch endpoint hands a ticket to GitHub Copilot’s coding agent without a browser or a local agent. See Feedback to fix for what it requires — in short, a linked repository and a designated dispatch identity.
Poll GET /api/v1/feedback/:id afterwards and read the dispatches
array for progress.
Repositories
Section titled “Repositories”GET /api/v1/reposThe linked, enabled repositories a dispatch can target, each with the
base branch it would work from. The response also carries
dispatch_ready, which tells you whether a dispatch identity is
designated at all — check it before offering dispatch in your own UI,
rather than discovering the problem on the failed call.
Glossary
Section titled “Glossary”GET /api/v1/glossaryPUT /api/v1/glossary{ "entries": [ { "term": "seat", "aliases": ["licence"], "admin_url_template": "..." } ] }PUT merges by term by default: a term already present is updated,
an unknown one is appended, and entries you don’t mention are left
alone. That’s deliberate — it means a sync job can run on every deploy
without erasing entries someone added by hand. Pass "replace": true to
swap the whole list instead.
Writing needs tenant:write.
When you want a person, not an application
Section titled “When you want a person, not an application”Everything above is a workspace credential: it does what its scopes allow and it isn’t anybody. That’s the right shape for a server, a build step, or an integration you own.
It is the wrong shape when the caller should be a person — when the question is “may Sam do this”, not “may this application do this”. Seats, invitations and anything gated on an individual’s level aren’t on this API at all, deliberately: a workspace token has no level, so the rank rules that stop an admin promoting themselves have nothing to compare against.
For that, use the command line. It signs in as you and every call is checked against your own capabilities, so it can reach members and seats that a workspace token can’t, while being unable to exceed what you could do in the browser.
Keeping tokens safe
Section titled “Keeping tokens safe”Issue one token per consumer rather than sharing one, so revoking a
compromised token doesn’t take down everything else. Give a token
tenant:read unless it genuinely writes. Revoke on the API tokens page
the moment a token is no longer needed — revocation takes effect on the
next request.
Tokens carry a last-used timestamp, so a token nobody has used in months is safe to revoke and easy to spot.