Skip to main content

Identity Plane APIs

Platform version: v11.1.0. Edition: Enterprise. Status: available, and inert on every deployment until an organization opts in.

These two endpoints arrived together in v10.2.0 and are best read as a pair. The admin API writes an organization's identity settings row; the CAEP receiver does nothing at all until that row opts the organization in. Neither endpoint grants or denies a request by itself. In v11.0.0 the settings row carries the Shared Signals channel only: the per-organization compatibility and decision modes it held in v10.x are removed, because there is no mode left to select (Identity Compatibility Mode says what they were).

Neither endpoint is in the generated OpenAPI reference

The generated reference is vendored from the community mirror's specs, and both of these endpoints are Enterprise-only, so they do not appear there. This page is their authoritative reference.

Shared Signals / CAEP push receiver​

POST /api/v1/identity/caep/events

A Shared Signals Framework push receiver on the agent. An identity provider that supports Continuous Access Evaluation delivers a signed Security Event Token (SET) here when a session is revoked or credentials change, and AxonFlow drops the affected cached identity data so the next governed request re-resolves it.

When the route exists​

The route is registered only on a process that can act on it. An agent registers it when it has wired the tenant OIDC configuration, the identity attribute resolver and the organization settings store together. Anywhere else, including every Community build, the path is not registered at all and a request to it receives the agent's ordinary 404. That is deliberate: a transmitter configured against a deployment that cannot act sees the failure in its own delivery log rather than a receiver that accepts and silently discards.

A process that does not register it logs the reason once at startup:

[IDENTITY-CAEP] no Shared Signals receiver registered: this process did not wire all of the tenant OIDC configuration, the identity attribute resolver and the org settings store, so no realm declares a Shared Signals channel

A process that does register it logs:

[IDENTITY-CAEP] Shared Signals push receiver registered at POST /api/v1/identity/caep/events; a tenant's OIDC realm declares the channel once its identity settings opt in

Registration is not the same as being on. With the route registered but no organization opted in, every delivery is refused: no realm declares the channel, so the issuer resolves to nothing. Turn it on for an organization with the identity settings API below.

Authentication​

The transmitter authenticates with the organization's ordinary agent client credential, the same Authorization: Basic base64(clientId:clientSecret) pair the agent's other authenticated routes take. No separate credential type is introduced.

The organization is the authenticated one, never a claim inside the token. A SET that names some other organization does not reach that organization's data; the token is evaluated only against the realms of the credential that carried it.

POST /api/v1/identity/caep/events
Authorization: Basic <base64 clientId:clientSecret>
Content-Type: application/secevent+jwt

eyJhbGciOiJSUzI1NiIsInR5cCI6InNlY2V2ZW50K2p3dCIsImtpZCI6...

The body is the compact-serialized SET. It must be between 1 and 65536 bytes; anything larger is refused without being parsed.

Responses​

StatusMeaning
202 AcceptedThe SET was validated and applied, or recognized as a duplicate of one already applied. The body is empty, per RFC 8935.
400 Bad RequestThe delivery is wrong and will stay wrong. Do not redeliver.
401 UnauthorizedThe client credential was missing or invalid.
403 ForbiddenThe transmitter is not permitted to send this event to this organization. Do not redeliver.
503 Service UnavailableAxonFlow could not act on a SET it has no reason to reject. Redeliver.

Errors carry the RFC 8935 body shape:

{
"err": "invalid_issuer",
"description": "the issuer is declared by no trust realm in this organization"
}

The err values are invalid_request, invalid_key, invalid_issuer, invalid_audience, authentication_failed, access_denied and temporarily_unavailable.

The 400/403-versus-503 split is the contract that matters to a transmitter, and it is drawn on one question: is redelivering this SET pointless, or might it work? A malformed token, an undeclared issuer, a bad signature and an unknown event type are all permanent and answer in the 400 family. A cache AxonFlow could not clear is temporary and answers 503, and its jti is deliberately not remembered, so the transmitter's retry is processed rather than answered as a duplicate. A revocation must never be acknowledged as applied when it was not.

What a delivery is checked against​

In order, and every event in a SET is validated before any is applied:

  1. the media type is application/secevent+jwt;
  2. the token is a well-formed JWS whose header declares typ=secevent+jwt, so an id_token cannot be replayed as a SET;
  3. the issuer resolves to a trust realm in the authenticated organization, before any key is fetched. An undeclared issuer costs no outbound request;
  4. that realm has opted in to Shared Signals, and the token's algorithm is on the realm's allow-list;
  5. the audience matches the organization's configured caep_audience;
  6. the signature verifies against the realm's own JWKS, which is the SSO configuration the realm was derived from;
  7. freshness, jti and replay dedupe;
  8. event types are known, and subjects are iss_sub from the realm's own issuer. An email, phone, opaque or foreign-issuer subject is refused rather than ignored.

Every refusal is logged as [IDENTITY-CAEP] ... refused stage=<stage> and counted.

Metrics​

Two Prometheus counters, on the agent's existing metrics endpoint:

MetricLabelsMeaning
axonflow_identity_caep_push_totaloutcome (applied, duplicate, refused), stage (the check that refused)push deliveries
axonflow_identity_caep_invalidations_totalscope (subject, org)cache invalidations performed

A scope="org" invalidation means the directory could not map the subject to a user, so the organization's whole cached set was dropped. That over-invalidates on purpose: the alternative is under-invalidating a revocation.

What it does not do​

  • It does not revoke AxonFlow-minted per-user tokens. Those carry AxonFlow's own jti and are governed by the existing revocation surfaces. A Shared Signals event is a statement about the IdP's realm.
  • It grants nothing and denies nothing. Its whole effect is to drop cached identity data early, so the next request resolves fresh instead of waiting out a TTL.

Per-organization identity settings​

GET /api/v1/admin/organizations/{org_id}/identity-settings
PUT /api/v1/admin/organizations/{org_id}/identity-settings
DELETE /api/v1/admin/organizations/{org_id}/identity-settings

On the customer portal, behind the admin API. This is an operator decision about a tenant, so it deliberately sits on no tenant-session route: it is registered under the same admin middleware as the rest of the /api/v1/admin/... family, which in SaaS production requires the X-Admin-API-Key header. Treat that key as described under Admin API key auth.

The record​

{
"success": true,
"settings": {
"org_id": "acme",
"caep_enabled": true,
"caep_audience": "https://agent.acme.example/api/v1/identity/caep/events",
"updated_by": "[email protected]",
"updated_at": "2026-08-31T09:14:02Z",
"propagation": "the agent and the orchestrator re-read this row on a per-organization TTL (AXONFLOW_IDENTITY_ORG_SETTINGS_TTL_SECONDS, default 60s); a change is honoured within that bound"
}
}
FieldMeaning
caep_enabledwhether this organization's OIDC realm declares a Shared Signals channel. Requires caep_audience.
caep_audiencethe audience value a SET must carry for this organization.
propagationhow long a change takes to be honoured. The response says it rather than making the caller look it up.

Requests​

GET returns 200 with the record, or 404 if the organization has no row (which is not an error: it means the organization has declared no Shared Signals channel).

PUT replaces the record:

PUT /api/v1/admin/organizations/acme/identity-settings
X-Admin-API-Key: <key>
Content-Type: application/json

{
"caep_enabled": true,
"caep_audience": "https://agent.acme.example/api/v1/identity/caep/events"
}

400 if caep_enabled is true without a caep_audience, or if the body is malformed. The body is decoded strictly, so a field this record does not carry is refused rather than ignored: automation written for v10.x that still sends compat_mode, decision_shadow_mode or decision_shadow_planes receives 400 with invalid request body: json: unknown field "compat_mode" (naming the field it sent).

404 if the organization does not exist.

DELETE removes the record and answers 204. The organization's Shared Signals channel is withdrawn within the propagation bound.

Storage​

The row lives in identity_org_settings, added by migration enterprise/146, org-scoped under row-level security. It is an Enterprise table; a Community deployment federates no identity provider and holds no such row.