Specs · CAEP Interoperability Profile 1.0
CAEP Interoperability Profile
SSF and CAEP leave many choices open. The Interoperability Profile picks one baseline, so a Transmitter and a Receiver built by different teams can connect without negotiating first. If you are building something new, build to this.
Status. Draft 01 was put to a 60-day public review as a proposed Final Specification on July 27, 2026. On September 15, 2026 the working group agreed to revise it in response to review feedback and restart the review, so the planned vote did not happen; a new timeline is still to be announced. The working copy has meanwhile added the Risk Level Change use case. This page follows the working copy and marks what is new since draft 01.
What it profiles
Shared Signals Framework 1.0, CAEP 1.0 and OAuth 2.0. To conform, an implementation must meet the common requirements below and support at least one of the use cases. (Earlier drafts said implementations may pick use cases; draft 01 made at least one mandatory.)
Transmitter checklist
| Area | Requirement (MUST) |
|---|---|
| Discovery | spec_version present, 1_0 or greater. |
| Discovery | delivery_methods_supported present. |
| Discovery | jwks_uri present and resolving to the current signing key(s). |
| Discovery | configuration_endpoint, status_endpoint and verification_endpoint present. |
| Discovery | authorization_schemes includes { "spec_urn": "urn:ietf:rfc:6749" }. |
| Delivery | Accept Create Stream with delivery.method of push (urn:ietf:rfc:8935) or poll (urn:ietf:rfc:8936); always return a delivery in the created stream. |
| Streams | Support Create, Read Configuration, Read Status, Verification (and send the Verification event), and Delete. |
| Subjects | Send events using at least one of email, iss_sub (and opaque for Verification events). |
| Signing | Sign every SET with RS256, keys of at least 2048 bits. |
| SETs | Exactly one event in each SET’s events claim. |
| OAuth | Accept bearer tokens in the Authorization header; reject tokens in query parameters (RFC 6750 §2.3). |
| OAuth | Validate token integrity, expiry and revocation, check the token grants enough access, and return RFC 6750 §3.1 errors when it doesn’t. |
| TLS | TLS 1.2 or later on every endpoint and outbound connection; follow RFC 9325. |
Receiver checklist
| Area | Requirement (MUST) |
|---|---|
| Delivery | Accept events by push (RFC 8935) or poll (RFC 8936) — at least one. |
| Delivery | Create Stream with delivery.method of push or poll, or omit delivery (which means poll). |
| Keys | Fetch the Transmitter’s signing keys from jwks_uri. |
| OAuth | Use OAuth 2.0 for every Stream Management API call. |
| Subjects | Assume every subject is in the stream — no Add Subject calls required. |
| Subjects | Accept events whose subject is email, iss_sub or opaque. |
| Streams | Be able to Create, Read Configuration, Read Status, request Verification (and process the Verification event), and Delete. |
| TLS | Fully validate the Transmitter’s TLS certificate (chain, expiry, revocation) per RFC 6125. |
OAuth
The roles map directly: the Transmitter is the resource server, the Receiver is the client, and the authorization server is anything the Transmitter trusts to issue tokens — it doesn't have to be the Transmitter itself. How the Receiver finds that authorization server is out of scope; it may publish RFC 8414 metadata.
- Tokens are issued with the client credentials grant or the authorization code grant (at least one).
- Access tokens must be short-lived: 60 minutes or less.
Scopes
Scope names starting with ssf. are reserved for the SSF specifications. Authorization servers issuing tokens
to Receivers must support two:
| Scope | Allows |
|---|---|
ssf.read | Read Stream Configuration, Read Stream Status |
ssf.manage | Everything in ssf.read, plus Create Stream, Delete Stream and Stream Verification |
Earlier drafts had Receivers discover scopes through OAuth Protected Resource Metadata (RFC 9728) when it was available. Draft 01 dropped that, so the two fixed scopes are now the only mechanism.
Use cases
Support at least one. In every one, Transmitters must populate reason_admin with a
non-empty object (a localised message map, e.g. {"en": "Policy violation"}). Receivers must understand every allowed value of the listed fields;
Transmitters may send any of them.
Credential Change
change_type: All valuescredential_type: All valuesreason_admin: required, non-empty object
Device Compliance Change
previous_status: All valuescurrent_status: All valuesreason_admin: required, non-empty object
Risk Level Change
principal: All valuescurrent_level: LOW, MEDIUM, HIGHprevious_level: All values; absent = unknownreason_admin: required, non-empty object
Read the source
Checked against openid/sharedsignals @ a34d6ff (2026-09-01).