Wire a partner website to KeenAgents end to end — from credentials to live chat and staying in sync, in order.
The wiring is done in the admin before you write a line of integration code.
Before a site can connect, a KeenAgents admin builds the access chain in this order: the space and the agent your site will run; a Certificate (the signing material, optionally with a shared secret key); a Client (your server's identity — its secret is shown once); a Consumer Contract (your policy, the spaces it offers and the default spaces new consumers get); a Webhook entry (your site's exact browser Origin plus the notification URL on your server); and finally the Api Key, binding all four. When those four are bound the key is fully bound and your Origin is on the allow-list. The admin then hands you three things: your Client id and secret, your Api Key id and secret (both secrets are shown once), and confirmation of the Origin and notification URL registered for you — and presses Rebuild Resource Cache and Wake on the key so your notification URL receives the certificate material and a first refresh notice.
Origin header, matched exactly on scheme://host[:port] against the one the admin registered (only the default ports 80/443 are omitted; no wildcards). A request from an unregistered Origin is silently dropped — you see a connection reset or a generic proxy error on your side, never a descriptive one — so an unknown caller learns nothing. If connections fail with no message, check the Origin first.Everything the admin does above, an AI agent can do through the MCP server — under the same rules.
Everything on this page that an admin does in the console, an AI agent can do through the Keen MCP Server at https://<your KeenAgents host>/mcp. The agent signs in with an MCP account — an id plus a secret shown once — using mcp_login, and then operates as that account, under its role's scopes: create_certificate, create_client, create_consumer_contract / update_consumer_contract_policy / assign_contract_spaces / assign_contract_default_outside_spaces, create_webhook, create_api_key / update_api_key (bind the four), rebuild_api_key_cache, wake_api_key, and the matching list_*, get_*, rotate_* and delete_* tools.
The same rules apply as in the console — the four bindings, reveal-once secrets, a half-wired key refused by rebuild and Wake with a message naming what is missing — and every action is recorded in the audit log under the MCP account. An MCP account can never manage human accounts, roles or other MCP accounts, whatever its role.
Server-side first, then the browser — in this order.
spaces — that set is the boundary of what your consumers may use, and a chat run outside it is refused.Interface-level shapes — every path below is relative to https://<your KeenAgents host>/keen-api (the host is provided at onboarding); the /v1 prefix in the examples is replaced by /keen-api.
1 · Application token (server-side, once — no expiry and no refresh; request a new one if it stops verifying)
POST /v1/auth/application_token?client_id=kcid_… Origin: https://your-site.example Authorization: <base64 of "client_secret|api_key_id|api_key_secret"> // client_id rides the query string; the base64 triple rides the // Authorization header — bare, no "Bearer " / "Basic " prefix. No body. → 200 { "success": true, "status": 200, "message": "Success", "result": { "type": "Bearer", "access_token": "…" } // keep this on the server }
2 · Certificate handshake (issue a code, then claim it)
GET /v1/resources/certificate?client_id=kcid_…
Authorization: Bearer <application_token>
Origin: https://your-site.example
→ 200 { "success": true, "status": 200, "message": "Success", "result": { "code": "…" } }
// the code lives ~120s and is single-use; a new GET reissues + resets it
POST /v1/resources/certificate?client_id=kcid_…
Authorization: Bearer <application_token>
Origin: https://your-site.example
Content-Type: application/json
{ "code": "…" }
→ 200 { "success": true }
// on this valid claim the certificate material is POSTed to your
// notification URL — the public key + shared secret, not in this reply3 · Fetch your resources (all Bearer + Origin; contract, policy and front settings are scoped to your key — the space list is the whole organization, filter it by your contract)
// every read carries the two headers, and returns the same envelope: // Authorization: Bearer <application_token> // Origin: https://your-site.example // → { "success": true, "status": 200, "message": "Success", "result": … } GET /v1/space/list // every space with its nested agents[] — keep the ones in your contract GET /v1/resources/consumer-contract // your contract: identity + the ids of the spaces you may use GET /v1/resources/consumer-policy // your live registration / login policy GET /v1/resources/agent-front-settings?agentId=<slug> // one agent's partner-facing config
/v1/space/list — the result shape (spaces, agents nested by slug)
{ "success": true, "status": 200, "message": "Success", "result": [ { "id": "…", "createdAt": "…", "updatedAt": "…", "title": "Support", "description": "…", "deploymentToken": "", // present but always empty — deployment tokens are never served here "agents": [ { "id": "…", "agentId": "billing-help", // the SLUG — what you name to open a chat "displayName": "Billing Help", "startFlow": "…", "startNode": "…", "active": true } ] } ] }
4 · Sign in an end user (consumer login; a passcode step may follow)
POST /v1/auth/access_token Origin: https://your-site.example Content-Type: application/json { "email": "user@example.com", "password": "…" } // contract does NOT require a passcode → tokens straight back: → 200 { "success": true, "status": 200, "statusCode": 200, "result": { "access_token": "…", "refresh_token": "…" } } // contract DOES require a passcode → a challenge instead of tokens: → 200 { "success": true, "status": 200, "statusCode": 201, "message": "Verify", "result": { "hash": "…" } // passcode emailed to the user; hold this hash } // then confirm the emailed passcode to receive the token pair: POST /v1/auth/otp/confirm Origin: https://your-site.example { "pin": "123456", "token": "<hash from the Verify response>" } → 200 { "success": true, "status": 200, "statusCode": 200, "result": { "access_token": "…", "refresh_token": "…" } }
spaces tells you which of them you may use; the slug is what you name when opening a chat.Kept fresh for you:
spacesagentscontractpolicyfront settingsThe observable failure at each step, and how to recover.
Origin is dropped silently at every step (on your side it looks like a generic proxy error). No status, no message. Serve your site from the exact Origin registered on your webhook entry, then retry.{ "message": "Invalid request" } — a uniform reject for every credential or binding failure (bad client id / secret, bad api key, or a key that is not yet fully bound with a certificate, contract and webhook). No hint about which. Re-check the credentials the admin handed you and that the key is fully bound, then retry.client_id that does not match the token. The POST (claim) returns 401 { "success": false } if the token is invalid, the client_id mismatches, or the code is wrong, missing, expired (~120s) or already spent. The claim consumes the code either way — GET a fresh one and claim again.Missing or malformed Authorization header / Invalid token / Token api_key no longer valid / Token client_id no longer matches binding). Mint a fresh token. A contract or policy read for a key with no contract bound is a success with "result": null, not an error; the space list is never empty on that account — it lists the organization's spaces regardless. agent-front-settings returns 404 Agent not found for a slug that is not in one of your contract's spaces — the same message whether the agent is elsewhere or does not exist.{ "success": false, "status": 406 } — invalid credentials, an account that is not registered for this tenant, or a login your contract blocks, all returned the same way with no leak of which. On the passcode branch, a wrong or expired pin comes back 200 { "success": false, "message": "Expired" } — start the login again for a fresh challenge.Each piece, in depth.
Api Keys
The hub credential and its four bindings; Rebuild, then Wake.
Authentication
The two-token model, one-time passcodes and refresh.
Live Chat
Running an agent over the streaming connection.
Self-Service
Consumer profile, password reset and account deletion.
Webhooks
The Origin + notification URL your key is bound to.
Keen MCP Server
Let an AI agent wire the chain by tool calls.
Resource Cache
Why your fetched data is always current.
Logs & Monitoring
Live-stream an agent's runtime logs to your site.
Keen Agents 2026
Documentation
Release 15