REST API
The v1 API is for server-to-server use: n8n, Zapier, cron jobs, your own backend. It answers questions with the same retrieval, settings and custom actions a visitor gets.
Base URL — https://engadin.ai/api/v1
Authentication
Section titled “Authentication”Create a key on Developers → API and send it as a bearer token:
Authorization: Bearer <api key>A key can be scoped to one workspace, and optionally locked to a single agent. An agent-locked key cannot be pointed at another agent, even by naming it.
Every call updates the key’s last-used timestamp and is written to the call log shown on the API page.
Being authenticated, API callers skip the anonymous-visitor defences — Turnstile, origin checks, per-IP caps — but still hit the per-key burst limit and the platform-wide daily message budget.
Access level
Section titled “Access level”Every key declares, when it is created, what it is allowed to do. There is no default — the choice is required.
| Access | May call | Notes |
|---|---|---|
| Read only | GET /agents | Reads metadata. Cannot change anything. |
| Read and write | all endpoints | Adds POST /chat, which records a conversation, spends model budget and can trigger your custom actions. |
A read-only key calling a write endpoint gets 403 with an error explaining
why. GET /agents echoes the key’s own level in the access field, so an
integration can check what it holds without trial and error.
Keys created before access levels existed keep full read and write access.
Per-key settings
Section titled “Per-key settings”Beyond the access level, each key carries its own limits. Every one of them can be changed later without re-issuing the secret, and every one is optional: left alone, the key is unrestricted.
| Setting | Default | What it does |
|---|---|---|
| Expiry | Never | After the date, the key answers 401. Picking a duration always counts from the moment you pick it, so re-picking extends it. |
| Custom actions | Allowed | Switch off and the key can still ask questions, but the agent’s custom actions are withheld — no mail, no webhooks, no tickets. Read-only keys never reach actions anyway. |
| Calls per minute | 120 | A ceiling on how fast the key may call, counted from its own call log. |
| Calls per month | 50,000 | The same, per UTC calendar month. Both are runaway-workflow guards rather than plan limits; clear a field for no cap. |
| Allowed IPs | any | A list of addresses or CIDR blocks (IPv4 or IPv6). Enforced only when set: a call from anywhere else gets 403, so a leaked key is useless off your network. |
| Note | — | Free text: who owns the key, which system uses it. |
Exceeding either cap returns 429 naming the cap that was hit. The caps are
counted from calls already logged, so they contain runaway loops rather than
policing exact concurrency — the per-key burst limiter does that.
Rotation
Section titled “Rotation”Rotating a key issues a new secret immediately and keeps the old one working
for a grace period you choose: not at all, one hour, 24 hours, or seven days.
Both secrets are accepted during the window, so a running integration can be
updated without downtime; calls that arrive on the old secret carry an
X-Api-Key-Rotated response header naming the moment it stops working. When
the window ends the old secret returns 401.
Everything else about the key — its id, name, settings, call history and usage counters — survives rotation.
Revoking, by contrast, is final: both secrets stop working at once and the key cannot be brought back.
GET /agents
Section titled “GET /agents”The agents this key can reach.
curl https://engadin.ai/api/v1/agents \ -H "Authorization: Bearer $ENGADIN_API_KEY"{ "access": "write", "agents": [ { "id": "acme", "name": "Acme Assistant", "status": "active", "site": "https://acme.example" } ]}GET /usage
Section titled “GET /usage”Billable conversation counts for one calendar month. Read-only keys may call it.
This is the endpoint to point a billing system at. It returns counts, never money. How a conversation is priced (per unit, with a cap, with voice weighted heavier) lives in the contract, and the contract lives in whatever system issues the invoice.
curl "https://engadin.ai/api/v1/usage?month=2026-09" \ -H "Authorization: Bearer $ENGADIN_API_KEY"{ "month": "2026-09", "org": "acme-org", "timezone": "Europe/Zurich", "from": "2026-08-31T22:00:00.000Z", "to": "2026-09-30T22:00:00.000Z", "final": true, "totals": { "conversations": 2416, "voice": 37, "dictation": 12, "messages": 9871 }, "byAgent": [ { "agent": "acme", "name": "Acme Assistant", "conversations": 2416, "voice": 37, "dictation": 12, "messages": 9871 } ]}Query parameters
Section titled “Query parameters”| Field | Type | Notes |
|---|---|---|
month | string | YYYY-MM. Omit for the most recently completed month, which is what a biller running on the 1st wants. |
agent | string | Restrict to one agent. Omit for every agent the key can reach, summed in totals. |
tz | string | IANA zone the month is named in. Defaults to Europe/Zurich. |
Response
Section titled “Response”| Field | Notes |
|---|---|
org | The organization these counts belong to. Check it against the customer you are billing: a key created in the wrong workspace answers happily, and its numbers would land on the wrong invoice. |
final | false while the month is still running. Never invoice a month whose final is false: read the field rather than comparing dates yourself. |
from / to | The half-open UTC window [from, to) the counts cover. A civil month, so these are local midnight, not 00:00Z. |
totals.conversations | Every conversation started in the window, across the agents in scope. |
totals.voice | Of those, the ones held as spoken conversations (voice-to-voice). |
totals.dictation | Conversations where the mic was used for input only. Not counted in voice. |
totals.messages | Messages exchanged across those conversations. |
Counts for a closed month do not change: conversations are recorded as they happen and never backdated, so the same request months later reproduces the figure that was invoiced.
Paused agents still report their conversations, because pausing an agent does not un-happen the traffic it already served.
POST /chat
Section titled “POST /chat”Ask a question. Returns the complete answer — this endpoint does not stream. Needs a read and write key.
curl https://engadin.ai/api/v1/chat \ -H "Authorization: Bearer $ENGADIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"message": "Do you ship to Switzerland?", "agent": "acme"}'Request
Section titled “Request”| Field | Type | Notes |
|---|---|---|
message | string | Required. Subject to the platform’s maximum message length. |
agent | string | The agent’s embed name, or any name it has previously had. Optional when the key is agent-locked, or when the workspace has exactly one active agent. |
conversationId | string | From a previous response. Continues that conversation. |
pageUrl | string | Stored on the conversation for attribution. |
Response
Section titled “Response”{ "conversationId": "1b9f…", "answer": "Yes — Switzerland is in our standard EU zone…", "sources": [ { "url": "https://acme.example/shipping", "title": "Shipping and delivery" } ]}Pass conversationId back on the next call to keep the thread. The last
twelve turns are used as history. Without it, every call starts fresh.
Errors
Section titled “Errors”| Status | Meaning |
|---|---|
400 | Missing or over-long message; ambiguous agent when the key can reach several; agent conflicts with an agent-locked key. |
401 | Missing, unknown, revoked or expired key, or a rotated key’s old secret past its grace period. |
403 | The key is read-only and the endpoint writes, or the call came from an address outside the key’s allowlist. |
404 | Unknown agent, agent paused, or unknown conversationId for that agent. |
429 | The key’s per-minute or monthly cap, the per-key burst limit, or the platform’s daily message budget. |
502 | The model failed to answer. The question is still recorded; retry. |
The error body is always {"error": "…"} with a message written to be read by
whoever is debugging the integration.
What API conversations look like in the dashboard
Section titled “What API conversations look like in the dashboard”They appear in Inbox → Conversations like any other, marked as having arrived through the API. Lead extraction, embedding and custom actions all work the same way — an n8n workflow can trigger a real action, and the run is logged against the conversation.
Example: a nightly digest
Section titled “Example: a nightly digest”const res = await fetch('https://engadin.ai/api/v1/chat', { method: 'POST', headers: { Authorization: `Bearer ${process.env.ENGADIN_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ agent: 'acme', message: 'Summarise our return policy in three bullet points.', }),});const { answer, sources } = await res.json();For reading conversations, leads, analytics and gaps rather than asking questions, use the MCP server — it exposes those as tools and needs no endpoint-by-endpoint integration.