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

# Digital Credentials (Native) Integration

> Verify government-issued identity credentials from device wallets using the W3C Digital Credentials API

## Overview

Digital Credentials integration lets you verify identity documents (passports, ID cards, driver's licenses) stored in device wallets like Google Wallet and Apple Wallet. Your app requests the credential, the user consents, and Hopae Connect validates the cryptographically signed response.

<Info>
  **When to use this integration:**

  * Building a native mobile app (Android or iOS)
  * Building a web app that calls the DC API directly
  * Want the wallet request payload returned immediately (no QR scan or polling)
  * Verifying credentials from decentralized wallet providers (e.g., `us-id-pass`)
</Info>

## How it works

```
Your App                         Hopae Connect                    Device Wallet
  |                                   |                               |
  |  1. Create verification           |                               |
  |  POST /connect/v1/verifications   |                               |
  |---------------------------------->|                               |
  |  { verificationId, request }      |                               |
  |<----------------------------------|                               |
  |                                   |                               |
  |  2. Present request to wallet (on-device, no Hopae call)          |
  |-------------------------------------------------->                |
  |  signed credential                |                               |
  |<--------------------------------------------------|               |
  |                                   |                               |
  |  3. Submit credential             |                               |
  |  POST /connect/v1/verifications/dc-response                       |
  |---------------------------------->|                               |
  |  { status: success }              |                               |
  |<----------------------------------|                               |
  |                                   |                               |
  |  4. Retrieve verified identity    |                               |
  |  GET /verifications/{id}/userinfo |                               |
  |---------------------------------->|                               |
  |  { given_name, birthdate, ... }   |                               |
  |<----------------------------------|                               |
```

Your app makes **3 API calls** to Hopae Connect. The wallet interaction (step 2) happens entirely on-device — Hopae is not involved.

## Prerequisites

* API credentials from the [Console](https://console.hopae.com)
* A DC-enabled provider (e.g., `us-id-pass`) enabled for your app
* Android: [CredentialManager API](https://developer.android.com/identity/digital-credentials) (requires Google Play Services)
* iOS: Safari 26+ with Digital Credentials API support
* Web: Chrome 128+ with Digital Credentials API support

## Implementation Steps

### Step 1: Create Verification Session

Create a verification with `options.mode = "native"` to receive the wallet request payload immediately in the response.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://sandbox.api.hopae.com/connect/v1/verifications \
    -u "CLIENT_ID:CLIENT_SECRET" \
    -H "Content-Type: application/json" \
    -d '{
      "providerId": "us-id-pass",
      "options": {
        "mode": "native",
        "platform": "android",
        "expected_origins": ["https://your-app-domain.com"]
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://sandbox.api.hopae.com/connect/v1/verifications", {
    method: "POST",
    headers: {
      Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      providerId: "us-id-pass",
      options: {
        mode: "native",
        platform: "android",
        expected_origins: ["https://your-app-domain.com"],
      },
    }),
  });
  ```

  ```python Python theme={null}
  import requests
  import base64

  credentials = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()

  response = requests.post(
      "https://sandbox.api.hopae.com/connect/v1/verifications",
      headers={
          "Authorization": f"Basic {credentials}",
          "Content-Type": "application/json",
      },
      json={
          "providerId": "us-id-pass",
          "options": {
              "mode": "native",
              "platform": "android",
              "expected_origins": ["https://your-app-domain.com"],
          },
      },
  )
  ```
</CodeGroup>

#### Options parameters

| Parameter          | Required | Description                                                                                                                                              |
| :----------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`             | Yes      | Set to `"native"` to receive the wallet request payload in the response. Without this, the payload is not returned.                                      |
| `platform`         | Yes      | `"android"` or `"ios"`. Determines the VP request format — Android returns JSON (OpenID4VP), iOS returns binary CBOR (ISO mDOC).                         |
| `expected_origins` | Yes      | Array of origins your app presents credentials from. The wallet validates that the request originates from one of these before releasing the credential. |

#### Response

```json theme={null}
{
  "verificationId": "f775c594fc9c4ac59848aac485862d71",
  "providerId": "us-id-pass",
  "status": "awaiting_user_action",
  "flowType": "dc",
  "flowDetails": {
    "request": {
      "protocol": "openid4vp-v1-unsigned",
      "data": {
        "response_type": "vp_token",
        "response_mode": "dc_api.jwt",
        "nonce": "13f16b12-a4b7-4d87-b654-41bb518e59d6",
        "client_id": "decentralized_identifier:did:web:...",
        "expected_origins": ["https://your-app-domain.com"],
        "dcql_query": { "..." : "..." },
        "client_metadata": { "..." : "..." }
      }
    },
    "dcSessionId": "13f16b12-a4b7-4d87-b654-41bb518e59d6"
  },
  "createdAt": "2026-06-03T15:22:42.404Z",
  "expiresAt": "2026-06-03T15:52:42.404Z",
  "verification_model": "disclosure"
}
```

<Note>
  When `platform` is `"ios"`, the `request.data` contains `deviceRequest` and `encryptionInfo` (binary CBOR) instead of the JSON fields shown above.
</Note>

The key field is `flowDetails.request` — pass this to the device wallet in step 2. The session expires after 30 minutes.

### Step 2: Request Credential from Wallet

Pass `flowDetails.request` from step 1 to the W3C Digital Credentials API on your platform. This step happens entirely on-device — no Hopae API call is needed.

<Tabs>
  <Tab title="Android">
    Use Android [CredentialManager](https://developer.android.com/identity/digital-credentials) with `GetDigitalCredentialOption`:

    ```kotlin theme={null}
    val requestJson = Gson().toJson(flowDetails.request)
    val option = GetDigitalCredentialOption(requestJson = requestJson)
    val credentialRequest = GetCredentialRequest(listOf(option))

    val result = credentialManager.getCredential(context, credentialRequest)
    val responseString = result.credential.data.getString("response")
    // Use responseString in step 3
    ```
  </Tab>

  <Tab title="iOS / Web">
    Use `navigator.credentials.get()` in Safari 26+ or Chrome 128+:

    ```javascript theme={null}
    const credential = await navigator.credentials.get({
      mediation: "required",
      digital: {
        requests: [{
          protocol: flowDetails.request.protocol,
          data: flowDetails.request.data,
        }],
      },
    });

    const responseString = credential.data.response;
    // Use responseString in step 3
    ```
  </Tab>
</Tabs>

The OS displays a consent screen showing which data is being requested. The user must approve before the credential is released.

### Step 3: Submit Wallet Response

Send the signed credential response to Hopae Connect for validation.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://sandbox.api.hopae.com/connect/v1/verifications/dc-response \
    -u "CLIENT_ID:CLIENT_SECRET" \
    -H "Content-Type: application/json" \
    -d '{
      "sessionId": "f775c594fc9c4ac59848aac485862d71",
      "response": "eyJhbGciOiJFUzI1NiIs..."
    }'
  ```

  ```javascript Node.js theme={null}
  const result = await fetch(
    "https://sandbox.api.hopae.com/connect/v1/verifications/dc-response",
    {
      method: "POST",
      headers: {
        Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        sessionId: verificationId,
        response: responseString, // from step 2
      }),
    }
  );
  ```

  ```python Python theme={null}
  result = requests.post(
      "https://sandbox.api.hopae.com/connect/v1/verifications/dc-response",
      headers={
          "Authorization": f"Basic {credentials}",
          "Content-Type": "application/json",
      },
      json={
          "sessionId": verification_id,
          "response": response_string,  # from step 2
      },
  )
  ```
</CodeGroup>

| Parameter   | Description                               |
| :---------- | :---------------------------------------- |
| `sessionId` | The `verificationId` from step 1.         |
| `response`  | The signed credential string from step 2. |

<Info>
  This endpoint requires the same Basic Auth credentials (`CLIENT_ID:CLIENT_SECRET`) as steps 1 and 4. The credential is also validated cryptographically by the Verifier — signature verification, nonce binding, origin check, and trust chain validation against government issuer certificates. A forged or replayed credential is rejected regardless of the caller.
</Info>

**Success response:**

```json theme={null}
{
  "status": "success",
  "data": {
    "family_name": "Garcia",
    "given_name": "Kelly",
    "birth_date": "1987-02-01",
    "sex": 2,
    "document_number": "D12345678",
    "issue_date": "2003-02-10",
    "expiry_date": "2052-02-10",
    "issuing_country": "XX",
    "issuing_authority": "Test Issuing Authority"
  },
  "sessionId": "f775c594fc9c4ac59848aac485862d71"
}
```

### Step 4: Retrieve Verified Claims

Once the verification is complete, retrieve the normalized identity data with provenance.

```bash theme={null}
curl -X GET https://sandbox.api.hopae.com/connect/v1/verifications/{verificationId}/userinfo \
  -u "CLIENT_ID:CLIENT_SECRET"
```

```json Response theme={null}
{
  "sub": "LF4E9dRJylDBHJO7eKsIurVmVmt6tRA0",
  "provider_id": "us-id-pass",
  "verification_model": "disclosure",
  "hopae_loa": 4,
  "hopae_loa_label": "high",
  "amr": ["us-id-pass"],
  "user": {
    "source_id": "D12345678"
  },
  "missing_claims": [],
  "provenance": {
    "presentation": {
      "channel": { "type": "wallet", "transport": "internet" },
      "credentials": [
        {
          "type": "us-id-pass",
          "claims": {
            "given_name": "Kelly",
            "family_name": "Garcia",
            "birth_date": "1987-02-01",
            "sex": 2,
            "document_number": "D12345678",
            "issue_date": "2003-02-10",
            "expiry_date": "2052-02-10",
            "issuing_country": "XX",
            "issuing_authority": "Test Issuing Authority"
          },
          "issuer": {
            "authority_name": "US Digital ID Issuers",
            "is_government": true
          },
          "evidence": { "vpClaims": "..." }
        }
      ]
    },
    "_metadata": {
      "verification_id": "b93a6140a7f04c539789112503c5dcb0",
      "verified_at": "2026-06-03T15:50:48.672Z",
      "status": "completed"
    }
  }
}
```

<Info>
  Identity claims are available in `provenance.presentation.credentials[].claims`. The `sub` field is a stable pseudonymous identifier derived from the document number — consistent across verifications of the same document.
</Info>

## Error Handling

<Tabs>
  <Tab title="API Errors">
    | Scenario                             | Step    | Error                                             |
    | :----------------------------------- | :------ | :------------------------------------------------ |
    | Invalid credentials                  | 1, 3, 4 | `401 AUTH_INVALID_CREDENTIALS`                    |
    | Provider not enabled                 | 1       | `400 PROVIDER_NOT_ENABLED`                        |
    | Provider doesn't support native mode | 1       | `400 PROVIDER_ERROR`                              |
    | Missing `platform` in native mode    | 1       | `400 VALIDATION_MISSING_PARAMETER`                |
    | Invalid / tampered credential        | 3       | `{ status: "fail", code: "VERIFICATION_FAILED" }` |
    | Session expired                      | 3, 4    | `400 SESSION_EXPIRED`                             |
    | Verification not found               | 4       | `404 SESSION_VERIFICATION_NOT_FOUND`              |
  </Tab>

  <Tab title="Client-Side Errors">
    | Scenario                         | Step | Handling                                                   |
    | :------------------------------- | :--- | :--------------------------------------------------------- |
    | No matching credential in wallet | 2    | Show message asking user to add credential to their wallet |
    | User denied consent              | 2    | Allow retry or show alternative verification options       |
    | DC API not supported             | 2    | Require supported browser/OS version                       |
  </Tab>
</Tabs>

## Available Providers

| Provider ID  | Credential                    | Country | Platforms    |
| :----------- | :---------------------------- | :------ | :----------- |
| `us-id-pass` | US Digital Passport (ID PASS) | US      | Android, iOS |

More DC-enabled providers will be added. The integration flow is identical — only `providerId` changes.

## Comparison with Standard API Flow

<Warning>
  **This guide is for native DC providers only.** Most eID providers (BankID, MitID, Smart-ID, etc.) use the standard [REST API integration](/guides/api-integration) with QR, redirect, or push flows. Use this guide only when integrating DC-enabled wallet providers listed above.
</Warning>

|                        | Standard API Flow              | DC Native Flow                 |
| :--------------------- | :----------------------------- | :----------------------------- |
| **Providers**          | Most eIDs (QR, redirect, push) | Wallet providers (DC API)      |
| **API calls**          | 2–3 + polling                  | 3 (no polling)                 |
| **Wallet interaction** | Handled by Hopae web UI        | Your app calls DC API directly |
| **Credential format**  | OAuth2/OIDC tokens             | W3C Verifiable Presentation    |
| **`options.mode`**     | Not needed                     | `"native"` required            |

## Next Steps

<CardGroup cols={2}>
  <Card title="Standard API Integration" icon="code" href="/guides/api-integration">
    For QR, redirect, and push flow providers
  </Card>

  <Card title="Verification API Reference" icon="square-terminal" href="/api-reference/verifications/create-verification">
    Full endpoint documentation
  </Card>

  <Card title="Return Data Model" icon="database" href="/guides/concepts/return-data-model">
    Understanding user claims and provenance
  </Card>

  <Card title="Digital ID Types" icon="id-card" href="/guides/concepts/digital-id-types">
    Learn about wallet-based credentials
  </Card>
</CardGroup>
