Skip to content

Federation API

/api/v1/federation/* lets you create and manage the identity-provider connections your tenant accepts. Social providers (Google, GitHub, Microsoft, Apple), custom OIDC IdPs, and SAML 2.0 connections all live here. Scoped to federation:read and federation:write.

You rarely call this surface from an end-user app. It is for the tenant admin console, for Terraform-style automation, and for occasional B2B integrations where adding an SSO connection programmatically is faster than clicking through the UI.

The kind field on every connection is one of:

KindUse
social.googleGoogle OAuth (web sign-in)
social.githubGitHub OAuth
social.microsoftMicrosoft Entra (OIDC mode)
social.appleSign in with Apple
oidcGeneric OIDC — any compliant IdP
samlSAML 2.0

Each kind has a slightly different config shape; the common fields are normalised under data.

GET /api/v1/federation/connections
Authorization: Bearer <access-token>
Required scope: federation:read
Query parameters:
kind — filter by kind (multiple: kind=oidc&kind=saml)
state — 'enabled' | 'disabled'
cursor
limit
{
"data": [
{
"id": "fed_01HZX...",
"slug": "cymmetri-okta",
"kind": "saml",
"name": "Cymmetri Okta",
"state": "enabled",
"created_at": "2026-03-01T10:00:00Z"
}
],
"meta": { "next_cursor": null, "limit": 50 }
}
POST /api/v1/federation/connections
Authorization: Bearer <access-token>
Content-Type: application/json
Required scope: federation:write
{
"kind": "social.google",
"name": "Google",
"slug": "google",
"client_id": "...",
"client_secret": "...",
"scopes": ["openid", "email", "profile"]
}

The slug must be URL-safe and unique within the tenant. Slugs are immutable once saved; renames mean delete + recreate.

POST /api/v1/federation/connections
Content-Type: application/json
{
"kind": "oidc",
"name": "Cymmetri Internal SSO",
"slug": "cymmetri-sso",
"issuer": "https://login.cymmetri.com",
"client_id": "...",
"client_secret": "...",
"scopes": ["openid", "email", "profile"],
"use_discovery": true,
"attribute_mapping": {
"email": "$.email",
"name": "$.preferred_username"
}
}

When use_discovery: true, the platform fetches the IdP's .well-known/openid-configuration and pulls authorization, token, and JWKS endpoints from it.

POST /api/v1/federation/connections
Content-Type: application/json
{
"kind": "saml",
"name": "Cymmetri Okta",
"slug": "cymmetri-okta",
"idp_metadata_url": "https://cymmetri.okta.com/app/.../sso/saml/metadata",
"attribute_mapping": {
"email": "emailaddress",
"name": "name",
"first_name": "givenname",
"last_name": "surname",
"groups": "groups"
},
"jit_provisioning": true
}

If you prefer to paste the IdP metadata XML directly:

{
"kind": "saml",
"name": "...",
"slug": "...",
"idp_metadata_xml": "<EntityDescriptor xmlns=...>...</EntityDescriptor>",
"attribute_mapping": { ... }
}

The response includes the SP-side values the IdP needs:

{
"data": {
"id": "fed_01HZX...",
"slug": "cymmetri-okta",
"acs_url": "https://banking-cymmetri.intelliauth.local/auth/saml/cymmetri-okta/acs",
"sp_entity_id": "https://banking-cymmetri.intelliauth.local/saml/cymmetri-okta",
"sp_metadata_url": "https://banking-cymmetri.intelliauth.local/saml/cymmetri-okta/metadata"
}
}

Send these to whoever is configuring the IdP side.

PATCH /api/v1/federation/connections/{connection_id}
Content-Type: application/json
Required scope: federation:write
{
"attribute_mapping": { "groups": "memberOf" },
"scopes": ["openid", "email", "profile", "offline_access"]
}

slug and kind are immutable. Other fields are PATCH-mergeable.

POST /api/v1/federation/connections/{connection_id}/disable
POST /api/v1/federation/connections/{connection_id}/enable
Required scope: federation:write

A disabled connection refuses new sign-ins. Existing users who signed in via the connection keep their sessions; only new attempts fail.

POST /api/v1/federation/connections/{connection_id}/test
Required scope: federation:write

Returns a one-time test URL. Open it in a browser; complete the IdP flow; receive a JSON report:

{
"data": {
"success": true,
"duration_ms": 1842,
"claims_received": { "sub": "...", "email": "...", "groups": [...] },
"warnings": []
}
}

Use this during integration to verify the round trip without involving real users.

DELETE /api/v1/federation/connections/{connection_id}
Required scope: federation:write

Hard delete. Users previously signed in via the connection retain their user record (their external_identities array loses the connection entry, but they remain in the tenant).

ErrorWhen
slug_unavailableAnother connection already uses this slug
slug_invalidSlug isn't URL-safe or is reserved
metadata_fetch_failedIdP discovery URL or SAML metadata URL didn't return a valid document
kind_unsupportedUnknown kind value
attribute_mapping_invalidA mapping references a claim the IdP isn't sending