Skip to content

Pre-release. v0.1 is not out yet, so there is nothing to install and no public source to clone — the quickstart builds from a checkout.

Endpoints

Everything the control plane answers, in one place. The contract for each is in the v0.1 API surface; the links go to the section that defines it.

No credential. All three are documents a verifier has to be able to fetch before it holds anything.

Method Path What it answers
GET /.well-known/openid-configuration Discovery: the issuer, the three endpoints, the JWKS URL, the grant types and the one client authentication method
GET /.well-known/jwks.json The public keys task tokens are signed with. Every configured key is published; only the active one signs
GET /agents/{agent_id}/jwks.json An agent’s public keys, when its registration carries them rather than naming a URL. 404 when it names one

A disabled agent still publishes its keys: tokens it was already issued are still being presented, and whoever is checking them needs the key that signed them.

Method Path Authenticated by What it does
POST /oauth2/token The agent’s private_key_jwt assertion Exchange a user’s token for a task token, or refresh one with a task grant
POST /oauth2/introspect Holding the token Whether the token is active, answered from storage
POST /oauth2/revoke The agent’s private_key_jwt assertion Revoke a task token by jti, or a task grant — which revokes its task and everything under it

Both grants use the same endpoint, as RFC 8693 and RFC 6749 intend: grant_type=urn:ietf:params:oauth:grant-type:token-exchange to start a task, grant_type=refresh_token to renew one. What comes back is described field by field in your first exchange.

Introspection asks for no client credential in v0.1, and the reason is worth knowing: a task token is unforgeable and readable by whoever holds it, so introspection reveals nothing the bearer does not already have — except whether it has been revoked, which is the thing they are entitled to know.

Revocation is RFC 7009, and only the agent a token was issued to can use it. Anything else — a token that belongs to somebody else, a token that never existed — gets the same empty 200, so the endpoint cannot be used to find out which tokens are real.

Every one of these takes Authorization: Bearer <the admin API key>. Without Onbe:Admin:ApiKey set the whole surface answers 503; with a wrong key, 401 and an admin.denied record in the ledger. The contract is the admin API.

Method Path Result
POST /admin/agents 201 with the agent; 400 with per-field errors; 409 if the id exists
GET /admin/agents 200 with every agent
GET /admin/agents/{agent_id} 200, or 404
PATCH /admin/agents/{agent_id} 200 with the merged agent, validated as a whole; 400; 404
DELETE /admin/agents/{agent_id} 204; 404; 409 while the agent still has tasks
DELETE /admin/agents/{agent_id}/tasks 200 with {"revoked_tasks": n}; 404
DELETE /admin/tasks/{task_id} 200 with {"revoked_tasks": n}; 404
GET /audit 200 with one page of the ledger; 400 with per-field errors

The kill switches are idempotent: a repeat answers 200 with revoked_tasks: 0 and writes nothing.

Method Path What it means
GET /healthz The process is up. It does not mean it can do anything
GET /readyz The database, the signing key and the identity provider’s keys are all reachable. 503 until they are

Point a load balancer at /readyz and a restart policy at /healthz. The difference is the point: an instance that cannot reach Keycloak should stop taking traffic without being killed and restarted into the same problem.

Every other route is behind the per-source rate limiter and can answer 429 slow_down with Retry-After; /healthz and /readyz are never limited, so a busy instance is not pulled from service for being busy.

OnbePre-release. v0.1 is not out yet.

© 2026 Onbe