Link a federated identity to a user

POST /v1/users/{user_id}/identities · permission users:write

Links a federated identity (the external subject of a social, SAML or OIDC connection) to the user, so the user can sign in with that connection. linked_via is always admin_api. The connection must be an enabled connection of this environment whose kind matches type. If the same identity is already linked to this or another user the request fails with 409 and the owner is not revealed. The external subject is personal data: it is stored encrypted and never returned, and the management audit record user.identity_linked (actor: the API key) does not contain it. Merging two users (source_user_id) is not available; an unknown field returns 400 unknown_field.

Request

Parameter In Required Description
user_id path yes User id, prefixed usr_.
Field Type Required Description
type string yes social, saml or oidc; must match the connection (social for a social connection, saml/oidc for an enterprise connection of the same strategy).
connection_id string yes Connection id, prefixed conn_.
subject string yes External subject at the identity provider (OIDC sub, SAML NameID), at most 1024 bytes of UTF-8.

Response

201 Created with the new identity, in the shape returned by GET /v1/users/{user_id}/identities.

{
  "id": "idn_0a1b2c3d4e5f6g7h8j9k0m1n2r",
  "type": "oidc",
  "connection_id": "conn_0a1b2c3d4e5f6g7h8j9k0m1n2s",
  "provider": null,
  "linked_via": "admin_api",
  "created_at": "2026-01-15T09:30:00Z"
}

Errors

Every error is an application/problem+json body with a stable code.

HTTP code When
400 malformed_request, unknown_field The body is not valid JSON or has an unknown field.
409 conflict The identity is already linked to a user (errors[]: identity_already_linked), or the user reached the per-user identity limit (errors[]: identity_limit_reached).
415 unsupported_media_type The body was sent without Content-Type: application/json.
422 validation_failed A field is missing or invalid (/type, /connection_id, /subject), or the connection is not an enabled connection of this environment matching type (errors[]: /connection_id, connection_unavailable).
404 not_found The user does not exist in this environment, is being deleted, or the id is malformed.
401 unauthenticated The API key is missing, invalid or revoked.
403 insufficient_permissions The key lacks the users:write permission.
429 rate_limited The rate limit was exceeded; honor Retry-After.
503 service_unavailable A dependency is temporarily unavailable; retry later.

Example

curl -X POST https://api.eu.keenauth.com/v1/users/usr_0a1b2c3d4e5f6g7h8j9k0m1n2p/identities \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "oidc", "connection_id": "conn_0a1b2c3d4e5f6g7h8j9k0m1n2s", "subject": "248289761001"}'

Personal data

This endpoint carries personal data: linked identities. Personal data in the KeenAuth APIs describes what is stored, how long it is kept and how it is deleted. Do not send special categories of personal data (health, biometric data, religion, political opinions, sexual orientation) in any field.