For the complete documentation index, see llms.txt. This page is also available as Markdown.

Verification API Reference

This page is the HTTP contract for the Verification API — request parameters, response schemas, session states, and error codes. For the conceptual walkthrough — integration patterns, assurance tiers, and what each evidence field means — start with Get Started or Verify from a Web App.


Prerequisites

  • A Blerify service account with the verifications.api role — see Authentication

  • A verification template UUID, created in the Blerify Portal

  • Your backend can reach the Blerify API base URL for your environment


Authentication

Every request your backend makes must carry a service account token:

Authorization: Bearer <service_account_token>

Tokens are obtained via the OAuth 2.0 client_credentials grant using private_key_jwt as the client authentication method (RFC 7523). See Authentication for how to create a service account and exchange credentials for a token.

Cache the token and refresh before expiry — don't request a new one on every call. A 401 means your token has expired. A 403 on a template that exists means your service account hasn't been granted access to that project in the Portal.


Base URL and conventions

All paths are relative to https://api.blerify.com. Your Blerify admin provides the exact base URL for staging and production environments.

Path namespaces. The runtime endpoints live under two distinct prefixes:

Surface
Prefix
Auth
Used by

Verifier (your backend)

/client/api/v2/openid4vp/organizations/:oid/projects/:pid/verifications

Authorization: Bearer <service_account_token>

Your backend

Wallet / public

/public/api/v1/openid4vp/organizations/:oid/projects/:pid/verifications

None — the one-time request UUID is the bearer secret

The user's wallet

Content type. All endpoints accept and return application/json.

Session TTL. 5 minutes from creation by default, configurable per session or per template in the Portal. After expiry, all endpoints for that session return 410 Gone. The user needs a fresh session to retry.

Error shape. Every error response uses:

For the full OpenAPI spec, see https://dev.blerify.com.


Endpoints

Your backend calls exactly two endpoints. Everything else — the wallet fetching the credential request, the wallet submitting the presentation, attestation data exchange — happens between the wallet and Blerify directly. You don't implement any of that.

Start a verification session

Creates a new session from the template identified by {vid}. Call this from your backend when the user is ready to verify.

Request body

Field
Required
Description

nonce

Yes

A fresh, random, unguessable value your backend generates per session. It cryptographically binds the user's presentation to this session — replay protection. Never reuse a nonce. Minimum 6 characters.

expirationTime

No

Session lifetime in seconds. Defaults to 300 (5 minutes), or the template-level value if one is set in the Portal.

redirect_uri

No

Where the user's browser returns after completing the flow. Must match a URI you whitelisted in the Portal. Required for same-device web flows.

client_context.session_ip

No

The user's IP address as your frontend sees it. Blerify compares this against the wallet's IP as a relay-attack signal at Standard tier and above. Include it from the start.

client_context.session_id

No

Your internal session identifier. Echoed back in the result's evidence so you can link Blerify's record to your own audit trail.

Response — 200

Field
What to do with it

transaction_id

Your key for polling this session. Pass it to every subsequent call.

request_id

One-time UUID identifying the signed credential request for this session. Use it to build the wallet Universal Link (see below). Single-use and short-lived.

request_uri_method

post or get. Copy verbatim into the wallet Universal Link.

client_id_scheme

did or x509_san_dns. Copy verbatim into the wallet Universal Link.

client_id

The verifier identifier the user's wallet will display and validate. Copy verbatim into the wallet Universal Link.

Errors

Status

error

What to do

400

invalid_request

Malformed JSON or invalid field. Fix and retry.

401

invalid_token

Token missing or expired. Refresh your service account token.

403

forbidden

Your service account isn't authorized for this template. Check your Portal configuration.

404

not_found

Template doesn't exist. Verify the UUID.

Wrap the session response into a URL that opens the Blerify Wallet:

The request URL you encode into request_uri is the public wallet endpoint for this session, built from request_id:

All four query parameters are required. URL-encode the request_uri value. Build a fresh link per session — request_id is single-use.

Use this same URL for all three integration patterns:

  • Same-device (mobile web): render it as an anchor or button

  • Cross-device (desktop): encode it in a QR code

  • App-to-app (native mobile): open it via your platform's URL-opening API — see Mobile Integration


Retrieve the result

Returns the current session status and, once the session reaches COMPLETED or FAILED, the full result. Poll this every 1–2 seconds.

Note the query parameter name: transaction-id (hyphen), while the response body field is transaction_id (underscore).

Query parameters

Parameter
Required
Description

transaction-id

Yes

The UUID returned by the session-creation call.

Errors

Status

error

What to do

400

invalid_request

Missing transaction-id. Fix and retry.

401

invalid_token

Token expired. Refresh and retry.

403

forbidden

Your service account can't read this session.

404

not_found

transaction-id doesn't exist.

410

session_expired

Session TTL elapsed. Start a new one.


Session states

A session moves through these states in order:

While the session is in CREATED, PENDING, SUBMITTED, or VALIDATING, the poll response is:

Treat all in-progress states the same way: keep polling.

COMPLETED vs FAILED. COMPLETED means Blerify produced a verdict — including negative ones. A revoked or expired credential returns COMPLETED with the relevant flags set inside credentials[].result. FAILED means no trustworthy verdict could be produced: malformed request, replay or nonce mismatch, expired session, a required credential or attestation item was never submitted, or an unrecoverable internal error.

A credential whose signature doesn't verify also returns COMPLETED, but that entry's data is withheld (data: null) because the claims can't be trusted.


Response shapes

assurance_level and effective_tier

Every completed response carries both fields:

  • assurance_level — what your template requested

  • effective_tier — what the evidence actually supports

On a COMPLETED result, the two always match — required inputs all arrived and were valid. effective_tier is retained in the envelope for backward compatibility. Always enforce your risk policy against effective_tier.

Per-credential result fields

Each entry in credentials[] carries a result object:

Field
Type
Description

credential_valid

boolean

Overall verdict. true only when every component check passes. A single failing check sets this to false.

signature_valid

boolean

The issuer's cryptographic signature is valid. When false, that entry's data is withheld.

issuer_trusted

boolean

The issuer is on the trust list configured in your template.

revoked

boolean

The issuer revoked this credential before presentation.

expired

boolean

The credential's validity period has passed.

holder_binding

string

VERIFIED when the presenter cryptographically proved they hold the credential's signing key.

COMPLETED reports facts — it never automatically blocks. You apply your own policy.


Basic tier

Basic evidence is minimal: a timestamp and the cryptographic validation results per credential. No hardware attestation, no network signals, no signed_evidence JWT.

The data.namespaces structure reflects the ISO 18013-5 mobile document format. Fields are grouped by namespace; most identity document fields live under org.iso.18013.5.1. The exact fields present depend on what your template requested — the user only disclosed what you asked for.


Standard tier — Android

Standard adds network signals, key attestation, device metadata, a document render image, and a signed audit JWT.


Standard tier — iOS

On iOS, three fields differ from the Android response. All three reflect a structural difference in what Apple exposes to third-party apps, not a failure or a downgrade signal:

  • device_metadata.attested is false — Apple's attestation format doesn't include device model or OS version, so the wallet self-reports them

  • key_attestation.security_level_attested is false — Apple provides no API for third-party apps to cryptographically prove Secure Enclave key residency

  • key_biometric_binding.attested is false — Apple's attestation format carries no key access-control flags

The Secure Enclave still enforces biometric requirements locally — the limitation is that this can't be proven remotely. See Platform Attestation for the full breakdown.

device_attestation_reason is not_requested here, not platform_not_available. Standard never requests device attestation on either platform — the reason reflects your template configuration, not a platform limitation. The value platform_not_available only appears at Premium on iOS, where device attestation is requested but iOS has no equivalent API to fulfill it.


Premium tier — Android

Premium adds three fields to the Standard evidence: device_attestation, liveness_verification, and coercion_check.


Premium tier — iOS

On iOS at Premium, device_attestation is always null. iOS has no Play Integrity equivalent available to third-party apps. The Premium delta on iOS comes entirely from the biometric layer — liveness_verification and coercion_check work identically on both platforms.


Failed session

FAILED means the verification couldn't run to a trustworthy verdict. It is never used for credential-level outcomes — a revoked, expired, or untrusted-issuer credential returns COMPLETED with those facts set inside credentials[].result.


Evidence field reference

Core (all tiers)

Field
Description

timestamp

When Blerify completed the verification (ISO-8601 UTC). Use this as your audit timestamp.

Network signals (Standard+)

Field
Description

wallet_ip

IP address of the user's phone as Blerify observed it when the wallet connected.

client_reported_ip

IP your frontend sent in client_context.session_ip. Self-declared — Blerify preserves it as received.

client_session_id

The identifier you passed in client_context.session_id, echoed back for audit correlation.

ip_match

Whether the two IPs agree within Blerify's tolerance rules. false is a relay-attack signal; it isn't conclusive on its own — VPN, NAT, and mobile carriers frequently route through different IPs.

geolocation

IP-derived approximate location for both sides. country_match is the useful signal; city-level accuracy varies by ISP. source is always "ip_geolocation" today.

Device metadata (Standard+)

Field
Description

device_metadata.device_model

Phone model.

device_metadata.os_version

OS and version.

device_metadata.attested

true on Android — values come from the hardware-signed attestation certificate. false on iOS — Apple's attestation format doesn't include device metadata, so the wallet self-reports.

device_metadata.attestation_source

android_key_attestation on Android; wallet_self_reported on iOS.

Key attestation (Standard+)

Proves the credential's signing key lives in secure hardware and requires biometric authentication to use. For a deeper explanation of the Android/iOS differences, see Platform Attestation.

Field
Description

key_attestation.verified

Blerify validated the attestation chain against the platform's root trust.

key_attestation.hardware_backed

The signing key lives in the device's secure hardware (TEE, StrongBox, or Secure Enclave).

key_attestation.security_level

STRONG_BOX or TRUSTED_ENVIRONMENT on Android; SECURE_ENCLAVE on iOS.

key_attestation.security_level_attested

true on Android — hardware-signed. false on iOS — self-reported, because Apple provides no API for third-party apps to cryptographically prove Secure Enclave key residency.

key_attestation.platform

android or ios.

key_attestation.format

android_key_attestation or app_attest.

key_biometric_binding.biometric_required

The key requires biometric or device PIN to sign. Prevents the key from being used without user interaction.

key_biometric_binding.auth_type

Authentication methods bound to the key: FINGERPRINT, FACE, or DEVICE_CREDENTIAL (PIN/pattern/password, on devices without a strong biometric sensor).

key_biometric_binding.auth_timeout_seconds

0 means biometric is required for every signature, no caching.

key_biometric_binding.invalidated_on_enrollment_change

true means the key is destroyed if someone adds a new biometric to the device.

key_biometric_binding.attested

true on Android — the access-control properties above are hardware-signed. false on iOS — Apple's attestation format carries no key access-control flags.

Document render (Standard+)

A wallet-generated image of the credential showing exactly what the user disclosed. Disclosed fields are legible; every undisclosed field is blurred. Delivered as one image per document face. Use this for manual review, dispute resolution, and customer service — it's a human-viewable complement to the machine-readable credentials[].data, not a substitute for it.

Field
Description

document_render.format

MIME type of the images, e.g. image/jpeg.

document_render.redaction

How undisclosed fields are obscured. blur today.

document_render.images[]

One entry per document side. Each has side (front or back) and data (base64-encoded image). Single-sided documents return one entry.

Device attestation (Premium only)

Proves the wallet app is genuine and the device is unmodified — not an emulator, not rooted, not a sideloaded binary.

On iOS, device_attestation is always null with device_attestation_reason: "platform_not_available". iOS has no Play Integrity equivalent available to third-party apps. This is a structural platform limitation, not a session failure.

Field
Description

device_attestation.verified

The attestation token was successfully validated.

device_attestation.platform

android (Play Integrity).

device_attestation.device_integrity

Android only. true means the device passes the integrity check — no unlocked bootloader, no root, no emulator.

device_attestation.app_integrity

The wallet binary is recognized as a genuine, unmodified build.

device_attestation.nonce_match

Confirms the attestation token was generated for this specific session's nonce. false indicates a possible replay attempt.

Liveness verification (Premium only)

The wallet captures a liveness challenge during the session. Blerify compares the result against the portrait embedded in the credential server-side. You never receive raw biometric data — only the result and a confidence score.

Field
Description

liveness_verification.verified

The face match passed and the anti-spoofing check found no attack.

liveness_verification.match_confidence

0–1 confidence score from the face comparison.

liveness_verification.spoof_detected

true if the anti-spoofing check flagged a photo, video, 3D mask, or deepfake.

liveness_verification.challenge_completed

The user completed the liveness challenge.

Coercion check (Premium only)

Silent gaze-tracking and micro-expression analysis on the liveness frames. Reports whether the user showed signs of distress. Invisible to both the user and any potential coercer — they see only the standard liveness challenge.

Blerify never blocks a flow on this signal. It reports the fact; you decide how to act.

Field
Values
Description

coercion_check.status

CLEAR, GAZE_ANOMALY, EXPRESSION_ANOMALY

GAZE_ANOMALY: persistent off-camera gaze toward a fixed point. EXPRESSION_ANOMALY: involuntary stress or fear expressions detected.

coercion_check.method

"facial"

Always "facial" in this version.

coercion_check.alert

boolean

Convenience flag: false iff status == "CLEAR".

Signed evidence (Standard+)

signed_evidence is a JWT containing the full evidence object, signed by Blerify. Store it as your tamper-proof audit record. Blerify does not retain it after the session expires — you are the sole custodian.

To validate it later: verify the JWT signature against Blerify's public key at the JWKS endpoint described at https://dev.blerify.com.

_reason fields

Every optional evidence field has a companion _reason field. When the main field is populated, _reason is null. When it's null, _reason explains why.

Value
Meaning
How to treat it

null

The field is populated — nothing to explain

N/A

not_requested

Your template didn't configure this tier or feature

Expected absence

platform_not_available

The platform has no API for this (e.g., iOS device_attestation at Premium)

Structural limit — accept, or apply a platform-specific policy

wallet_not_supported

The wallet doesn't implement Blerify extensions

Capability gap — accept Basic or require a compatible wallet

user_declined

The user denied a required permission (camera for liveness, biometric prompt)

Active refusal — a different risk signal from a platform limitation

failed

The check was attempted but rejected (invalid token, spoof detected)

Treat as a strong fraud signal

attestation_not_supported

Older Android device — key is hardware-backed but cryptographic attestation is unavailable

Likely a legitimate older device — accept or downgrade per your policy

The distinction between platform_not_available and user_declined carries meaningfully different risk implications. Handle them separately in your policy logic.


Polling strategy

Poll every 1–2 seconds. Stop on COMPLETED, FAILED, or HTTP 410.

Typical session duration: 5–15 seconds for Basic and Standard; 15–30 seconds for Premium (the liveness capture adds time).

If you haven't received a terminal status after your UX timeout (2 minutes for same-device flows, 5 minutes for cross-device), stop polling and offer the user a "try again" button that starts a fresh session. The same transaction_id cannot be reused.


Next steps

Continue to Verify a Credential — the complete session lifecycle, how to read every evidence field, and exhaustive error handling including negative credential outcomes.

See also: Platform Attestation · Assurance Tiers · Biometric Binding · Full OpenAPI spec

Last updated