Skip to content

Prepare passkey updates

Prepare a user-scoped passkey update. The prepare endpoint updates the stored Passlock vault username for the user’s passkeys and returns a short-lived token for browser-side WebAuthn signalling.

HTTP Request
POST https://api.passlock.dev/v2/{tenancyId}/passkeys/update HTTP/1.1
Authorization: Bearer {apiKey}
Accept: application/json
Content-Type: application/json
{
"userId": "tenant-user-id",
"username": "jdoe@example.com",
"displayName": "Jane Doe"
}
HTTP Response
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"_tag": "PreparedPasskeyUpdate",
"updatePasskeysToken": "opaque-random-token",
"expiresAt": 1770123593000,
"warnings": []
}

Send only updatePasskeysToken to your frontend, then call @passlock/browser’s updatePasskeys helper. The browser helper exchanges the token with:

Token Exchange Request
POST https://api.passlock.dev/v2/{tenancyId}/passkeys/update/exchange HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"updatePasskeysToken": "opaque-random-token"
}
Token Exchange Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"_tag": "PasskeyUpdateInstructions",
"instructions": [
{
"rpId": "example.com",
"userId": "MTVkMTFmdHM1Yzg0bDN0anpieG9w",
"username": "jdoe@example.com",
"displayName": "Jane Doe"
}
],
"warnings": []
}

Warnings are non-fatal. For example, updating a user with no passkeys succeeds with a token and a NO_PASSKEYS_FOUND warning.

Prefer @passlock/server where possible:

backend/update-passkeys.ts
import { Passlock } from "@passlock/server";
const passlock = new Passlock({ tenancyId, apiKey });
const result = await passlock.updatePasskeys({
userId: user.id,
username: "jdoe@example.com",
displayName: "Jane Doe",
});