DocumentationSign in

Identity documents

A document policy, each document named on the screen, and a proof block.

Nothing moves without a document policy

The identity_documents scope is necessary and not sufficient. Documents are released only when the consent in force carries a document policy, and only the documents that policy names.

The policy lives in the application's allowedDataCategories, as marker strings:

document_policy:v1
The marker that switches the policy on. Without it, no document is ever released — every document call answers with nothing.
document:<country>:<type>
A document selection: an issuing country paired with a document type or a provider document code. Only matching documents may be released.
document_field:<scope>
One per document field scope you may receive — for example document_field:document_expiry_date. A field scope granted but not in the policy releases nothing.

A document also has to qualify on the person's side: verified, active, enabled for identity sharing, not deleted — and listed on the consent screen the person approved.

Listing what you may see

List identity documents
curl -s "$UNIFYID_API/v1/userinfo/identity-documents" \
  -H "authorization: Bearer $ACCESS_TOKEN"

Each entry in data.documents

documentReference
An opaque reference, derived per client id. Use it on the detail endpoint. It is not the document's identifier and it is not portable between applications.
displayName
Always present — the human name of the document.
documentType, documentCode
Present only with document_type granted and in the policy.
issuingCountry
Present only with document_issuing_country.
validityStatus
Present only with document_validity.
verified
Present only with document_verified.

Without identity_documents the endpoint refuses. Unauthenticated, it answers the same recorded 401 shape as userinfo: { "error": { "code": "invalid_token", … } }.

One document, and its proof

GET /v1/userinfo/identity-documents/{documentReference} returns the same per-field rules plus two things the list does not carry: expiryDate, verifiedAt, maskedDocumentNumber — each gated on its own scope — and a proof block.

Fetch one document
curl -s \
  "$UNIFYID_API/v1/userinfo/identity-documents/$DOCUMENT_REFERENCE" \
  -H "authorization: Bearer $ACCESS_TOKEN"

The proof block

typeUnifyIDAttestation
What kind of proof this is.
version2
The proof format. In version 2, attestationId and proofHash are derived for your application alone.
attestationId
att_ and 24 characters: your application's reference to the attestation this document's verification produced. The same every time your application reads this document, and different for every other application, so two applications cannot match a person by it.
schemaVersion
The schema the attestation was issued under.
status
The attestation's status.
proofHash
hmac-sha256: and 64 hex characters, bound to your application, the sub it knows the person by, and the attestation. Stable for your application, different for any other. It is a keyed value: your application cannot recompute or check it. Record it with your decision, and quote it with attestationId to UnifyID support, which can recompute both and confirm what they refer to.

proof is null when no attestation exists for the document. document_verified_at is the only verification timestamp the platform exposes anywhere, and it is delivered here as verifiedAt — the attestation's issue time.

maskedDocumentNumber is the masked form only, rendered as bullets and the last four characters. There is no scope in the catalogue that releases a full document number.

Per-document naming

This is why the consent screen names documents individually rather than saying "your identity documents". A person with a passport and a driving licence sees both by name and approves a release that mentions each. The approved ids are pinned to the interaction, so the resulting consent cannot stretch to a document that was not on the screen.

What this does not do

  • A document policy is a ceiling too. It bounds what could be released; the person still decides, and only what their screen listed is in force.
  • A documentReference is per client id. Do not store it as a global document identifier, and do not expect it to match anything another application sees.
  • Every document read is recorded as a document disclosure, in the transaction that reads it, naming the fields and the documents involved.

Verified against API commit 234dba22 on 2026-10-18. The routes, scopes, checklist, events and limits on these pages are read from that export, not typed.