Users and credentials

Credential aggregator API

One view of every sign-in method a user has: password, TOTP, SMS and email OTP, passkeys, recovery codes. It also keeps login history and the tenant's list of enabled methods. It reads events from the credential services; it doesn't hold the credentials themselves.

https://{tenant-host}/credential-aggregation-service/api/v1audience credential-aggregation-service13 operations
Reviewed, not yet called live: 28 October 2026checked live called on a tenantfrom source read in the service codenot confirmed not confirmed

When to use it

  • Show a user which sign-in methods they have and when they last signed in.
  • Remove a user's TOTP device, passkey or recovery codes through one API.
  • Enable, disable or pause a sign-in method for the whole tenant.

Connect

Base URL
https://{tenant-host}/credential-aggregation-service/api/v1
Token
POST https://{tenant-host}/oauth/v1/token with grant_type=client_credentials. The client ID and secret go in the form body.checked live
Client to create
Access admin → Web clients → a client with the grant type Client credentials. Not an API client: those are only for Access's own configuration API.checked live
Default audiences
must include credential-aggregation-service (aggregation, not aggregator). Not yet seen on a tenant.from source
Default scopes
credential-aggregation-service:get, :post, :delete, :patch, as each operation lists below. Access may not define them yet: create them in Access first.from source
Availability
Not deployed on every tenant. Where it's missing, the gateway answers {"code":"not_found",...} with HTTP 511.checked live
curl -s https://{tenant-host}/oauth/v1/token \
  -d grant_type=client_credentials \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET

curl -s https://{tenant-host}/credential-aggregation-service/api/v1/management/authenticators \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Operations

Open an operation to see its parameters and code, and send it to your tenant. The token needs any one of the listed scopes. {userId} and {id} are the Identity Store user ID. {authenticatorId} is the aggregator's own row ID (id in the list), except in the management PATCH, where it is a method name such as TOTP.

Connect to your tenant

Then open any operation below and send it.

Sends grant_type=client_credentials to https://{tenant-host}/oauth/v1/token through this site's server. The secret and token stay in this tab only; host and client ID are remembered.

What trips people up

Body fields are camelCase, not the snake_case of the reference

from source

The service reads enabledMethods, enabled, active and includedInRemainingAuthCount. enabled_methods, is_enabled and default_challenge from the older reference don't exist. Replies use camelCase too: lastLogin, availableAuthenticators, authnId, authnMethod.

POST on management replaces every method

from source

Methods you leave out are removed from the tenant list, not kept as disabled. PASSWORD is always put back. To change one method, use the PATCH instead.

Enabling a method without active leaves it paused

from source

PATCH accepts only enabled and active. {"enabled":true} on a disabled method gives active: false, so users still can't enroll. Send {"enabled":true,"active":true}. {"active":false} pauses an enabled method.

PASSWORD can't be disabled or paused

from source

Either request returns 400 CAS-112. Pausing a method that isn't enabled returns 400 CAS-111; an empty PATCH returns 400 CAS-104.

The method is FIDO, the delete path says passkey

from source

Use FIDO in bodies and in the management PATCH path. PASSKEY is obsolete. The user delete route is still /authenticators/passkey/{authenticatorId}.

Deletes return 204 and the TOTP delete takes no OTP

from source

The reference says 200 and a required otp query parameter. The service returns 204 and reads no OTP. A passkey row that doesn't exist also returns 204, and nothing is deleted.

authenticatorId is the aggregator's ID, not the credential's

from source

Pass id from the user's list. reference (and credId in the detail) is the passkey credential ID or TOTP device ID; the aggregator looks it up itself.

It shows recorded metadata, not live credentials

from source

Rows come from events sent by the credential services, so they can lag or be stale. A recovery-codes row can stay after every code is used: ask the recovery code service for the real count. A 204 from a delete doesn't prove the credential is gone.

Writing records doesn't enroll or sign anyone in

from source

POST .../authenticators and POST .../logins only store metadata. Each call adds a new row, so repeats create duplicates. Use the credential services to enroll a user.

Empty answers differ per endpoint

from source

/authenticators and /logins return 200 with an empty array. /last-login and an unknown /authenticators/{authenticatorId} return 404 with an empty body. is_include_disabled on the tenant list defaults to true.

Errors

Error bodies carry id, code, message and details. Some 404s (no last login, unknown user authenticator) have an empty body instead. 401 and 403 from the security layer have no CAS code.

400CAS-100Invalid parameters, including an unknown method name or a malformed UUID.
400CAS-104PATCH without enabled or active.
400CAS-111Pause requested on a method that isn't enabled.
400CAS-112Tried to disable or pause PASSWORD.
401CAS-110A credential service the aggregator called refused it.
404CAS-102Not found. An unsupported HTTP method also maps here.
404CAS-106Method not configured for the tenant, or TOTP row not found.
422CAS-109Invalid method value stored or sent.
500CAS-101 / CAS-105 / HD-102Invalid tenant, database failure, or a failed call to another service. Quote id when you report it.

When a call fails

ReplyMeansWhat to change
The service answers 401The token lacks this service's audience, or it expired.Add the audience to the client's Default audiences (Access admin → Web clients → the client), then get a new token.
The service answers 403 and the token has the audienceA scope the operation needs is missing.Add one of the operation's scopes to the client's Default scopes. Use the scope names your tenant lists under Access admin → Scopes.
Token endpoint: 401 invalid_clientUnknown client ID or wrong secret.Check the client ID and paste the secret again.
Token endpoint: 400 unauthorized_clientThe client may not use client credentials.Add the grant type Client credentials to the web client.
Token endpoint: 400 invalid_request about an audienceYou asked for an audience the client doesn't have.Add it to Default audiences, or stop asking for it.
Token endpoint: 500 right after scopes were addedThe client's Default scopes, joined with spaces, are over about 1024 characters.Move scopes to Additional scopes and request them per call, or split the work over two clients.
{"code":"not_found","message":"The requested location was not found"}That service isn't deployed on this tenant.Nothing to change in the client. Ask whoever runs the tenant.

Two kinds of client exist in Access. Service APIs like this one take a web client with the Client credentials grant. Access's own configuration API takes an API client: a web client sent there gets 401 invalid_credentials.