Integration Overview
This section is for your IT and integration team. Business users can build, test, and launch agents entirely in the console. Your engineers are needed for a few one-time connections: placing the chat widget on your site, telling Achilles who the logged-in user is, giving the agent read access to your data, and, if you use them, connecting messaging channels. Each is small and well defined, and most teams finish in days.
What you will do
| Task | Who does it | Typical effort |
|---|---|---|
| Add the widget script to your site | Web developer | An hour |
| Issue signed user tokens from your backend | Backend developer | A day |
| Create a read-only database or API account for the agent | Database administrator | A few hours |
| Enter credentials in the console | Workspace admin | Minutes |
| Allow-list origins and network paths | Network or security team | Varies |
| Connect WhatsApp or Instagram webhooks | Channel owner | A few hours |
Embedding the widget
The widget is one script tag. It runs inside an isolated container, so your page styles cannot break it and it cannot affect your page.
<script
src="https://widget.achillessystems.co/widget.js"
data-agent-key="your-agent-key"
data-api="https://api.example.com"
data-token="SIGNED_USER_TOKEN"
></script>
| Attribute | Required | Purpose |
|---|---|---|
data-agent-key | Yes | Identifies the agent. Shown in the console under Deployments. |
data-api | Yes | Your Achilles API address. |
data-token | One of these three | A widget token your backend minted for the logged-in user (backend minting). |
data-token-endpoint | One of these three | A route on your backend that returns a fresh widget token (single sign-on). The widget calls it on load and before each token expires. |
data-publishable-key | One of these three | The channel's pk_live_… key (guest mode). Public; works only from the channel's allowed origins. |
data-position | No | bottom-right (default) or bottom-left. |
data-theme | No | auto, light, or dark. |
data-color | No | Primary brand color, for example #4f46e5. |
data-title | No | Header title override. |
data-voice | No | false hides the microphone. |
Which of the three token attributes you use depends on the channel's identity mode, described in the next section. The console's Deployments → Channels → Web Widget page shows the exact snippet for each channel in Step 4.
Three keys are involved. Only the channel key is secret.
| Key | Prefix | Where it lives |
|---|---|---|
| Channel key | chn_live_… | Your backend's environment only. Sent as X-Channel-Key. Shown once. |
| Agent key | the agent's embed key | The page (data-agent-key) and API calls (agentKey). |
| Publishable key | pk_live_… | The page (data-publishable-key), guest mode only. Shown once. |
The business-side view of this step is in Website widget.
Passing signed user identity
Achilles never trusts the browser to say who someone is. Your backend, which already knows the logged-in user, issues a short-lived signed token, and the widget presents it on every message. The token carries the user id, role, and tenant, which decide what data the agent may show. If any required claim is missing, the request is refused rather than run with broad access.
Each Web Widget channel has one identity mode, chosen in Step 2 of the channel page:
| Mode | How your backend gets a token | Best for |
|---|---|---|
| Backend minting (default) | POST /agent/token with X-Channel-Key, asserting the user id and role in the body. | Quick starts and internal tools |
| Single sign-on (token exchange) | POST /auth/token-exchange with X-Channel-Key and the user's own signed login token. Achilles verifies it against your public key and maps its claims. | Production apps with their own login (recommended) |
| Guest | None. The widget calls POST /auth/guest-token itself with the publishable key. | Public sites with no login |
A channel created under Deployments decides which roles may be issued and which site origins may use the tokens, whatever the mode.
Backend minting
Your backend asserts who the user is. It is simple, but anyone holding the channel key can mint a token for any allowed role, so protect the key accordingly.
curl -X POST https://api.example.com/agent/token \
-H "Content-Type: application/json" \
-H "X-Channel-Key: $ACHILLES_CHANNEL_KEY" \
-d '{ "agentKey": "your-agent-key", "role": "viewer", "userId": "u-123" }'
# → { "token": "eyJ...", "expiresIn": 900 }
Single sign-on (token exchange)
Your backend forwards the token your own login already issued, so Achilles never has to take your word for who the user is. Once a channel is switched to this mode, POST /agent/token stops working for it, and a leaked channel key alone can no longer impersonate users.
Set it up once per agent in Step 2 of the channel page:
- Trust the host's tokens. Choose the signing algorithm (RS256 recommended; a shared-secret HMAC is possible but not advised) and where your public keys live: a public HTTPS JWKS URL (rotations are picked up automatically) or one pasted PEM or JWK key. Enter the expected issuer (
iss) and audience (aud), the claim that holds the role, and a claim for every identity field your row filters use, such asagentIdorbranchId. An unmapped role is rejected, never downgraded. - Test with a real token. Paste a token from your login system and see the identity Achilles would derive, before any traffic depends on it.
- Switch the channel to single sign-on.
Then add one route to your backend and point the widget at it with data-token-endpoint:
app.get('/api/chat-token', requireLogin, async (req, res) => {
const upstream = await fetch('https://api.example.com/auth/token-exchange', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Channel-Key': process.env.ACHILLES_CHANNEL_KEY,
},
body: JSON.stringify({
subject_token: req.user.jwt, // the user's own signed token
origin: 'https://your-app.example', // the page that shows the widget
}),
});
if (!upstream.ok) return res.status(502).json({ error: 'chat unavailable' });
res.json(await upstream.json()); // { widget_token, expires_in, token_type }
});
Single-page apps can pass a tokenProvider function to the widget instead of a URL.
Guest mode
For public sites with no login. Every visitor chats as one role, and the page carries the channel's publishable key. Guest tokens last 15 minutes by default and only work from the channel's allowed origins. You can cap messages per day and per minute across all visitors.
Achilles refuses a guest role that:
- can run tools that change data, unless the role denies writes;
- has row filters needing identity fields a guest never has (guests carry only
userId,agentId,roleandtenantId); - is exempt from personal-data masking.
Switching a channel out of guest mode deletes the publishable key, so the public embed stops working until its code is changed.
Sign it yourself
Teams that cannot call an external service per page load can have their backend sign a standard HS256 token with a shared secret held by their Achilles administrator.
Token claims:
| Claim | Meaning |
|---|---|
userId | Required. Identifies the person in sessions and audit records. |
role | Required. One of your agent roles, or a role name from your own system mapped in Roles and access. |
tenantId | Required. Scopes data and sessions. |
agentId, branchId | Optional. Identifiers your data filters can use, such as an advisor id or branch. |
| Extra claims | Any additional values your data rules reference, such as a customer id. |
Tokens last 15 minutes by default and at most one hour. To cut access early, for example on logout, your backend calls POST /auth/revoke with the channel key and either a token's jti (one token) or a user_id (every token issued to that user so far; logging back in still works). If your tokens already use different claim names, ask your administrator to map them instead of re-issuing.
Mint tokens only from your server, never in browser code. The full request format is in the API reference.
The REST API at a glance
Everything the console does is available over HTTPS.
| Area | Purpose |
|---|---|
| Chat and confirm | Send a message, approve or cancel a pending action |
| Streaming and voice sessions | Live typing and spoken conversations over secure sockets |
| Definitions | Create, validate, version, and restore agents |
| Sessions and traces | Read conversations and audit events for your reporting |
| Evaluations | Run your test cases from a CI pipeline |
| Health | A no-login endpoint for uptime monitoring |
Administrative calls accept a person's console session or a shared admin key for automation. Keep the admin key in your secrets manager and rotate it on your normal schedule. See the API reference.
Datasource credentials
Your agent reads from your systems through credentials you provide. Create a dedicated, read-only account for each system (a database user, or an API key limited to the endpoints the agent needs) and enter it in the console under Datasources. Credentials are encrypted when stored and are never shown again after saving, and they are never written into the agent definition itself. Test the connection on the same page before you save.
Restrict the account to the tables the agent needs. Achilles adds its own row-level limits per role, but least privilege at the source is your second line of defense. For background see Connect your data.
Network and security considerations
- Outbound from Achilles. The API must reach your database or service, and your chosen AI model provider. Allow-list the Achilles API address on your database firewall, or use a private connection if you self-host.
- Inbound to Achilles. Browsers reach the API over HTTPS. Voice and streaming need WebSocket support through any proxy or gateway.
- Allowed origins. Only listed website origins may call the API from a browser, and widget tokens are bound to them. List each origin as scheme, host and port with no path. Edit them any time under Security on the channel, and remove staging origins before go-live.
- Channel key hardening. Under Security you can also pin the channel key to your backend's outgoing IP addresses (single addresses or CIDR ranges), so a leaked key is refused off your network, and give it an expiry date.
- Key rotation. Rotate key issues a new channel key and keeps the old one working for 7 days so you can deploy without an outage; the overlap can be ended early. Leaked? Rotate now revokes the old key immediately, and anything still using it gets
401at once. - Separate secrets. Console logins and end-user tokens use different signing secrets, so a console login can never act as a customer.
- Rate limits. Chat is limited per user, 30 messages a minute by default, and token issuing is limited per key.
- Personal data. Masking rules are applied by the platform before the model sees results. See Personal data.
- Audit. Every request, permission decision, and admin change is recorded. See Audit trail.
Channel webhooks
WhatsApp and Instagram messages arrive at a single webhook address on your API. In the console, create the channel under Deployments, then paste the webhook address and the one-time verify token into your Meta app settings. Every incoming message is checked against a signature from your Meta app secret, queued before it is acknowledged so nothing is lost, and retried with backoff on transient failures.
Because messaging senders are not logged in, every sender on a channel is given one default role. Choose the narrowest role that supports the conversation. Channel setup from the business side is in Messaging channels.