Docs
Verification

Response Handling

Process and validate wallet responses to verification requests

After a wallet submits its credential presentation, the API decrypts, verifies, and stores the result. You retrieve it by polling the status endpoint.

Polling for results

GET /verifier/status/:request_id
Authorization: Bearer oat_your_publishable_key

Poll this endpoint while the status is created (wallet has not fetched the request yet) or claimed (wallet fetched the request JWT but has not responded):

async function waitForResult(requestId, apiKey) {
  while (true) {
    const res = await fetch(`${BASE_URL}/verifier/status/${requestId}`, {
      headers: { Authorization: `Bearer ${apiKey}` },
    })
    const data = await res.json()

    if (data.status !== 'created' && data.status !== 'claimed') return data
    await new Promise((r) => setTimeout(r, 2000))
  }
}

Response format

The result has three top-level fields: status (valid or failed), verification (arrays of cryptographic checks grouped by check category, each with an emoji status: ✅ passed, 🛑 failed), and presentation (extracted claims keyed by namespace/doctype for mDoc, or by vct for SD-JWT).

mDoc response

{
  "status": "valid",
  "verification": {
    "ISSUER_AUTH": [
      { "status": "✅", "check": "Issuer certificate must be valid" },
      { "status": "✅", "check": "The MSO must be valid at the time of verification" }
    ],
    "DEVICE_AUTH": [
      { "status": "✅", "check": "Device signature must be valid" }
    ],
    "DATA_INTEGRITY": [
      { "status": "✅", "check": "Issuer Auth must include a supported digestAlgorithm element" }
    ],
    "query_validation": [
      { "status": "✅", "check": "Presentation satisfies query: pid" }
    ]
  },
  "presentation": {
    "eu.europa.ec.eudi.pid.1": {
      "family_name": "Dupont",
      "given_name": "Marie",
      "birth_date": "1990-01-15",
      "age_over_18": true
    }
  }
}

SD-JWT response

{
  "status": "valid",
  "verification": {
    "issuer_auth": [
      { "status": "✅", "check": "SD-JWT issuer signature verified" }
    ],
    "holder_binding": [
      { "status": "✅", "check": "KB-JWT signature and claims verified" }
    ]
  },
  "presentation": {
    "eu.europa.ec.eudi.pid.1": {
      "family_name": "Dupont",
      "given_name": "Marie"
    }
  }
}

What gets verified

The API performs full cryptographic verification before returning a valid status:

mDoc verification

  • Device signature validation (ECDSA)
  • Issuer-signed data integrity (MSO digest matching)
  • Certificate chain validation
  • Session transcript binding (OID4VP Annex B.3.4.1)

SD-JWT verification

  • Issuer signature validation (ES256)
  • Key binding JWT validation (nonce + audience)
  • Disclosure hash verification
  • Expiration and issuance date checks

Error states

When verification fails, the status is failed and the failing checks appear inside verification with a 🛑 status. Failed checks may carry an additional reason field explaining why:

{
  "status": "failed",
  "verification": {
    "DEVICE_AUTH": [
      {
        "status": "🛑",
        "check": "Device signature must be valid",
        "reason": "Signature verification failed"
      }
    ]
  },
  "presentation": {}
}

There is no top-level error field. If the request record has expired (600 seconds without a response, or 60 seconds after the result was stored), the status endpoint returns:

{
  "error": "Authorization status not found"
}