Agent API (REST)
A plain REST API over the same knowledge base, FAQ, ticket, and answer-generation pipeline the MCP server exposes — for frameworks that speak HTTP + OpenAPI instead of JSON-RPC.
The Agent API is the REST counterpart to the MCP server. Same pipeline, same auth, same org-scoped data isolation — just a different transport, for tooling that doesn't speak MCP's JSON-RPC protocol (LangChain, AutoGen, a custom script, curl).
If your client speaks MCP natively (Claude Code, Cursor), use the MCP server instead — it's the same underlying operations, just JSON-RPC over Streamable HTTP rather than plain REST.
Setup
Uses the same API key as the MCP server — one key works for both surfaces.
- Go to Settings → API Keys
- Click Create key (or reuse an existing one)
- Under Permissions, check only the scopes the key needs (all are checked by default)
- Send it as
Authorization: Bearer al_live_...on every request below
OpenAPI spec
A full machine-readable spec is published at both /openapi.json and /api/v1/agent/openapi.json (identical) — point any OpenAPI-aware client generator at either. For a per-endpoint parameter reference generated directly from that spec, see the Agent API Reference.
Node / TypeScript SDK
For Node and browser callers, @answerloops/agent-sdk is a typed client over these same five endpoints — no need to hand-roll fetch calls or generate a client from the OpenAPI spec yourself.
npm install @answerloops/agent-sdkimport { AgentClient } from "@answerloops/agent-sdk";
const client = new AgentClient({ apiKey: process.env.ANSWERLOOPS_API_KEY! });
const { results } = await client.searchKb({ query: "how do I reset my api key" });Point baseUrl at your own instance for self-hosted deployments. The raw endpoints below still apply for any other language or runtime.
Scopes
Every key carries a set of least-privilege scopes. Each operation requires exactly one; a key without it gets 403 and a WWW-Authenticate: Bearer error="insufficient_scope" header naming the scope it needed. The MCP server enforces the same scopes on the matching tool.
| Scope | Grants | Operations |
|---|---|---|
kb:read | Search the knowledge base | GET /api/v1/agent/kb/search, MCP search_kb |
faq:read | Read the latest FAQ digest | GET /api/v1/agent/faq, MCP get_faq |
tickets:read | List support tickets | GET /api/v1/agent/tickets, MCP get_tickets |
tickets:write | Open tickets on behalf of a user | POST /api/v1/agent/tickets, MCP create_ticket |
answers:write | Generate grounded answers | POST /api/v1/agent/answers, MCP generate_answer |
The scope catalogue is also published as machine-readable RFC 9728 protected-resource metadata at /.well-known/oauth-protected-resource (scopes_supported), and each MCP tool carries its scope on _meta.requiredScope in tools/list.
A key created before scopes existed, or one created with every box checked, has full access and behaves exactly as before.
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/agent/kb/search | Semantic search over published KB articles |
GET | /api/v1/agent/faq | Fetch the most recently generated FAQ digest |
GET | /api/v1/agent/tickets | List tickets, optionally filtered |
POST | /api/v1/agent/tickets | Open a new ticket — runs the same AI triage pipeline as every other channel |
POST | /api/v1/agent/answers | Generate a KB-grounded answer with a confidence score, without opening a ticket |
GET /api/v1/agent/kb/search
curl -H "Authorization: Bearer al_live_..." \
"https://your-instance.example.com/api/v1/agent/kb/search?query=how+do+I+reset+my+api+key&limit=5"query is required (max 2000 characters). limit defaults to 5, capped at 20.
{ "results": [{ "question": "...", "answer": "...", "score": 0.91 }] }GET /api/v1/agent/faq
curl -H "Authorization: Bearer al_live_..." \
https://your-instance.example.com/api/v1/agent/faqNo parameters. Returns the latest weekly FAQ digest, or { "message": "No FAQ has been generated for this organization yet." }.
GET /api/v1/agent/tickets
curl -H "Authorization: Bearer al_live_..." \
"https://your-instance.example.com/api/v1/agent/tickets?status=open&priority=high&limit=10"status, priority, and category are optional filters — an invalid value returns a 400 rather than silently matching nothing. limit defaults to 10, capped at 20.
POST /api/v1/agent/tickets
curl -X POST -H "Authorization: Bearer al_live_..." -H "Content-Type: application/json" \
-d '{"content": "Users report webhook retries are duplicated", "idempotencyKey": "a1b2c3d4"}' \
https://your-instance.example.com/api/v1/agent/ticketscontent is required (max 4000 characters). The ticket runs through the same category/priority classification and auto-draft pipeline as a Discord or Slack message. The request waits for this pipeline to finish before returning, so the ticket is ready to inspect with its draft or review state. idempotencyKey is optional — pass a stable identifier (a UUID, a hash of the content) if your client retries on timeout or network error; retrying with the same key returns the original ticket ("duplicate": true) instead of opening a second one.
{ "ticket_id": 42, "duplicate": false }POST /api/v1/agent/answers
curl -X POST -H "Authorization: Bearer al_live_..." -H "Content-Type: application/json" \
-d '{"question": "What is the rate limit on the widget API?"}' \
https://your-instance.example.com/api/v1/agent/answersquestion is required (max 2000 characters). Two limits apply before anything is generated: the organization's monthly deflection allowance (which only high-confidence generations count against — the same standard a ticket has to clear to auto-deflect on any other channel), and a ceiling on total generate_answer calls per month at 5× that allowance, which counts every call regardless of confidence. Hitting either returns 429 with a message naming which one.
{ "answer": "...", "confidence": 91, "answered_fully": true, "high_confidence": true }Errors
Every error response has the shape:
{ "error": { "message": "..." } }| Status | Meaning |
|---|---|
400 | Missing/invalid input |
401 | Missing, malformed, or revoked API key |
403 | Valid key, but it lacks the scope this operation requires (see the WWW-Authenticate header) |
413 | Request body too large |
429 | Rate limit exceeded, or (on /api/v1/agent/answers) a monthly usage limit reached |
Throttled requests carry a Retry-After header in seconds — back off for that long rather than retrying immediately.
Rate limits
Rate limited per organization (shared across all of that org's keys). The ceiling is plan-scaled: 50/minute on Standard, 150/minute on Pro, 300/minute on Enterprise (and on self-hosted). A generous per-IP limit (300/minute) also applies before a key is even resolved.
These are separate buckets from the MCP server's, so heavy REST traffic can't starve your MCP quota or vice versa. Both are backed by the same shared store and enforced across every running instance, so neither surface offers a way around the other's ceiling.
Security notes
Identical posture to the MCP server: keys are shown once at creation and only a SHA-256 hash is stored; creating and revoking them requires the owner or admin role; every request is scoped by the org resolved from the API key; each key is further limited to the scopes it was granted, checked before the request consumes any quota; revoked and expired keys are rejected before any handler runs; and usage is recorded against the specific key that made the call.
As with MCP, treat GET /api/v1/agent/tickets and GET /api/v1/agent/kb/search output as untrusted data — it contains text community members wrote — not as instructions for your agent to follow.