Skip to main content

Overview

API integration provides complete control over the user verification experience through custom UIs and direct API calls. Choose this approach when you need full customization of the user journey.
When to use API Integration:
  • Need complete UI customization
  • Building native mobile applications
  • Require specific flow control logic
  • Want to handle multiple providers differently

Prerequisites

Implementation Steps

Step 1: Create Verification Session

Start a verification session with your chosen provider:
curl -X POST https://sandbox.api.hopae.com/connect/v1/verifications \
  -u "CLIENT_ID:CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "providerId": "bankidse"
  }'

Step 2: Handle Flow Response

The API response varies by verification flow type:
  • QR Code Flow
  • Redirect Flow
  • Push Flow
Response
{
  "verificationId": "eec15acab80b472bb20160d5751b8b40",
  "status": "initiated",
  "providerId": "bankidse",
  "flowType": "qr",
  "flowDetails": {
    "qrData": "bankidse:///?token=7c40b5c1-..."
  },
  "expiresAt": "2024-07-30T10:05:00.000Z"
}
Implementation:
  1. Generate QR code from flowDetails.qrData
  2. Display QR code to user
  3. Poll for verification status
Example
// Generate QR code
const qrCode = await QRCode.toDataURL(response.flowDetails.qrData);

// Display in UI
document.getElementById('qr-code').src = qrCode;

Step 3: Poll for Status

Poll GET /verifications/{id} until status becomes completed. Then call GET /verifications/{id}/userinfo to obtain the user attributes and provenance.
const pollVerification = async (verificationId) => {
  const pollInterval = setInterval(async () => {
    try {
      const response = await fetch(`https://sandbox.api.hopae.com/connect/v1/verifications/${verificationId}`, {
        headers: {
          Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
        },
      });

      const data = await response.json();

      switch (data.status) {
        case "completed":
          clearInterval(pollInterval);
          // Fetch user + provenance
          const uiResp = await fetch(`https://sandbox.api.hopae.com/connect/v1/verifications/${verificationId}/userinfo`, {
            headers: { Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}` }
          });
          const userinfo = await uiResp.json();
          handleSuccess(userinfo);
          break;
        case "failed":
        case "expired":
          clearInterval(pollInterval);
          handleError(data);
          break;
        // Continue polling for 'initiated' status
      }
    } catch (error) {
      clearInterval(pollInterval);
      handleError(error);
    }
  }, 3000); // Poll every 3 seconds

  // Set timeout for maximum polling duration
  setTimeout(() => {
    clearInterval(pollInterval);
    handleTimeout();
  }, 300000); // 5 minutes timeout
};

Step 4: Fetch User and Provenance

When status is completed, fetch GET /verifications/{id}/userinfo to retrieve personal attributes and verification context. You’ll see the personal claims alongside provenance (channel, credentials, evidence, metadata).
Request
curl --request GET \
  --url 'https://sandbox.api.hopae.com/connect/v1/verifications/{verificationId}/userinfo' \
  -u 'CLIENT_ID:CLIENT_SECRET'
Response
{
  "sub": "otV9EMJr-iG-dj-AHhrCslfdRkUUBQJ1",
  "acr": "urn:hopae:loa3",
  "hopae_loa": 3,
  "hopae_loa_label": "substantial",
  "user": {
    "birthdate": "1905-04-04",
    "given_name": "OK",
    "family_name": "TESTNUMBER",
    "nationality": "LT",
    "name": "OK TESTNUMBER",
  },
  "missing_claims": ["email", "gender", "picture"],
  "provenance": {
    "presentation": {
      "channel": {"type": "centralized_idp", "transport": "internet"},
      "credentials": [
        {
          "type": "smartid",
          "issuer": {
            "id": "urn:hopae:issuer:ee:smartid",
            "authority_name": "SK ID Solutions AS",
            "is_government": false
          },
          "claims": {
            "documentNumber": "PNOLT-40504040001-MOCK-Q",
            "birthdate": "1905-04-04",
            "countryCode": "LT",
            "givenName": "OK",
            "surname": "TESTNUMBER"
          },
          "evidence": {
            "token": {
              "id_token": "<BASE64_ID_TOKEN>",
              "expires_at": "2025-10-31T05:43:14.000Z",
              "access_token": "<OPAQUE_ACCESS_TOKEN>",
              "token_type": "Bearer"
            },
            "names": "id_token;expires_at;access_token;token_type"
          }
        }
      ]
    },
    "_metadata": {
      "verification_id": "fc2523d3270f4b64a4c461fb9af7e086",
      "verified_at": "2025-10-28T06:09:48.484Z"
    }
  }
}
Parsing tips:
  • provenance.presentation.credentials[].type reuses the providerId you supplied (for example, smartid, bankidse).
  • provenance.presentation.credentials[].evidence.token contains provider-specific keys; inspect the semicolon-delimited names string to know which ones are present.
  • Call GET /verifications/{verificationId}/evidence if you only need this token/names object without the rest of the UserInfo payload.
Evidence keys differ per provider. Use the semicolon-delimited names string to determine which fields are present under evidence.token.

Error Handling

  • Common Errors
  • Error Response Example
ErrorDescriptionAction
300001Session not foundVerify session ID is correct
300003Invalid status transitionSession already completed
400002Provider not enabledEnable provider in dashboard
400005Provider unavailableTry again later or use fallback

Error Code Reference

View complete error codes and handling strategies

Best Practices

  • Never expose Client Secret in frontend code - Implement proper session management - Use HTTPS for all API calls - Validate all responses server-side
  • Show clear loading states during polling - Provide timeout warnings before expiry - Offer alternative authentication methods - Display provider-specific instructions
  • Implement exponential backoff for polling - Cache provider information - Minimize API calls with proper state management - Use webhooks for real-time updates (if available)

Comparison with Hosted OIDC Flow

Consider the Hosted OIDC Flow First: The OIDC integration guide handles provider UX, PKCE, and token issuance automatically. Choose direct APIs only when you need full UI control, native-only UX, or bespoke flow logic.

Next Steps