A named pairing of a partner's browser Origin and its notification URL — the address side of the API access chain.
Where a partner's requests come from, and where its notifications go.
A webhook entry is a standalone resource with three fields: a name, the partner's browser Origin, and the partner's notification URL. It answers the two address questions of a partner integration in one record: which web origin the partner's application calls from (the inbound side), and where KeenAgents delivers refresh notices to the partner's server (the outbound side).
A webhook entry does nothing on its own — an API key points at it, the same way a key binds its Client, Certificate and Contract. Origins are unique across your organization's webhook entries, so an inbound request's Origin header always resolves to exactly one entry, and through it exactly one API key and the rest of that partner's chain.
scheme://host[:port] the partner's browser application runs on — no path, no trailing slash — so it matches the browser's Origin header byte for byte. The port matters (browsers omit only the default 80/443), wildcards are not supported, and the value is unique across all webhook entries. Together, the origins of every fully bound key form the organization's Origin allow-list.http:// or https:// URL on the partner's server. Refresh notices land here: lightweight POSTs that say something changed — never the data itself, with one exception: certificate deliveries (after a code claim or a Wake) carry the public PEM and the shared secret key. On receipt of a notice the partner re-fetches what it needs over its authenticated API connection (notify-then-fetch), so a missed notice costs nothing that the next fetch does not recover.100 characters, unique — it is how the entry appears in the API key form's picker.200 characters, in the form scheme://host[:port], unique across all webhook entries. Exact-match only — https://app.example.com and https://app.example.com:8443 are different origins.500 characters, a full URL starting with http:// or https://, and also unique — two entries cannot share a URL. A duplicate of any of the three fields is refused with Name / Origin / Webhook URL "…" is already in use by another webhook.What a partner endpoint should do with a refresh notice.
Exactly what KeenAgents POSTs to your notification URL — the request you receive, and how to answer it.
KeenAgents is the caller here: it POSTs to your notification URL. Every notice is POST with Content-Type: application/json and a body whose synch-data field names what changed. Two shapes exist — the ordinary refresh notice (an array of change kinds, no data) and the certificate delivery (after a code claim or a Wake; it carries the material). There is no authentication header on the notice; it is a doorbell, and the value lives behind your own authenticated re-fetch, never in the notice itself.
Paths shown as /v1/… are reached at https://<your KeenAgents host>/keen-api/… — the /v1 prefix is replaced by /keen-api.
Refresh notice — the ordinary case (no payload)
POST <your notification URL> Content-Type: application/json { "synch-data": [ "r_api_keys" ] } // a Wake sends TWO POSTs: first the certificate delivery (next block), // then one batch naming every list to re-read: { "synch-data": [ "r_api_keys", "r_consumer_policy", "r_space", "r_agent", "r_json" ] } // the same batch is sent once more, without the certificate, when your key is deleted // you respond fast with any 2xx — the body is ignored: → 200
The synch-data kinds tell you which authenticated read to make next:
r_space · r_agentGET /v1/space/list. That list is the organization's whole space collection with the agents nested in each space — keep only the spaces named in your consumer contract's spaces; chat runs outside them are refused. Space and agent edits do not send a notice of their own — the admin presses Wake.r_consumer_policyGET /v1/resources/consumer-contract and GET /v1/resources/consumer-policy. A r_consumer_policy_delete means the contract was removed — drop your stored copy; your key is no longer fully bound until the admin binds a new one.r_jsonGET /v1/resources/agent-front-settings?agentId=<slug> for each agent you use. It arrives only in a Wake batch — saving front settings does not send a notice on its own, the admin presses Wake.r_api_keysr_cert · r_cert_deleter_cert comes in two shapes: as a bare array entry ({ "synch-data": ["r_cert"] }) when the admin renames, edits the shared secret of, or rotates your certificate — then re-run the code handshake or ask for a Wake; and as the one notice that carries a payload (next block) after a handshake claim or a Wake. r_cert_delete means the certificate was deleted — clear your stored copy; your key is no longer fully bound until the admin binds a new one.Certificate delivery — the one notice that carries data
POST <your notification URL> Content-Type: application/json { "synch-data": "r_cert", // a bare string here, not an array "type": "resource", "result": { "cert": "-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----", "key": "<shared secret>" // "" when the admin never set one } } → 200
This lands after you claim a certificate code (see Connecting a Site) and every time an admin presses Wake on your key. A rotation alone sends only the bare r_cert notice — run the handshake again or ask for a Wake to receive the new material. Store result.cert (the public key you verify KeenAgents-issued tokens with) and result.key (the shared secret) when it arrives; it is not one-time-forever.
Delivery semantics & failure modes
list_webhooks, get_webhook, create_webhook, update_webhook, delete_webhook — same scope, same rules.
A webhook entry's detail screen — the key holding it, its Webhook ID, name, the partner's browser Origin, the notification URL, and the Save · Rebuild Resource Cache · Delete footer.
Keen Agents 2026
Documentation
Release 15