Covered verification

Every verification a paid Developer or Scale account runs is put in front of a gate. The ones that qualify get a certificate carrying a contractual warranty, and every result tells you in the response whether it qualified. There is nothing to enable and no request field to set.

The caps and the condition are on /warranty. This page is the wire format.

The coverage block #

Present on every verification result: the completed /verify/status/{task_id} body, GET /verifications/{id}, and the verification.completed webhook. /verify/batch and /select fan out to tasks whose results carry it through those same two.

It describes the pair (your account, this analysis), not the analysis alone. Two accounts can hold two certificates over one cached verdict, so the block you see is never the block another customer sees. It is absent for a reader who does not own the verification.

{
  "verification_id": "a1b2c3d4",
  "verdict": "True",
  "coverage": {
    "status": "covered",
    "reasons": [],
    "certificate_id": "9f2c...",
    "certificate_url": "https://lenz.io/certificate/9f2c...",
    "as_of": "2026-09-03T10:00:00+00:00",
    "currency": "EUR",
    "cap": 10000,
    "aggregate": 500000,
    "terms_version": "v1"
  }
}
FieldMeaning
statuscovered · pending_timestamp · uncovered
reasonsWhy not, when uncovered. Empty otherwise. Closed vocabulary, below.
certificate_id128-bit hex, or null. Rotatable — treat it as a handle, not an identifier you store forever.
certificate_urlThe public certificate page. Anyone with the link can read it, which is the point: it is what you link from a published piece.
as_ofThe date the verdict is warranted as of — the analysis time, not the issue time.
currencyISO 4217. The currency the cited terms_version is written in — read it, do not assume EUR.
cap / aggregateThe caps that applied when this certificate was issued, in major units (10000 means ten thousand, not a hundred). Not cents: these are contract figures, not amounts a payment processor charges.
terms_versionResolves at /terms/<version>, permanently.

pending_timestamp, and when to publish #

A certificate is signed and given a qualified timestamp by an authority on the EU Trusted List. That takes a few minutes, and until it lands the status is pending_timestamp.

This matters if you publish automatically. Cover requires the timestamp to precede what you publish or send, and verification.completed fires while the qualified timestamp is still pending. Key on the certificate.timestamped webhook instead, or poll until status is covered. Publishing on the earlier event builds a back catalogue that falls outside your own warranty, and nothing will tell you.

{
  "event": "certificate.timestamped",
  "verification_id": "a1b2c3d4",
  "task_id": "…",
  "status": "completed",
  "result": null,
  "coverage": { "status": "covered", "certificate_url": "…", … },
  "attempt": 1,
  "delivered_at": "2026-09-03T10:04:11+00:00"
}
The block rides coverage, not result. This event says a timestamp landed, not that a verdict was produced, so result is null.

Why a verdict is uncovered #

Six values, deliberately fewer than the rules behind them. Two are actionable, two are rules about the analysis itself, and two are statements about Lenz rather than about your claim.

ReasonWhat it meansWhat to do
planNot on a paid Developer or Scale plan.Upgrade. Nothing else about the verdict was the problem.
depthYou sent depth: "low".Send standard.
verdictThe verdict is not True or False.Nothing. Mostly True, Mixed and Mostly False are never warranted — a hedged verdict is one Lenz is telling you not to lean on.
qualityThe analysis did not meet the bar: low panel confidence, too few source domains, or a degraded run.Nothing. Re-running the same claim will usually reach the same place.
withdrawnLenz has retracted this verdict.Stop relying on it going forward. The warranty still applies to anything you published or sent before the notice.
issue_failedThe verdict qualified and Lenz failed to issue the record.Nothing, and contact us — this is ours, not yours.

More than one can appear. The order is stable, and plan comes first when it fired — every other rule is still evaluated on a Free or Plus account, so a ["plan"] on its own means the verdict would otherwise have qualified.

Fetching the certificate #

Two ways to the same bytes. The API endpoint is key-authenticated and resolves by (verification, account); the public link is what you put in an article.

GET /api/v1/verifications/{verification_id}/certificate
Authorization: Bearer lenz_…

GET https://lenz.io/certificate/{certificate_id}.json

The document carries the signed payload, the record hash, the signature and both anchors. Nothing in it names your account — it is designed to be published.

Checking it without us #

A warranty you can only check by asking the warrantor is not much of a warranty. Download the record and run the checker against it:

pip install cryptography rfc8785 rfc3161-client
curl -O https://lenz.io/verify_certificate.py
curl -O https://lenz.io/.well-known/lenz-certificate-keys.json

python verify_certificate.py certificate.json \
    --keys lenz-certificate-keys.json \
    --tsa-roots qtsp-roots.pem

qtsp-roots.pem is the timestamping authority’s root certificate, from the EU Trusted List.

It recomputes the record hash under RFC 8785, checks the ECDSA P-256 signature against the published keys, and checks the qualified timestamp against roots you chose to trust. Lenz is not in the loop. Exit codes: 0 the record checks out, 1 the document does not match what Lenz signed, 3 a required check could not run — which is not a pass.

Withdrawal cannot be checked offline. withdrawn_at sits outside the signed payload by design, so that retracting a verdict cannot invalidate a record someone may already be entitled to claim on — which also means it can be stripped from a downloaded file. Check the live certificate page for current status.