> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corbado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WebAuthn Signal API

> Native apps can ask credential managers to align passkeys stored on a device with the credentials the server accepts. Without it, deleted or unknown passkeys can keep appearing in the OS account chooser and fail after a successful biometric prompt.

## Keeping the Authenticator and Your Server in Sync

Your server and the user's credential manager hold two separate lists of passkeys, and nothing keeps them aligned automatically. When they drift, the user pays for it: a passkey your server no longer accepts is still offered in the account chooser, the user selects it, authenticates with Face ID or a fingerprint, and *then* the login fails.

That is the worst failure shape in passkey UX, because everything felt successful right up to the moment it did not. The entry also persists, so the same failure repeats on every attempt until the user digs into their credential manager settings.

Signalling closes that gap. It is not a UI flow, but it decides whether several of the flows in this section succeed, and it belongs in every production integration.

In a native app the stale credential surfaces through [Conditional UI](/passkey-ui-flows/native/passkey-login/conditional-ui) and the [app-start overlay](/passkey-ui-flows/native/passkey-login/overlay), both of which offer whatever the credential manager holds regardless of what your server still accepts.

<Info>
  **Support (August 2026):** Android 15+ via `androidx.credentials` **1.6.0** (stable since 8 April 2026). Apple platforms via `ASCredentialDataManager` on iOS, iPadOS, macOS and visionOS **26.2+**. For the browser equivalent see the [web Signal API](/passkey-ui-flows/web/signal-api).
</Info>

<Warning>
  **Apple renamed this API one point release after shipping it.** `ASCredentialUpdater` was introduced in 26.0 and **deprecated in 26.2** in favour of `ASCredentialDataManager`. Signalling is therefore possible from 26.0, but a new build should target `ASCredentialDataManager` and treat 26.2 as the floor. Sample code from WWDC25 and 2025-era articles uses the deprecated class.
</Warning>

### The three signals

| Purpose                                      | Android                                 | Apple                                                                                             |
| -------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Report a credential the server does not know | `SignalUnknownCredentialRequest`        | `reportUnknownPublicKeyCredential(relyingPartyIdentifier:credentialID:)`                          |
| Reconcile the full accepted list             | `SignalAllAcceptedCredentialIdsRequest` | `reportAllAcceptedPublicKeyCredentials(relyingPartyIdentifier:userHandle:acceptedCredentialIDs:)` |
| Update the displayed account details         | `SignalCurrentUserDetailsRequest`       | `reportPublicKeyCredentialUpdate(relyingPartyIdentifier:userHandle:newName:)`                     |

On Android all three are sent through `CredentialManager.signalCredentialState()` and carry a JSON string rather than typed fields.

<Steps>
  <Step title="On a failed login: signal the unknown credential">
    * When an assertion arrives for a credential your server does not recognise, report it. This carries no user identifier, so it is safe when the user is not authenticated.
    * Google Password Manager hides rather than deletes, so the credential can be restored if the signal was sent in error. Apple's credential managers may remove or hide it.
  </Step>

  <Step title="On a successful login: reconcile the full list">
    * Send the complete set of credential IDs your server accepts for that user. The provider may hide or remove credentials missing from the list and, if it supports restoration, may unhide credentials included again later.
  </Step>

  <Step title="After the user deletes a passkey in settings">
    * Reconcile again from your [passkey management](/passkey-ui-flows/native/passkey-management) screen so the provider can hide or remove the credential immediately, rather than waiting until the user's next login to receive the updated list.
  </Step>

  <Step title="After a profile change">
    * Report the updated account details so the OS account chooser shows current information.
  </Step>
</Steps>

<Warning>
  **Never send a partial or accidentally empty credential list.** The reconcile signal may hide or remove every passkey it does not find in the accepted list. An empty list is valid only when the authoritative server state confirms that the authenticated user has no accepted credentials, for example after deleting their final passkey. Treat the code that assembles this list as high-risk, and test it against accounts with zero, one and several credentials.
</Warning>

### Why Server-Side Revocation Still Needs Client Cleanup

Deleting the public key on your server revokes the credential immediately: any later assertion from it must be rejected. What that server-side action cannot do is remove the stale entry from the user's locally installed credential manager, because **your backend has no direct channel to the user's password manager**.

Client-side cleanup can happen only when the user's client runs again. Signal an unknown credential after a failed assertion, and reconcile the complete accepted list during the next authenticated session. Running that reconciliation after every successful sign-in is how server-initiated changes eventually reach credential managers that were not available when the revocation happened.

<Tip>
  Signals resolve with no information about what the provider did. You never learn whether a credential existed or whether the provider acted, which is deliberate and privacy-preserving. It also means signalling can never serve as an audit trail or a source of truth. Treat it as fire-and-forget hygiene, and never block a UI flow on the result.
</Tip>

### Platform differences worth planning for

<AccordionGroup>
  <Accordion title="Apple cannot signal a display name change">
    `reportPublicKeyCredentialUpdate` accepts only `newName`. The web and Android equivalents carry both `name` and `displayName`, so a display name change cannot be signalled from an iOS or macOS app.
  </Accordion>

  <Accordion title="Android enforces a rate limit">
    Android permits a maximum of 10 calls in any 120-second window; exceeding it results in throttling or rejection. `SignalCredentialRateLimitExceededException` carries a `retryMillis` value, so back off by that amount rather than retrying blind. That exception only exists from 1.6.0, which is one reason to require the stable release rather than an earlier beta.
  </Accordion>

  <Accordion title="Apple offers a signal with no web or Android equivalent">
    `reportUnusedPasswordCredential(domain:userName:)` tells credential managers that a password is no longer in use. This is directly relevant when retiring passwords on accounts that have moved to passkeys.
  </Accordion>

  <Accordion title="Background execution is supported on Android">
    Android explicitly supports sending signals from a background context, within the rate limit. No user presence or user verification is required on any platform.
  </Accordion>
</AccordionGroup>

### Further reading

* [Signal API for relying parties](https://developer.android.com/identity/credential-manager/signal-api-rp), Android Developers
* [ASCredentialDataManager](https://developer.apple.com/documentation/authenticationservices/ascredentialdatamanager), Apple Developer
* [What's new in passkeys](https://developer.apple.com/videos/play/wwdc2025/279/), WWDC25 session 279
* [WebAuthn Signal API explained](https://www.corbado.com/blog/webauthn-signal-api), Corbado blog
