Per-tenant signing certificates — your partner verifies consumer tokens with the public PEM; the private half never leaves the platform.
What a certificate is and why every partner chain needs one.
A certificate is the per-tenant signing identity of the API access chain. KeenAgents generates the keypair for you: the platform signs your partner's consumer tokens with the private half, and the public certificate (PEM) is what your partner application uses to verify them. A certificate is bound 1:1 to at most one API key, and it is one of the four bindings a key needs to be fully bound: until a certificate is bound, the key's Origin never joins the partner-facing allow-list and an application-token request is refused with the uniform 500 Invalid request — the reason is visible only to the admin, on the key's detail page.
Visibility is split on purpose: the public PEM is always visible in the admin UI, while the private key is never displayed and can never be read back through any API or UI surface — that guarantee is what makes the public PEM safe to share. Certificates are self-issued for your tenant with a one-year validity, so partners should trust the PEM they receive directly (pin it) rather than expect a public certificate-authority chain, and you should plan a rotation before expiry.
16–32 characters) used to encrypt consumer material that both KeenAgents and your partner must be able to read. Because both ends need the same value, it stays visible on the detail page — recoverable by design, unlike reveal-once secrets. It is an independent credential: rotating the keypair does not change it, and vice versa.1–100 characters, unique.20 certificates, checked on create only. Past the cap the create fails with 400 and a "Reached the maximum number of …" message — delete one first.16–32 characters (trimmed). Leave the field empty on create to store no secret — the list shows Unset. On the detail page an emptied field is ignored (the stored key stays); to change it, enter a new 16–32 character value or press Generate, then Save. Omitting the field on an update leaves it untouched. There is no "clear" action — to drop a secret entirely, replace the certificate.Rotate swaps the keypair in place; delete tears the whole thing down.
r_cert notice (no material). The partner then either re-runs the code handshake, or the admin presses Rebuild Resource Cache and Wake on the bound API key — Wake delivers the new public certificate and the shared secret key directly.Delete (as opposed to rotate) permanently destroys the certificate and its private key, and unbinds it from the API key — the key stops being fully bound, so its Origin drops off the partner-facing allow-list and the partner loses access until a new certificate is bound.
The two HTTP calls behind the handshake: issue a one-time code, then claim it. Both carry the partner's application token.
Every call is gated on an allow-listed Origin and the partner's Authorization: Bearer <application_token>. The client_id query must equal the client the token was minted for.
Paths shown as /v1/… are reached at https://<your KeenAgents host>/keen-api/… — the /v1 prefix is replaced by /keen-api.
GET /v1/resources/certificate — issue a code
GET /v1/resources/certificate?client_id=kcid_…Origin (allow-listed) and Authorization: Bearer <application_token>. Query client_id required; no request body. A re-issue against the same key overwrites the previous code and restarts the 120 s window.200 — code issued
200 OK { "success": true, "status": 200, "message": "Success", "result": { "code": "<one-time code — opaque, single-use, valid 120 s>" } }
Failure modes — GET
client_id, a missing or invalid token, a revoked key, or a client_id that doesn't match the token — every failure drops the connection with no status and no body, surfacing as a connection reset or a generic proxy error on your side. A probing scanner never even confirms the route exists. Recover by fixing the credential and retrying; the specific cause is never disclosed.POST /v1/resources/certificate — claim the code
POST /v1/resources/certificate?client_id=kcid_…Origin (allow-listed), Authorization: Bearer <application_token>, and Content-Type: application/json.code — string, required: the code returned by the GET issuance. Extra fields are rejected.POST /v1/resources/certificate
POST /v1/resources/certificate?client_id=kcid_… Origin: https://app.yourcompany.com // must be allow-listed Authorization: Bearer <application_token> Content-Type: application/json { "code": "<the code returned by the GET issuance>" } 200 OK { "success": true } // the redeem webhook fires server-side, in parallel
On 200 { "success": true } the code is consumed and the redeem webhook is dispatched to your registered URL in parallel — the success reply means the delivery went out.
Failure modes — POST
Origin is not allow-listed — the connection is dropped before authentication and surfaces as a connection reset or a generic proxy error on your side.Authorization header, or an invalid / revoked token — Invalid token. Also returned (as { "success": false }) when the client_id is missing or doesn't match the token, when the code is wrong, expired, or already claimed, or when the API key ↔ client binding no longer holds. Codes are single-use, so a replay always fails — recover by requesting a fresh code with a GET and claiming that.code, sends it empty, or includes unknown fields — rejected with the validation reasons. Send exactly { "code": "…" }.16–32 characters) with a Generate button; the value is displayed on the detail page — it is recoverable by design. On create the field reads Leave empty to store no secret; on the detail page, Leave empty to keep the current key.list_certificates, get_certificate, create_certificate, update_certificate, rotate_certificate, delete_certificate — same scope, same rules.
A certificate's detail screen — the name and shared secret key with its Generate button, the always-visible public PEM, the Rotate Key Material block, and the Save · Rebuild Resource Cache · Delete footer.
r_cert notice; deleting it sends r_cert_delete. Neither carries material — the partner re-runs the handshake or the admin presses Wake.Keen Agents 2026
Documentation
Release 15