Users and credentials

FIDO / passkeys API

The FIDO2 server of a tenant. It runs the passkey registration and sign-in ceremonies, stores each user's authenticators (passkeys) and holds the authenticator policies that decide which passkeys are accepted.

https://{tenant-host}/fido2audience fido2_server17 operations
Last checked against a live tenant: 28 October 2026checked live called on a tenantfrom source read in the service codenot confirmed not confirmed

When to use it

  • List, rename, suspend or delete a user's passkeys from a backend or an admin screen.
  • Delete a user's passkeys before you delete the user, so the passkey can't sign in any more.
  • Read or change the authenticator policy: user verification, discoverable credentials, allowed authenticators.
  • Get the registration or sign-in options for a WebAuthn ceremony, and verify the browser's answer.

Connect

Base URL
https://{tenant-host}/fido2
Token
POST https://{tenant-host}/oauth/v1/token with grant_type=client_credentials. The client ID and secret go in the form body. Send the token as Authorization: Bearer.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 fido2_server. Not fido2: no tenant we checked uses that name.checked live
Default scopes
One fido2_server-<operation> scope per operation, listed below. Access defines 16 of them, e.g. fido2_server-policies_list, fido2_server-authenticators_list. Keep the client's scopes, joined with spaces, within 1024 characters.checked live
Not API keys or mTLS
Older references name API_KEY and MTLS schemes and a demo server. On a tenant you call https://{tenant-host}/fido2/... with the bearer token above.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}/fido2/policies \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Operations

Open an operation to see its parameters and code, and send it to your tenant. Each operation checks its own scope. The scope names come from the reference and all exist in Access; policy reads were run on a tenant.

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

A user with no passkeys is a 404, not an empty list

checked live

GET /authenticators?userId=... answers 404 with {"status":"failed","errorMessage":"No authenticators are found for userId '...'"}. Treat that as "none", not as an error. The authenticators key is absent, not empty.

A policy name that matches nothing is also a 404

checked live

GET /policies?name=x answers 404 "No policies are found for tenant [...], policy name [x]" instead of an empty policies array. An unknown policyId is a 404 too.

Read status as well as the HTTP code

checked live

Every reply carries status (ok, failed or warn). Errors add errorMessage and sometimes errorRef. Some error messages include the tenant's internal ID: log them, don't show them to end users.

The audience is fido2_server

checked live

Not fido2, which no tenant we checked uses. The scopes share the prefix and name the operation: fido2_server-policies_list, fido2_server-authenticators_list.

One scope is documented but not defined in Access

checked live

Deleting a device-bound key needs fido2_server-authenticator_delete_dbk. Access on the tenant we checked defines the other 16 FIDO scopes but not this one. Create it in Access before you give it to a client.

A tenant starts with a policy named default

checked live

The platform creates it. On the tenant we checked it requires user verification and has outcome: fail. Read it before you create another one: policy names must be unique.

New policies are lenient unless you say otherwise

not confirmed

Only name is required. Fields you leave out fall back to outcome: warn, userVerification: preferred, discoverable: preferred and metadata: no. With warn, a passkey that breaks the policy is still accepted. Set outcome: fail to enforce it.

userId is the user handle given at registration

not confirmed

The server stores whatever userId the registration options carried. Flows that pass the Identity Store user ID there can list and delete passkeys with that same ID. Sign-in returns it as userId.

Bulk delete needs userId or clientId

not confirmed

DELETE /authenticators takes userId, clientId or both, and at least one is required. With both, only that user's passkeys from that client device are deleted.

Deleting a user doesn't delete their passkeys

not confirmed

Passkeys live here, not in the Identity Store. Delete them (DELETE /authenticators?userId=...) before you delete the user, and check the account after every passkey sign-in.

Errors

Error bodies carry status: "failed", errorMessage and sometimes errorRef (an error ID). There is no error code field. 401 and 403 come from the security layer.

400failedInvalid parameters, e.g. a bulk delete without userId or clientId.
401—Token missing, expired, or without the fido2_server audience.
403—The token has the audience but not the operation's fido2_server-... scope.
404failedNothing found: unknown policy or passkey, a user with no passkeys, or a policy name that matches nothing.
409failedConflict, e.g. a policy name that already exists, or a registration or sign-in that can't be completed.
500failedUnexpected failure. Quote errorRef 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.