API host — no /api prefix
Resources live at the root of the API host: call https://api.example.com/chatbots, never /api/chatbots.
Browse topics
What it is
Agentency runs on two hostnames, and the single most common integration bug is calling the wrong one:
- The dashboard host — where you sign in. It serves HTML and it is not the API.
- The API host —
api.on the same domain. Everything you call from your own code lives here.
On the API host, resources start at the root. There is no /api prefix.
https://api.example.com/chatbots ✅
https://api.example.com/api/chatbots ❌ 404
If you have integrated with other products where /api/v1/... is the convention, this is the habit to unlearn. Some routes do carry a /v1 segment of their own — the knowledge ingest endpoint is /v1/knowledge/ingest — but that is part of the route, never an /api wrapper.
When you would use it
Read this the first time you point a script, a server job, or a plugin at Agentency, and again the moment a request returns a 404 that you cannot explain.
Where to find your API host
Three reliable ways, in order of convenience:
- The ingest reference. Open Dashboard → Settings → Developer. The Knowledge Ingest API card prints the full endpoint URL for your installation. Everything before
/v1/knowledge/ingestis your API base. - The widget snippet. Open Chatbots → your chatbot → Integrations → Web and copy the embed code. The
data-api-base-urlattribute is the same origin. - Ask whoever runs your installation if you are on a self-hosted or white-labelled deployment.
Steps
- Copy the API base URL from one of the sources above. Strip any trailing slash.
- Build every request as
{base}/{resource}— for example{base}/chatbots. - Create a personal access token with the narrowest scopes your integration needs. See Create a personal access token.
- Send it as a bearer token in the
Authorizationheader on every request. - Send and accept JSON:
Content-Type: application/jsonon writes,Accept: application/jsonthroughout. - Test one read call before you write any integration code.
GET {base}/chatbotsis the cheapest possible smoke test. - Read the response envelope rather than only the status code — the body carries a machine-readable
error.codewhen something is refused.
A first request
Read your chatbots. This requires a token with chatbots:read.
curl -s https://api.example.com/chatbots \
-H "Authorization: Bearer $AGENTENCY_TOKEN" \
-H "Accept: application/json"
Push a knowledge item. This requires knowledge:write, and it is the endpoint the Developer tab documents in full.
curl -X POST https://api.example.com/v1/knowledge/ingest \
-H "Authorization: Bearer $AGENTENCY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chatbot_id": 42,
"name": "Returns policy",
"content": "We accept returns within 30 days of delivery."
}'
Replace api.example.com with your own API host and 42 with a real chatbot id from Dashboard → Chatbots.
What you will see
Responses are JSON with a consistent envelope: a success flag, a data payload on success, and a human-readable message. Failures add an error object carrying a stable code, a message, and a details object — switch your error handling on error.code, never on the message text, which is localised.
Codes you will meet early:
| Status | Meaning | Fix |
|---|---|---|
401 | Missing, malformed, expired, or revoked token | Check the Authorization header, then check the key still exists |
403 insufficient_scope | The token is valid but lacks the scope this route needs | Mint a new key with the right scope — scopes cannot be widened later |
402 | A plan limit or the message-credit gate | Read error.code and details; see How message credits work |
404 | Usually the /api prefix, or a resource in another account | Drop the prefix; confirm the id belongs to your account |
422 | Validation failed | Read errors for the per-field reasons |
429 | Rate limited | Back off and honour retry_after |
Which host is which
Do not point integration code at the dashboard host. If a request returns HTML instead of JSON, that is what happened.
If your own application is built on Next.js, note that its /api/* routes belong to your app. They are not Agentency's API and they will not proxy to it.
The widget is a separate case with its own credentials. It authenticates with the public widget token in data-widget-token plus the allowed-domain list — a personal access token must never be placed in browser JavaScript, where anyone can read it. See Embed the website widget.
Limits and plan notes
Every route is rate-limited, and the limits are per surface — the chat endpoints are tighter than the read endpoints, because each chat turn costs real work. Handle 429 by backing off rather than retrying immediately in a loop.
Chat sent through the API spends message credits exactly like a visitor's message would.
Requests are scoped to the account that owns the token. A token cannot switch accounts the way you can in the dashboard: it always acts on its owner's workspace.
Ids are per account. A 404 on an id that exists is usually a sign the id belongs to a different workspace.
Common problems
Everything 404s.
Check for /api in the path first. It is the cause in most reports.
I got HTML back, not JSON.
You called the dashboard host. Use the API host from data-api-base-url or the ingest reference.
401 on a key that worked yesterday.
The key was revoked, or it reached its expiry. Keys carry an absolute expiry chosen at creation. Mint a replacement — see Rotate or revoke a key.
403 with insufficient_scope.
The route needs a scope your key does not hold. The details.required_any_of array lists which scopes would have been accepted. Scopes are fixed at creation, so create a new key. See Choose token scopes.
Requests work from my laptop and fail from my server.
Almost always the header: confirm the server is actually sending Authorization, and that the secret was not truncated on its way into your environment configuration.
Can I use my browser session token instead?
No. Dashboard sessions are for the browser and are not a supported server credential. Use a personal access token.
Common questions
Where do I find my API base URL?
From the endpoint printed on the Knowledge Ingest API card at Settings → Developer, or from the data-api-base-url attribute in your widget embed snippet.
Why does everything return 404?
Check for an /api prefix in the path first — it is the cause in most reports. Some routes carry their own /v1 segment, but there is never an /api wrapper.
I got HTML back instead of JSON.
You called the dashboard host, which serves the web app. Point your client at the API host instead.
Which error code should my client branch on?
The nested error.code in the response body, not the message text, which is localised. 401 is auth, 403 insufficient_scope is a scope gap, 402 is a plan or credit gate, 429 is rate limiting.
Does an API chat message spend a message credit?
Yes. A chat turn sent through the API is charged exactly like a visitor's message on the widget or hosted page.
Was this article helpful?
Related articles
Create a personal access token
Mint a scoped API key on Settings → Developer. The secret is shown once, carries an expiry, and is owner-only.
API reference on the Developer tab
The Knowledge Ingest API reference prints your real endpoint plus curl, JavaScript and Python snippets you can copy.
Choose token scopes
Seven scopes are grantable to a self-service key. Team, account and key-management permissions are deliberately withheld.
Embed the website widget
Copy one script tag from Integrations → Web widget, paste it on your published pages, then allow your domain and verify.
Ready to try it on your own content?
Create a free workspace, add a document, and ask the questions your team is tired of answering.