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: a Webhook entry (your browser Origin plus a notification URL), a Client (your server's identity), a Certificate (the signing material), and a Consumer Contract (your policy and the spaces you may use), all bound to one Api Key. When those four are bound, the key is fully bound and your Origin is on the allow-list. The admin then hands you two things: your Client id and secret, and confirmation of the Origin and notification URL registered for you.
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 gateway error, never a descriptive one — so an unknown caller learns nothing. If connections fail with no message, check the Origin first.Server-side first, then the browser — in this order.
Interface-level shapes — your KeenAgents base URL is provided at onboarding.
1 · Application token (server-side, once, then refresh as needed)
POST /v1/auth/application_token?client_id=kcid_… Origin: https://your-site.example Authorization: <base64 of "client_secret|api_key|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, all scoped to your key)
// 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 // your spaces, each with a nested agents[] array GET /v1/resources/consumer-contract // your contract: identity + 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": "…", "title": "Support", "description": "…", "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": "…" } }
Kept fresh for you:
spacesagentscontractpolicyfront settingsThe observable failure at each step, and how to recover.
Origin is dropped silently at every step (through a proxy it looks like a generic gateway 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."result": null (contract / policy) or an empty [] (spaces), not an error. 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; the Wake action.
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.
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