> ## 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 local device authentication.

## Keeping the Authenticator and your Server in Sync

Your server and the user's credential manager hold separate passkey records and they are not synchronized automatically. If they differ, the account chooser may offer a passkey that the server no longer accepts. The user can complete local device authentication before the login fails. The stale entry may also remain available on later attempts until the credential manager updates it.

Where the platform and credential provider support it, signalling can reduce that gap. It is a progressive, best-effort lifecycle capability rather than a UI flow or authoritative source of truth; unsupported integrations must continue to work without it.

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 support signalling from **26.0**: use legacy `ASCredentialUpdater` on 26.0–26.1 and current `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; use 26.2 as the recommended new-build target for the current API, not as the feature's availability 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="When the server rejects an unknown credential">
    * Send this signal only when the credential ID is conclusively absent from or no longer accepted by server state. Do not send it for invalid signatures, expired challenges, RP or origin mismatch, cancellation, network failure or other transport and verification errors.
    * A provider may hide or remove the credential; some providers may later unhide it after a complete accepted-list signal, but do not rely on recoverability or claim that provider action occurred.
  </Step>

  <Step title="At an authenticated reconciliation point">
    * According to a documented schedule, send the complete set of credential IDs your server accepts for that user. Typical points include selected authenticated refreshes and server credential-set changes, subject to platform rate limits.
  </Step>

  <Step title="After a successful credential-set mutation">
    * After creation or server-side revocation, reconcile the complete server-side list while the user is authenticated, subject to availability, coalescing and rate limits. Revocation never waits for the signal.
  </Step>

  <Step title="After a profile change">
    * Report only the fields supported by the current platform so enabled credential managers can choose to update them. Do not block the profile change or claim that an update occurred.
  </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

Revoking the credential in authoritative server state makes it unusable immediately: every later assertion from it must be rejected. The server may retain an inactive record for audit. What revocation 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 only after the server confirms that the submitted credential is unknown. Reconcile the complete accepted list on a documented authenticated schedule, including after authoritative credential-set changes and selected refresh points. The schedule should account for capability, coalescing and rate limits; revocation itself must never wait for signalling.

<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>

## Acceptance criteria

| Benchmark as          | User reach |
| --------------------- | ---------- |
| Supporting capability | Low        |

Native signalling does not create or promote passkeys. It reduces stale credential choices after server credential state changes. A signal request is best effort and never proves that a credential provider changed anything.

| ID          | Level   | Acceptance criterion                                                                                                                                                                                                                                                                                                                          | How to verify                                                                                                                                                                                                                                       |
| ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `N3.2-AC01` | Core    | Native signal APIs are checked at runtime and used as an enhancement, so an unsupported OS or library version does not break login, profile updates or passkey management.                                                                                                                                                                    | Run these operations with the relevant API available and unavailable. Each operation reaches the same result without a crash or unsupported call.                                                                                                   |
| `N3.2-AC02` | Core    | Send an unknown-credential signal only when the presented credential ID is conclusively absent from server records. A credential known to the server but revoked uses full-list reconciliation. Account restrictions, invalid signatures, expired challenges, cancellation and network failures do not trigger the unknown-credential signal. | Test an absent credential ID, known revoked credential, restricted account, invalid signature, expired challenge, network error and cancellation. Only the absent ID emits the unknown-credential signal; revocation uses full-list reconciliation. |
| `N3.2-AC03` | Core    | Successful credential creation or revocation and the documented authenticated refresh point schedule full-list reconciliation using the latest server state. Equivalent pending work is coalesced, while failed or unauthenticated paths do not schedule it.                                                                                  | Complete successful and failed creation, revocation and the configured refresh path. Confirm that successful authenticated triggers retain one latest reconciliation and other paths add no work.                                                   |
| `N3.2-AC04` | Core    | The full-list assembly path prevents partial or accidentally empty snapshots. An empty accepted list is sent only when server state confirms that the authenticated user has no accepted credentials.                                                                                                                                         | Simulate partial data, pagination failure, timeout, stale cache and a legitimate zero-credential account; only the confirmed zero-credential state emits an empty list.                                                                             |
| `N3.2-AC05` | Core    | Android signal calls stay within ten calls in any 120-second window. A rate-limited request is either dropped or retried no earlier than `retryMillis` using current server state.                                                                                                                                                            | Generate more than ten eligible triggers in 120 seconds and force `SignalCredentialRateLimitExceededException`. Confirm the limit, then test dropping and delayed retry; no retry is early or replays stale data.                                   |
| `N3.2-AC06` | Core    | Use the supported native signal interface: `ASCredentialDataManager` on Apple 26.2 or later, the legacy `ASCredentialUpdater` only on 26.0–26.1, and `CredentialManager.signalCredentialState()` with a compatible AndroidX Credentials version on Android.                                                                                   | Run the supported Apple and Android matrix and inspect the selected adapter and request type. Each platform uses its current supported interface.                                                                                                   |
| `N3.2-AC07` | Core    | Every native signal carries the RP ID configured for the credential.                                                                                                                                                                                                                                                                          | Capture all signal types for each configured RP and compare the RP ID with the credential record. No origin, host alias or unrelated RP ID is substituted.                                                                                          |
| `N3.2-AC08` | Core    | An accepted signal request is never treated as proof that a credential provider updated, hid, removed or restored a credential.                                                                                                                                                                                                               | Make the platform accept a request while the test provider takes no action. No product message or server state claims a provider-side result.                                                                                                       |
| `N3.2-AC09` | Core    | WebAuthn user handles and credential IDs use the exact current server values in the platform-required representation; a product account ID is not substituted for the WebAuthn user handle.                                                                                                                                                   | Compare captured Apple `Data` arguments and Android JSON/Base64URL values with the server's user-handle and credential records for zero, one and several credentials.                                                                               |
| `N3.2-AC10` | Quality | After a confirmed account-name change, the metadata signal contains the current fields supported by that platform and does not create, replace or revoke a credential.                                                                                                                                                                        | Change name and display-name fields independently, inspect Apple and Android requests and compare credential inventory before and after. The supported current values are sent and the credential set is unchanged.                                 |

### References

* **Relevant criteria:** `N3.2-AC02`–`N3.2-AC04`, `N3.2-AC07`–`N3.2-AC10`: [Web Authentication Level 3: Signal Credential Changes to the Authenticator](https://www.w3.org/TR/webauthn-3/#sctn-signal-methods) defines trigger-specific inputs, complete-list safety, metadata semantics and best-effort provider behavior.
* **Relevant criteria:** `N3.2-AC01`–`N3.2-AC07`, `N3.2-AC09`: [Android Developers: Keep credentials consistent with credential providers](https://developer.android.com/identity/credential-manager/signal-api-rp) defines native availability, request types, payloads, the ten-call rolling limit and testing; [SignalCredentialRateLimitExceededException](https://developer.android.com/reference/androidx/credentials/exceptions/publickeycredential/SignalCredentialRateLimitExceededException) defines `retryMillis`.
* **Relevant criteria:** `N3.2-AC01`, `N3.2-AC03`, `N3.2-AC06`–`N3.2-AC10`: [Apple Developer: ASCredentialDataManager](https://developer.apple.com/documentation/authenticationservices/ascredentialdatamanager) defines the current interface, supported reports and result semantics; [ASCredentialUpdater](https://developer.apple.com/documentation/authenticationservices/ascredentialupdater) documents the deprecated 26.0–26.1 interface.
* **Relevant criteria:** `N3.2-AC03`–`N3.2-AC04`, `N3.2-AC08`: [Corbado: WebAuthn Signal API](https://www.corbado.com/blog/webauthn-signal-api) provides implementation experience for reconciliation timing, complete-list safety and provider uncertainty.

### 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
