Skip to content

Verify passkey registration and store the credential

POST
/v1/mfa/passkeys/register/verify
curl --request POST \
--url https://api.aetherpush.com/v1/mfa/passkeys/register/verify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "response": {}, "name": "example", "stepUp": { "type": "passkey", "response": {}, "code": "example" } }'

Session-only. Verifies the browser’s registration response against the stored challenge and persists the credential. A passkey enrolled while MFA is off enables MFA and returns the recovery codes, which are shown exactly once. Enrolling a passkey on an account that already has MFA enabled, through a passkey or an authenticator app, requires a fresh step-up proof and keeps the existing recovery codes.

Media typeapplication/json
object
response
required

WebAuthn RegistrationResponseJSON, as returned by startRegistration() from @simplewebauthn/browser.

object
key
additional properties
any
name

Display name for the passkey. Defaults to “Passkey”.

string
>= 1 characters <= 100 characters
stepUp

Required only when enrolling an additional factor on an account that already has MFA enabled. Omitted for the first factor.

object
type
required
string
Allowed values: passkey totp
response

WebAuthn AuthenticationResponseJSON. Required when type is passkey.

object
key
additional properties
any
code

A current authenticator code. Required when type is totp.

string

Passkey stored.

Media typeapplication/json
object
passkey
required
object
id
required
string format: uuid
name
required
string
deviceType

singleDevice or multiDevice, as reported at registration.

string | null
backedUp
required
boolean
transports
required
Array<string>
createdAt
string format: date-time
lastUsedAt
string | null format: date-time
mfaEnabled
required
boolean
recoveryCodes

Present only when this write replaced the account’s recovery codes. Shown exactly once; the server stores only hashes.

Array<string>

Example generated

{
"passkey": {
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"name": "example",
"deviceType": "example",
"backedUp": true,
"transports": [
"example"
],
"createdAt": "2026-04-15T12:00:00Z",
"lastUsedAt": "2026-04-15T12:00:00Z"
},
"mfaEnabled": true,
"recoveryCodes": [
"example"
]
}

Invalid registration response, expired challenge, or bad name.

Media typeapplication/json
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "The requested resource was not found.",
"code": "not_found",
"requestId": "req_abc123"
}

Authentication required, or step-up is required and not satisfied (code: step_up_required).

Media typeapplication/json
Any of:
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "The requested resource was not found.",
"code": "not_found",
"requestId": "req_abc123"
}

API keys and named access keys receive a plain 403. A login session that is not a dashboard login session receives code dashboard_session_required.

Media typeapplication/json
Any of:
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "This operation requires a login session. API keys and named access keys cannot access it.",
"code": "session_required",
"requestId": "req_abc123"
}

The credential is already registered.

Media typeapplication/json
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "The requested resource was not found.",
"code": "not_found",
"requestId": "req_abc123"
}

The tenant’s per-minute management API budget is spent. Retry-After and X-RateLimit-* say when to retry. This is not a plan quota: quota 429s use the Error envelope and do not set Retry-After.

Media typeapplication/json
RateLimitError

Returned with HTTP 429 when an endpoint-specific rate limit is hit, including the per-tenant management API budget. Not the process-wide address limiter (see GlobalAddressRateLimitErrorResponse). retryAfterSeconds indicates how long the client should wait before retrying, and matches the Retry-After header when that header is set. code names the limit that fired so clients can translate without matching on error.

object
error
required
string
code
required
string
retryAfterSeconds
required
integer
>= 1
requestId
string

Example

{
"error": "Rate limit exceeded",
"code": "tenant_rate_limit_exceeded",
"retryAfterSeconds": 60
}
Retry-After
integer
>= 1

Seconds until the tenant window resets.

Passkeys are not configured on this server.

Media typeapplication/json
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "The requested resource was not found.",
"code": "not_found",
"requestId": "req_abc123"
}