Every surface is a client of one contract
Web App, MCP, Slack, Telegram, Mattermost, the extension — all of them are just clients of one public API. Deployed Achilles and want your own surface — a Teams bot, a CLI, a corporate portal — you plug it into the same entry.
/public/v1
search · versioned · OpenAPI · ACL pre-filter
All you need is to call the contract over HTTP
Achilles is API-first: every surface, ours and yours, is a
client of one public contract. The contract is a
curated, stable slice (/public/v1). Our MCP, Slack,
Telegram, Mattermost and extension are reference implementations: build the same way.
One conversation engine behind the contract →
Outward — a narrow set of operations with the version in the path: the old version lives until a declared retirement of the new one.
search
findings + sources, read-only
ask v2
a full chat turn — a ready answer with sources
OpenAPI is served out of the box (FastAPI) — the client is generated from the schema. Transport is plain REST/JSON; MCP — the same contract wrapped for AI clients.
We don’t invent our own mechanism — the same entry as at the MCP door.
ach_…
in the header · headless
A key resolves to user_id → role → ACL. A key’s scope only
narrows rights — ACL sits on top, there’s no way around it.
The one genuinely new piece — how your surface’s users become
Achilles identities. The bridge is
identity_mapping
with its own source — like 'slack' for Slack or
'telegram' for Telegram.
What the bridge looks like depends on whom the surface represents: two classes below. An account is still created by an admin via invitation — membership in your system grants no access.
A custom surface sits under the same oversight as the native ones — there’s no separate mode.
| Personal | Service | |
|---|---|---|
| Principle | a key = one employee | the surface acts for many |
| Identity | key → its user_id |
own source in identity_mapping, each external → user_id |
| Access | the owner’s personal key | a service key + identity resolution on every request |
| Reference | MCP · Extension | Slack · Telegram · Mattermost |
A service surface goes through the shared “act on behalf of” path
— a service key with its own source and presenting the identity on
every request. Slack, Telegram and Mattermost are only references for this
mechanism, not code to fork: your surface uses the same path without
touching the platform.
Working examples of the contract — build yours by looking at the closest one to your task.
/new
Mattermost
the same class on a self-hosted server: identity bridge, thread = conversation
Extension
a personal client in the browser over any page
POST /public/v1/search Authorization: Bearer ach_… { "query": "release 4.2 status", "limit": 8 }
200 OK { "results": [ { "title": "RELEASE-482", "snippet": "Release 4.2 in regression tests…", "source": "ticket", "url": "https://jira…/RELEASE-482", "score": 0.82 } ], "degraded": false }
Search runs under the key owner’s ACL — what they can’t see
won’t appear in the results. Answer synthesis is on the client side;
degraded flags a result set without the vector leg (the embedder was silent). v2 will add
POST /public/v1/ask — a ready answer with sources in a single call.
identity_mapping → user_id bridge.
→ Auth / Security