Skip to main content

Verify Credentials with the DC API on iOS (Safari)

On iPhone and iPad the W3C Digital Credentials API (DC API) is a Safari feature. Your page calls navigator.credentials.get() from a web context, WebKit validates the call against the page origin, and iOS then routes it to the wallet layer, which asks the holder to authorise the release of the requested claims. Because every browser on iOS is built on WebKit, the behaviour you get in Safari is the behaviour you get everywhere on the platform.

Two things make this materially different from DC API verification on Android with Chrome, and they shape the whole of this guide:

  • One protocol, not two. Safari implements ISO/IEC 18013-7 Annex C (protocol identifier org-iso-mdoc) and does not offer OpenID4VP over the DC API. Requests are therefore always signed mdoc requests, and the credential format is always an ISO/IEC 18013-5 mdoc.
  • No open provider chooser. Android hands the request to Credential Manager, where any installed wallet may register as a provider. iOS has no equivalent open registry surfaced to the web, so which wallet can answer, and which document types it may answer with, is constrained by the platform rather than by your request.

This guide otherwise follows the same send and verify flow as the Send and Verify Credential guide, and uses the same iGrant.io APIs.

As a verifier on iOS you will:

  • Define the claims you require in a presentation definition, fixed to Annex C and to a signed DC API response mode.
  • Declare the origins your request may legitimately come from, since a signed request without expectedOrigins cannot be checked by the wallet.
  • Invoke the request from Safari, on the iPhone itself or by having the iPhone scan a QR code shown by a desktop browser.
  • Decrypt and validate the mdoc response returned to your page.
DC API prerequisite on iOS:
  1. Safari 26 or later, which is when the Digital Credentials API first shipped in a stable Apple browser (September 2025). There is no flag to enable it on an older version, so older devices need a fallback path rather than a setting change.
  2. A page served over HTTPS from the origin you list in expectedOrigins. The wallet compares the calling origin against that list and will reject a mismatch.
  3. For the cross-device flow (desktop browser, iPhone wallet), Bluetooth enabled on both devices, kept near each other for the proximity check.
  4. An age verification credential in your Data Wallet before trialling; see the Age Verification workflow.

Step 1: Get the API Key

To obtain your API key, please contact [email protected]. Once you have received your API key, enter it in the field below and click the Set API Key button to save it for future use.

Step 2: Create Presentation Definition (Verifier Admin)

To create a presentation definition for requesting proof, you can run the code block below using the Run button. Alternatively, you can manually copy the code block and use it in the body of the API request provided here. On iOS, DC API runs over ISO 18013-7 Annex C only, so the Request panel below comes preconfigured: the credential format is ISO/IEC 18013-5 mdoc/mDL and the response mode is DC API, Signed. Because the request is signed, expectedOrigins is required. It is prefilled with the origin this page is served from, so you can run the request straight away. In your own application, set it to your verifier origin so the wallet can detect replay from a malicious verifier.

The protocol and response mode selectors are locked here on purpose. Choosing OpenID4VP or an unsigned response mode would produce a request that Safari cannot dispatch, so the panel removes those options rather than letting you build one that fails at navigator.credentials.get() time.

Request

ISO 18013-7 Annex C Request / Response
W3C Working Draft 26 June 2026
ISO/IEC 18013-5 mdoc/mDL
DC API, Unsigned

Response

Tip: Once a presentation definition is created, the presentationDefinitionId can be reused to verify multiple credentials (Step 3).

Step 3: Create Verification Request (Verifier/Relying Party)

Now you can ask a wallet for the credential you defined in Step 2. When Step 2 succeeds it fills the presentationDefinitionId into the request below, and that id tells the wallet which credential to present, so check that it matches the one Step 2 returned before you click Run. Leave requestByReference set to true, which sends the wallet a link to the request instead of the whole request and keeps larger requests working smoothly. You can also copy the code block and use it in the body of the API request provided here.

Once the request succeeds, the Request Credential button becomes active. Clicking it hands the request to the browser, which invokes the wallet. Step 4 covers what happens next.

Request

Response

The presentation definition provided adheres to the specifications outlined in the DIF Presentation Exchange standard, as detailed here.

The response the API returns carries a per-browser request object. The client library picks the Safari variant when it detects a WebKit user agent and passes its protocol and data straight into navigator.credentials.get(), so you do not build the Annex C payload by hand.

Step 4: Send and Receive Verifiable Presentation (iOS Device)

Clicking Request Credential calls the DC API from the page. iOS then routes the request out of the browser and into the wallet layer, and what the holder sees depends on where that wallet is.

Same device (browsing in Safari on the iPhone or iPad). iOS presents the request in a system-controlled sheet on top of the page. The holder reviews the claims being asked for, authorises the release, and control returns to Safari with the response. There is no QR code and no camera step in this case, and the sheet is drawn by the operating system rather than by your page, so you cannot style or script it.

Cross device (browsing on a desktop, wallet on the iPhone). The desktop browser displays a QR code that encodes the hand-off, not the credential. Scanning it with the iPhone hands the request to the wallet on the phone. The two devices then establish an end-to-end encrypted channel and complete a Bluetooth proximity check, which is why both need Bluetooth on and need to be physically close. The holder consents on the phone, and the response travels back to the desktop page that made the call, so the desktop tab must stay open for the whole exchange.

In both cases the holder sees the requested claims before anything leaves the device, and because the credential is an mdoc, selective disclosure applies: an age check can return only an "over 18" result rather than a date of birth.

Once the holder consents, the credential is returned to the page, a green Credential shared successfully message appears above the panels, the Request Credential button changes to Credential Received, and the Response panel shows what came back. An Annex C response arrives as a base64 Response field holding an encrypted mdoc response, which is forwarded to the receive endpoint for decryption, so the raw value in the panel is not readable until the backend has processed it. If the holder dismisses the sheet, the API rejects with NotAllowedError or AbortError, which the page reports as a cancelled wallet operation rather than a failure of your request.

Step 5: Send and Receive Verifiable Presentation (Verifier/Relying Party)

  • The verifier (relying party) receives the requested credentials and can verify them. Read the received credential by executing the Read Verification History API.
  • From the response received, the vpTokenResponse can be decoded using the JWT Decoder.
  • Because the credential itself is an ISO/IEC 18013-5 mdoc rather than a JWT, use the mdoc Decoder to inspect the namespaces and the individual disclosed elements.
Trust status of the verifier

Annex C authenticates the verifier to the wallet through reader authentication, so the holder is shown who is asking before any element is released. On top of that, the wallet checks whether the verifier can be matched against a recognised trust list, exposed in the response through the isVerifiedWithTrustList and trustServiceProvider fields. If the verifier cannot be matched, the Data Wallet shows a "Service provider not verified" warning. See The "Service provider not verified" notice and Trust in the Wallet Ecosystem.

Try it Yourself

Watch a short demo of verifying credentials with the Digital Credentials API in Safari on iOS:

Watch the iOS DC API demo on YouTube

Frequently asked questions about the DC API on iOS

Which protocols can I request in Safari? Only ISO/IEC 18013-7 Annex C, identified as org-iso-mdoc. OpenID4VP over the DC API is not implemented in Safari, so a request built with responseMode: dc_api for OpenID4VP will not reach a wallet on iOS. If your verifier supports both platforms, build the request per platform rather than sending one shape everywhere.

Will this work in Chrome or Firefox on my iPhone? Those browsers use WebKit on iOS, so they inherit Safari's DC API implementation and its Annex C limitation. You do not get Chrome's OpenID4VP support by installing Chrome on an iPhone. Test against Safari and treat the others as the same engine.

Which credentials can actually be requested on iOS? Requests are limited to ISO 18013-5 mdocs, and iOS recognises only a fixed set of five mobile document type categories defined by Apple's Identity Document Services: EU age verification, EU personal ID (PID), photo ID, the Japan MyNumber card, and the mobile driving licence (mDL). A document is only requestable if the holder has provisioned one of these on the device. The two EU categories, age verification and PID, are the ones most relevant to European Digital Identity use cases. See the DC API overview for the identifiers and constraints.

Do I need a certificate or registration from Apple to act as a verifier? Annex C requires reader authentication, so your request is signed with a certificate the wallet can check, and signed requests must carry expectedOrigins matching the page that calls the API. Where the credential comes from Apple Wallet, Apple additionally requires the verifier to obtain a signing certificate from Apple and use the Verify with Wallet mechanism. Plan for certificate issuance and renewal as part of your release process, not as a one-off setup step.

Can a native iOS app request credentials without a browser? No. The DC API is a web platform API, and Apple has not shipped a native app-to-app verification equivalent, so a native iOS verifier needs a web view to make the call. A native app can act as a credential provider, but not as a requester. On Android the same capability is also exposed natively through Credential Manager, which is a genuine architectural difference between the two platforms rather than a temporary gap.

Why does my request succeed on Android but fail on iOS? The usual causes, in order: the request was built for OpenID4VP instead of Annex C, expectedOrigins does not match the origin actually serving the page (a staging host or a preview URL is a common culprit), the credential requested is not one iOS can present, or the Safari version predates 26. A cancelled sheet reports as NotAllowedError or AbortError, which means the holder declined rather than that the request was malformed.

Where to go next