Skip to content

Tamga Verify API ​

Ask a person for credentials from your website or service and receive the verified result.

Real network
https://verify.tamga.network
Sandbox (test network)
https://verify.sandbox.tamga.network
Authentication
Bearer (tamga-rp+jwt) or X-Tamga-Status-Token header
Definition
OpenAPI 3.1 file · Guide

Tamga Verify is the hosted verifier of Tamga Network (ADR-0017). The browser part is @tamga-network/verifier/web; server-side verification without the hosted verifier is @tamga-network/verifier.

Flow. Your server opens a presentation for a policy (which credential and which fields) → your page shows the QR code or the "Open in your wallet" link and polls the status with the status_token → when the status is DONE, your server reads the result and, once, the approved values.

Privacy. The result and values go only to the relying party that opened the presentation. Values can be read once and are deleted at the latest 5 minutes after the result. The browser sees only the status, never values.

Outcomes. ACCEPTED, REJECTED (with the failing step) or INDETERMINATE ("could not check right now" — never treat it as invalid).

Endpoints ​

EndpointDescription
POST /presentationsOpen a presentation request
GET /presentations/{id}Get the status or the verification result
GET /presentations/{id}/claimsRead the approved values (once)
GET /presentations/{id}/qr.pngGet the QR code image
GET /policiesList the policies
GET /tamga-verifier.jsLoad the page kit

Authentication ​

Bearer (tamga-rp+jwt)

Authorization: Bearer <RP assertion> — a short-lived JWS (typ: tamga-rp+jwt, ES256) signed with the private key of your access certificate registered in the Tamga trust list. Header x5c carries the certificate; payload { iss: <your client_id>, aud: "https://verify.tamga.network", iat, exp ≤ iat + 60, jti }. No shared secret; jti is single-use. Helper: createRpAssertion() in @tamga-network/verifier.

X-Tamga-Status-Token header

Status-only token for the browser (also accepted as ?st=). Shows the status, never values.

Presentations ​

Ask for credentials and read the result.

Open a presentation request ​

POST/presentations

Creates a signed OpenID4VP request for the policy. The policy must be within your registered scope (otherwise 400 policy_exceeds_scope). Send Accept: application/json to get JSON; without it the verifier redirects to its own waiting page.

Authentication Bearer (tamga-rp+jwt)

Parameters ​

NameInTypeDescription
Accept requiredheaderstringAsk for a JSON answer. Always application/json

Request body ​

application/json

FieldTypeDescription
policy_id requiredstringA policy from GET /policies, e.g. site-signup, age-over-18-mdoc.
dc_api_originstring (uri)Optional. The page origin (https://…, no path) when the presentation is answered through the browser's Digital Credentials API. The request is then bound to this origin (expected_origins).

Responses ​

StatusDescription
200Presentation opened. Returns StartedPresentation
400unknown_policy, policy_exceeds_scope (the policy asks for more than your registered scope; detail lists what) or invalid_request (invalid dc_api_origin). Returns Error
401invalid_rp_assertion — the RP assertion is expired, reused or not signed by a registered, active relying party. Returns Error
429rate_limited — too many requests. Wait the number of seconds in Retry-After and try again. The client address is not stored or logged; the wallet-facing /vp/response and the gate /terminal/verify endpoints have the same protection. Returns Error Headers: Retry-After

Example ​

bash
curl -X POST "https://verify.tamga.network/presentations" \
  -H "Authorization: Bearer $RP_ASSERTION" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"policy_id":"site-signup"}'
json
{
  "presentation_id": "prs_Q2x0dVJmNkp3",
  "request_uri": "https://verify.tamga.network/vp/req/prs_Q2x0dVJmNkp3",
  "qr_payload": "openid4vp://?client_id=x509_hash%3A…&request_uri=https%3A%2F%2Fverify.tamga.network%2Fvp%2Freq%2Fprs_Q2x0dVJmNkp3",
  "expires_at": "2026-10-09T12:05:00Z",
  "status_token": "9VdB0qXr7mYk2LwPq1sZ3A"
}

Get the status or the verification result ​

GET/presentations/{id}

With your RP assertion: the full verification result ({"state": "PENDING"} until the wallet answers). With only the status token (?st= or X-Tamga-Status-Token, no Authorization): the status for the browser, without values or names.

Authentication Bearer (tamga-rp+jwt) or X-Tamga-Status-Token header

Parameters ​

NameInTypeDescription
id requiredpathstringThe presentation_id from createPresentation.
stquerystringStatus token from createPresentation, for the browser.

Responses ​

StatusDescription
200Status (PENDING), the browser status, or the full result. Returns VerificationResult or BrowserStatus or Pending
401invalid_rp_assertion. Returns Error
404not_found — unknown presentation, or not yours (existence is not revealed). Returns Error

Example ​

bash
curl "https://verify.tamga.network/presentations/prs_Q2x0dVJmNkp3" \
  -H "Authorization: Bearer $RP_ASSERTION"
json
{
  "verification_id": "vrf_Q2x0dVJmNkp3",
  "outcome": "ACCEPTED",
  "failed_step": "…",
  "failed_reason": "…",
  "indeterminate_reason": "SCHEMA_UNREACHABLE",
  "spec_version": "…",
  "sdk_version": "…",
  "checks_performed": [
    "T0",
    "A1",
    "A2",
    "B1",
    "C1",
    "D1",
    "E1"
  ],
  "checks_skipped": [],
  "issuer": {
    "issuer_id": "…",
    "state_code": "TR",
    "legal_name": "…",
    "category": "…",
    "assurance": "…",
    "class": "PUB"
  },
  "schema": {
    "schema_id": "…",
    "vct": "urn:tamga:id:IdentityAttestation:1",
    "status": "…"
  },
  "disclosed_claims": [
    "given_name",
    "family_name"
  ],
  "status": {
    "value": "VALID",
    "list_version": 0,
    "token_age_sec": 0,
    "reason": "…"
  },
  "freshness": {
    "trust_source": "list",
    "trust_version": 0,
    "trust_age_sec": 0
  },
  "evaluated_at": "2026-10-09T12:00:00Z"
}

Read the approved values (once) ​

GET/presentations/{id}/claims

Returns the values the person approved. Available once, at most 5 minutes after the result.

Authentication Bearer (tamga-rp+jwt)

Parameters ​

NameInTypeDescription
id requiredpathstringThe presentation_id from createPresentation.

Responses ​

StatusDescription
200Values, or null while the presentation is pending.
404not_found — unknown presentation, or not yours.
410claims_gone — values already read or expired.

Example ​

bash
curl "https://verify.tamga.network/presentations/prs_Q2x0dVJmNkp3/claims" \
  -H "Authorization: Bearer $RP_ASSERTION"
json
{
  "claims": {
    "given_name": "Ayşe",
    "family_name": "Yılmaz",
    "pseudonym": "ps_7Qm…"
  }
}

Get the QR code image ​

GET/presentations/{id}/qr.png

A PNG of the qr_payload link (440 px), ready to put in an <img>.

Authentication None — public

Parameters ​

NameInTypeDescription
id requiredpathstringThe presentation_id from createPresentation.

Responses ​

StatusDescription
200PNG image.
404Unknown presentation.

Example ​

bash
curl "https://verify.tamga.network/presentations/prs_Q2x0dVJmNkp3/qr.png"

Policies ​

What can be asked.

List the policies ​

GET/policies

The policies this verifier can ask for. No personal data.

Authentication None — public

Responses ​

StatusDescription
200Policy summaries. Returns PolicySummary

Example ​

bash
curl "https://verify.tamga.network/policies"
json
[
  {
    "policy_id": "age-over-18-zk",
    "purpose": "Over-18 check with a zero-knowledge proof — yes/no only, nothing else",
    "purpose_localized": {
      "en-US": "Over-18 check with a zero-knowledge proof — yes/no only, nothing else",
      "tr-TR": "Sıfır bilgi ispatıyla 18 yaş üstü doğrulaması — yalnızca evet/hayır, başka hiçbir şey"
    },
    "vct_values": [
      "urn:tamga:id:IdentityAttestation:1"
    ],
    "claims": [
      "age_over_18"
    ],
    "proximity": false,
    "format": "mso_mdoc_zk"
  }
]

Page kit ​

Files for the browser side.

Load the page kit ​

GET/tamga-verifier.js

The browser bundle of @tamga-network/verifier/web (QR code, "Open in your wallet" button, status polling, Digital Credentials API). Cached for 5 minutes.

Authentication None — public

Responses ​

StatusDescription
200JavaScript.

Example ​

bash
curl "https://verify.tamga.network/tamga-verifier.js"

Objects ​

StartedPresentation ​

FieldTypeDescription
presentation_id requiredstring
request_uri requiredstring (uri)Where the wallet fetches the signed request.
qr_payload requiredstringopenid4vp://… link — show as a QR code on desktop, as a button on phones.
expires_at requiredstring (date-time)
status_token requiredstringGive only this to the browser.
dc_api_requestobjectPresent when dc_api_origin was sent — input for navigator.credentials.get({ digital }).
dc_api_request.protocolstringAlways openid4vp-v1-signed
dc_api_request.dataobject
dc_api_request.data.requeststringSigned request object (JWS).

Pending ​

The wallet has not answered yet.

FieldTypeDescription
statestringAlways PENDING

BrowserStatus ​

What the browser sees with the status token — no values, no names.

FieldTypeDescription
statestringOne of: PENDING · DONE
outcomestringOne of: ACCEPTED · REJECTED · INDETERMINATE
failed_reasonstring | null
indeterminate_reasonstring | null

VerificationResult ​

The full result, for the relying party that opened the presentation.

FieldTypeDescription
verification_idstring
outcomestringINDETERMINATE means "could not check right now" — never treat it as invalid. One of: ACCEPTED · REJECTED · INDETERMINATE
failed_stepstring | nullThe first failing step of the verification pipeline (T0, A1…E3, P1) when REJECTED.
failed_reasonstring | null
indeterminate_reasonstring | nullOne of: SCHEMA_UNREACHABLE · STATUS_UNREACHABLE · STATUS_STALE · CHAIN_UNREACHABLE · INDEXER_STALE · SDK_VERSION_MISMATCH
spec_versionstringVerification specification version.
sdk_versionstringVerifier package version.
checks_performedarray<string>
checks_skippedarray<string>
issuerobject | nullThe institution that issued the credential, from the trust list.
issuer.issuer_idstring
issuer.state_codestring
issuer.legal_namestring
issuer.categorystring
issuer.assurancestring
issuer.classstringOne of: PUB · QUALIFIED · EAA
schemaobject | null
schema.schema_idstring
schema.vctstring
schema.statusstring
disclosed_claimsarray<string>Names of the disclosed fields (values via /claims).
statusobjectRevocation status of the credential.
status.valuestringOne of: VALID · INVALID · SUSPENDED · NOT_APPLICABLE · UNKNOWN
status.list_versioninteger | null
status.token_age_secinteger | null
status.reasonstring | nullWhy the status has this value, when it is not self-explanatory — e.g. NOT_APPLICABLE for a zero-knowledge presentation, which does not reveal the revocation index (ADR-0032; the credential is short-lived). The policy decides whether this is accepted; if not, the outcome is INDETERMINATE. Contains no personal data.
freshnessobjectHow fresh the trust data used was.
freshness.trust_sourcestringOne of: list · chain
freshness.trust_versioninteger
freshness.trust_age_secinteger
evaluated_atstring (date-time)

PolicySummary ​

FieldTypeDescription
policy_idstring
purposestringPurpose in English.
purpose_localizedobjectPurpose by language tag (en-US, tr-TR).
vct_valuesarray<string>
claimsarray<string>Requested fields.
proximitybooleanThe policy also issues a gate pass.
formatstringmso_mdoc_zk = zero-knowledge proof over an mdoc (ADR-0032). A wallet that cannot produce the proof uses the classic mso_mdoc policy instead (e.g. age-over-18-mdoc). One of: dc+sd-jwt · mso_mdoc · mso_mdoc_zk

Error ​

FieldTypeDescription
errorstring
error_descriptionstring
detailarray<string>With policy_exceeds_scope, what goes beyond your scope.

Import the machine-readable definition into any OpenAPI tool to generate a client or send test requests: hosted-verifier-api.openapi.yaml.