AUDRIEDOCS
ReferenceVerifications

List your organisation's verifications

GET
/v1/verifications

Returns your organisation's verification log, newest first.

Scoped to the calling key's organisation and environment: a test key never sees live verifications. Anonymous verifications are attributable to no one and are not logged, so a credential is required.

Rows carry the verdict as it was recorded. They carry no matches: to get matches for a hash, POST it, which is the operation that computes them.

Cursor-paginated. See Pagination.

Errors

Every query parameter is rejected rather than ignored or clamped. An unparseable from, an out-of-range limit or a cursor that does not decode all answer 400 with the offending field in error.details — a silently different page is worse than a refusal on a log you reconcile against.

Authorization

apiKey
headerAuthorizationBearer <aud_{live|test}_<keyId:16><secret:32><crc:6>>

Authorization: Bearer aud_live_…

Optional on POST /v1/verifications, which answers without any credential. Required to list or read verifications back.

Send exactly one Authorization header. A credential that is present and does not work is refused rather than downgraded to the anonymous tier.

Issue a key in the portal under Settings → Organisation → API credentials; it is shown once, at creation.

Three scopes exist. verifications:read covers every verification operation and is on every key. records:write covers POST /v1/records, and records:revoke covers POST /v1/records/{recordId}/revoke. Both are granted to a live or a test credential alike, on an organisation that holds the organisation-issued authentication add-on at the moment the key is minted; records:revoke is granted only when you ask for it. Scopes are fixed then and never widen afterwards, because a credential's recorded scopes are what an auditor reads to know what that key could do on a given date. A test key's records:write and records:revoke reach sandbox records only: signed with the organisation's sandbox key, anchored on Hedera's test network, and never a live record. A key minted without a scope never gains it; mint a new one. Buying the add-on later means minting a new key too; the old one keeps verifying.

Scopes, rotation, revocation, the live and test prefixes and what to do about a leaked key: https://audrie.io/docs/authentication

Query Parameters

verdict?string

Return only verifications that recorded this verdict.

Value in"verified""verified_org""revoked""signature_invalid""pending_anchor""no_record""sandbox"
originalDocHash?string

Return only verifications of this document hash.

Match^sha256:[0-9a-f]{64}$
from?string

Return only verifications at or after this instant. RFC 3339 timestamp.

Formatdate-time
to?string

Return only verifications at or before this instant. RFC 3339 timestamp.

Formatdate-time
limit?integer

Rows per page. An integer between 1 and 100. Defaults to 25. A value outside that range is refused, not clamped.

Range1 <= value <= 100
Default25
after?string

Opaque cursor from a previous page's pagination.nextCursor. Pass it back verbatim; do not construct or modify one.

Response Body

A page of verifications, newest first, with a cursor for the next page in pagination.

application/json
  1. response

One page of the reconciliation log.

data*array<>

The page's rows, newest first.

pagination*

Where you are in the log. See Pagination.

Example Request
curl -X GET 'https://api.audrie.io/v1/verifications?from=2026-08-01T00%3A00%3A00Z&to=2026-08-31T23%3A59%3A59Z'
Example Response
{  "data": [    {      "verificationId": "72d12273-0723-448e-a9f6-f7957128efa5",      "verdict": "verified",      "assuranceLevel": "audrie.registry.v1",      "mode": "partial",      "environment": "live",      "originalDocHash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",      "verifiedAt": "2019-08-24T14:15:22Z",      "reference": "string"    }  ],  "pagination": {    "nextCursor": "string",    "hasMore": true  }}

Pagination

How to page through the verification log with cursors.

Verify a document hash POST

Submits a document hash and returns a **verdict** — Audrie's answer to "does this document check out, and what do you know about it". Both cryptographic checks are recomputed server-side for your request. Nothing in the request body is trusted beyond the identifiers used to look records up: a client that lies about a hash can only produce a verdict that fails its own verification. Works with or without a credential. See [Disclosure tiers](https://audrie.io/docs/disclosure-tiers). ## What you get back `verdict` is a single value you can branch on. It is derived from the four checks in `matches[].checksPerformed` plus revocation state, and it fails closed: anything Audrie cannot fully substantiate is never reported as `verified`. Every determinate outcome — `no_record` included — returns `200` with a `verdict`. A hash Audrie holds no record of is information, not a failed request. `mode` says what the verdict was computed over. Read the field rather than hardcoding a value. ## Two modes `partial` is the default: omit `mode`, or send `partial` explicitly, and Audrie looks up every anchored record registered against `originalDocHash`. This is the mode described above — one hash, zero or more records. `full` re-verifies ONE exact certified PDF you already hold. Send `mode: full` together with `recordId` (the record id embedded in that PDF) and `signedDocHash` (prefixed `sha256:`). `signedDocHash` is the SHA-256 of the exact bytes you are holding with each signer's signature value replaced by the same number of ASCII `0` characters. A signer's signature value is the hex text of `signers.<alg>.sig` in the embedded certificate, without its quotes; Audrie's seal signature (`serverSeals.<alg>.sig`) is hashed unchanged. It is not the `sha256sum` of the file: a signer's signature cannot cover a hash that includes itself. Audrie's verifier computes it when it reads a certified PDF; `sha256sum` cannot, because each signer's signature value has to be located before it is blanked. `originalDocHash` stays the plain `sha256sum` of the original file. `recordId` and `signedDocHash` are required with `full` and rejected with `partial` — a request naming both a mode and fields that mode does not use is refused with `400`, not silently corrected. A `full` response carries at most one match — the exact record you named — and `matches[]` is empty for every collapsed outcome below. **A `recordId` that does not exist, one that names a document Audrie did not embed a certificate in, and a `recordId` whose hashes do not match the ones you sent all answer the same way**: `200` with `verdict: no_record` and an empty `matches[]`, identical in every field but `verifiedAt`, which records when the request was answered and carries no information about which cause applied. Response timing is not part of that guarantee either. `recordId` is not a secret — it appears in the Hedera messages an authenticated document anchors to — so treating any of these three causes differently in the response body would let anyone holding a `recordId` alone learn whether it exists and what kind of record it is, with no proof of possessing the document at all. Do not build logic that expects to tell these three apart from the response; a well-formed `recordId` reused across requests returns a body identical in every field but `verifiedAt` (which records when the request was answered and carries no information about which cause applied), regardless of which cause applies. A malformed `recordId` — one that is not a UUID — is the one exception, and answers `400`: it is a syntax error you can detect yourself before sending the request, not a fact about whether a record exists. `pending_anchor` is answered honestly. It is reachable only once `originalDocHash` and `signedDocHash` are both confirmed against the named record, so by the time you see it you already hold a document whose bytes (apart from each signer's signature value) and record id, we have just told you, are correct — the reply adds nothing an attacker without that document could not already infer. ## One hash, several records `partial` mode does not let you pick the record. One hash can legitimately have several records — a re-signing, or several parties authenticating the same document — so the response returns every anchored match, ordered by anchor time, earliest first. The timeline is the answer. `verdict` asserts that some valid anchored authentication exists, not that a particular party made it. Read each match's own `status`, `signerStanding` and `signerHint` before attributing a document to anyone. ## Signer standing is live `signerStanding` is recomputed at request time. A certificate issued earlier carries a frozen snapshot; this endpoint does not. The two can legitimately disagree, and the live value is the current answer. ## Errors A `400` here means `originalDocHash` or `signedDocHash` is not `sha256:` followed by 64 lowercase hex characters, `reference` is longer than 128 characters or contains a control character, `mode` is not `partial` or `full`, `recordId`/`signedDocHash` are sent with `partial` or missing with `full`, `recordId` is not a UUID, or the body is not a JSON object. `error.details` names the field.