Developer docs

The age verification stats API

The numbers on your Stats page, from code. How many checks a wallet answered, why attempts were refused, where people dropped off and how the pass rate moved, for your whole organisation or one project. The same five reads answer an AI assistant through the MCP server.

Signup is open and a test project works as soon as you create one. Create an account to make a key.

Which key

Every read takes a key as a Bearer token, and the key decides whose checks it reads.

The five reads

Each is a GET under https://api.tessio.eu/v1/stats. A read your plan does not include answers 403 with plan_feature_unavailable and the feature that would admit it.

Route What it answers Plan
/v1/stats/summary Checks, pass rate, fail rate, no answer rate and time to result for a period, beside the period of equal length before it. Every plan
/v1/stats/timeseries One metric per bucket: checks, passed, under_age, failed, expired or pass_rate. Buckets are hours for two days or less, then days, weeks or months as the period grows. Starter and up
/v1/stats/failures Refused presentations by reason category, such as the person declining, an issuer that is not trusted or an expired credential. Optionally split by method. Growth and up
/v1/stats/funnel How far the checks got: created, a wallet answered, completed. For EU Digital Identity Wallet requests, also how many were fetched by a wallet. Growth and up
/v1/stats/breakdown The checks grouped by one column, each group with its outcomes: project, api_key, method, transport, format or mode. Growth and up

Parameters

An example

curl "https://api.tessio.eu/v1/stats/summary?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer tk_your_stats_key"

Rates are fractions of the checks that ended, so a check still open counts in neither the pass rate nor the fail rate. The figures here are an example.

{
  "mode": "live",
  "current": {
    "from": "2026-09-01",
    "to": "2026-09-30",
    "outcomes": { "total": 1240, "passed": 951, "underAge": 142, "failed": 0,
                  "expired": 147, "pending": 0, "noAnswer": 120 },
    "passRate": 0.7669,
    "failRate": 0.1145,
    "noAnswerRate": 0.0968,
    "p50Seconds": 22,
    "p95Seconds": 40
  },
  "previous": { "from": "2026-08-02", "to": "2026-08-31", "...": "the same fields" },
  "retention": { "recordsFrom": null, "eventsFrom": null }
}

The time series takes the same period and answers one value per bucket:

curl "https://api.tessio.eu/v1/stats/timeseries?metric=pass_rate&from=2026-09-01" \
  -H "Authorization: Bearer tk_your_stats_key"

When a period reaches past what your plan keeps

Check records and check timelines are deleted when your plan's period passes, as the privacy policy sets out. Every answer carries retention: when recordsFrom or eventsFrom is a date, the days before it count only what is still held, so a drop there is the purge and not your traffic. Failures and the funnel read the timelines, which are kept for less time than the checks.

Errors and limits

Rather ask than query?

Connect an AI assistant to the MCP server with the same key and ask it how your checks did last month.

The MCP server →