diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml
index bdde8032..70e482bc 100644
--- a/mintlify/openapi.yaml
+++ b/mintlify/openapi.yaml
@@ -3939,7 +3939,7 @@ paths:
description: |
Register an authentication credential for an Embedded Wallet customer.
- Embedded Wallet internal accounts are initialized with an `EMAIL_OTP` credential tied to the customer email on the account. Use this endpoint to add another credential (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential and one `PASSKEY` credential are supported per internal account.
+ Embedded Wallet internal accounts are initialized with an `EMAIL_OTP` credential tied to the customer email on the account. Use this endpoint to add another credential (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential is supported per internal account; multiple distinct `PASSKEY` credentials may be registered.
Adding a credential requires a signature from an existing verified credential on the same account. Call this endpoint with the new credential's details to receive `202` with `payloadToSign` and `requestId`. Use the session API keypair of an existing verified credential (decrypted client-side from its `encryptedSessionSigningKey`) to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `201` with the created `AuthMethod`. For `EMAIL_OTP`, the OTP email is triggered on the signed retry, and the credential must then be activated via `POST /auth/credentials/{id}/verify`.
operationId: createAuthCredential
@@ -4058,7 +4058,7 @@ paths:
requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
expiresAt: '2026-04-08T15:35:00Z'
'400':
- description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type that already exists on the internal account. Only one email OTP credential and one passkey credential are supported per internal account at this time.
+ description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` when registering an email OTP credential while one already exists, or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a passkey whose WebAuthn credentialId is already attached to the internal account.
content:
application/json:
schema:
@@ -7450,7 +7450,7 @@ components:
| UNSUITABLE_DOCUMENT | Document type is not accepted or not supported |
| INCOMPLETE | Document is missing pages or sides |
| EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is already registered on the target internal account; only one email OTP credential is supported per internal account at this time |
- | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential is already registered on the target internal account; only one passkey credential is supported per internal account in v1 |
+ | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the same WebAuthn credentialId is already registered on the target internal account |
enum:
- INVALID_INPUT
- MISSING_MANDATORY_USER_INFO
diff --git a/mintlify/snippets/global-accounts/authentication.mdx b/mintlify/snippets/global-accounts/authentication.mdx
index aee79c55..52451543 100644
--- a/mintlify/snippets/global-accounts/authentication.mdx
+++ b/mintlify/snippets/global-accounts/authentication.mdx
@@ -6,7 +6,7 @@ Every Global Account action beyond receiving funds must be authorized by a sessi
| **`OAUTH`** | Your platform already authenticates the user via OIDC (Google, Apple, your own IdP) and you want Grid to trust the same identity. |
| **`EMAIL_OTP`** | Lowest-friction option. Works on any device with email access — no biometric hardware, identity provider, or client SDK required beyond the code entry field. |
-A single internal account can hold one credential of each type concurrently. Only one `PASSKEY` and one `EMAIL_OTP` per account in v1.
+A single internal account can hold one `EMAIL_OTP` credential and multiple distinct `PASSKEY` credentials concurrently. `OAUTH` credentials can be added for each supported provider identity.
## Registration vs. verification
@@ -501,7 +501,7 @@ Same pattern as the first activation: call `/challenge` to send a new OTP, then
## Managing credentials
-Every Global Account starts with a single credential — the one used in the quickstart. In production, encourage customers to register a second credential of a different type (e.g., an email OTP alongside a passkey) so the account is recoverable if their primary device is lost. Adding, revoking, and rotating credentials after the first all go through the same **two-step signed-retry** pattern.
+Every Global Account starts with a single credential — the one used in the quickstart. In production, encourage customers to register a backup credential, such as another passkey or an email OTP, so the account is recoverable if their primary device is lost. Adding, revoking, and rotating credentials after the first all go through the same **two-step signed-retry** pattern.
### List credentials
@@ -617,7 +617,7 @@ Requires an active session on an *existing* credential on the same account. The
- Only one credential of each type (`EMAIL_OTP`, `PASSKEY`) is allowed per internal account in v1. Registering a second credential of the same type returns `400 EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `400 PASSKEY_CREDENTIAL_ALREADY_EXISTS`.
+ Only one `EMAIL_OTP` credential is allowed per internal account. Multiple distinct `PASSKEY` credentials are allowed; registering the same WebAuthn credentialId twice returns `400 PASSKEY_CREDENTIAL_ALREADY_EXISTS`.
### Revoke a credential
diff --git a/openapi.yaml b/openapi.yaml
index bdde8032..70e482bc 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -3939,7 +3939,7 @@ paths:
description: |
Register an authentication credential for an Embedded Wallet customer.
- Embedded Wallet internal accounts are initialized with an `EMAIL_OTP` credential tied to the customer email on the account. Use this endpoint to add another credential (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential and one `PASSKEY` credential are supported per internal account.
+ Embedded Wallet internal accounts are initialized with an `EMAIL_OTP` credential tied to the customer email on the account. Use this endpoint to add another credential (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential is supported per internal account; multiple distinct `PASSKEY` credentials may be registered.
Adding a credential requires a signature from an existing verified credential on the same account. Call this endpoint with the new credential's details to receive `202` with `payloadToSign` and `requestId`. Use the session API keypair of an existing verified credential (decrypted client-side from its `encryptedSessionSigningKey`) to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `201` with the created `AuthMethod`. For `EMAIL_OTP`, the OTP email is triggered on the signed retry, and the credential must then be activated via `POST /auth/credentials/{id}/verify`.
operationId: createAuthCredential
@@ -4058,7 +4058,7 @@ paths:
requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
expiresAt: '2026-04-08T15:35:00Z'
'400':
- description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type that already exists on the internal account. Only one email OTP credential and one passkey credential are supported per internal account at this time.
+ description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` when registering an email OTP credential while one already exists, or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a passkey whose WebAuthn credentialId is already attached to the internal account.
content:
application/json:
schema:
@@ -7450,7 +7450,7 @@ components:
| UNSUITABLE_DOCUMENT | Document type is not accepted or not supported |
| INCOMPLETE | Document is missing pages or sides |
| EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is already registered on the target internal account; only one email OTP credential is supported per internal account at this time |
- | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential is already registered on the target internal account; only one passkey credential is supported per internal account in v1 |
+ | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the same WebAuthn credentialId is already registered on the target internal account |
enum:
- INVALID_INPUT
- MISSING_MANDATORY_USER_INFO
diff --git a/openapi/components/schemas/errors/Error400.yaml b/openapi/components/schemas/errors/Error400.yaml
index 248dc7e8..10c75ed8 100644
--- a/openapi/components/schemas/errors/Error400.yaml
+++ b/openapi/components/schemas/errors/Error400.yaml
@@ -48,7 +48,7 @@ properties:
| UNSUITABLE_DOCUMENT | Document type is not accepted or not supported |
| INCOMPLETE | Document is missing pages or sides |
| EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is already registered on the target internal account; only one email OTP credential is supported per internal account at this time |
- | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential is already registered on the target internal account; only one passkey credential is supported per internal account in v1 |
+ | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the same WebAuthn credentialId is already registered on the target internal account |
enum:
- INVALID_INPUT
- MISSING_MANDATORY_USER_INFO
diff --git a/openapi/paths/auth/auth_credentials.yaml b/openapi/paths/auth/auth_credentials.yaml
index b0ccb07f..e1894a48 100644
--- a/openapi/paths/auth/auth_credentials.yaml
+++ b/openapi/paths/auth/auth_credentials.yaml
@@ -7,8 +7,9 @@ post:
Embedded Wallet internal accounts are initialized with an `EMAIL_OTP`
credential tied to the customer email on the account. Use this endpoint
to add another credential (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP`
- back after it has been removed. Only one `EMAIL_OTP` credential and one
- `PASSKEY` credential are supported per internal account.
+ back after it has been removed. Only one `EMAIL_OTP` credential is
+ supported per internal account; multiple distinct `PASSKEY` credentials
+ may be registered.
Adding a credential requires a signature from an existing verified
@@ -160,11 +161,10 @@ post:
expiresAt: '2026-04-08T15:35:00Z'
'400':
description: >-
- Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or
- `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type
- that already exists on the internal account. Only one email OTP
- credential and one passkey credential are supported per internal
- account at this time.
+ Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS`
+ when registering an email OTP credential while one already exists, or
+ `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a passkey whose
+ WebAuthn credentialId is already attached to the internal account.
content:
application/json:
schema: