DPP Agent docs

Validate — system conformance and content coverage

The validate endpoint checks any passport address, on DPP Agent or anywhere else: how the passport is served (identifier, content negotiation, linkset, passport header, back-up declaration) and how much of the ESPR fields its data covers. It is the engine behind dppagent.com/validate.

It is CORS-open and works without a key. With an API key the answer includes field-level detail and the anonymous limits do not apply.

Check a passport address

GET /api/v1/validate?url=https://example.dpp.dppagent.com/01/09506000134352

or

POST /api/v1/validate
Content-Type: application/json

{ "url": "https://example.dpp.dppagent.com/01/09506000134352" }

The endpoint fetches the address as JSON, as HTML and as a linkset, and scores what it gets back. Add include_document=1 (GET) or "include_document": true (POST) to receive the passport data it read.

Response

{
  "url": "https://example.dpp.dppagent.com/01/09506000134352",
  "fetchedAt": "2026-10-12T15:59:50.546Z",
  "dataAvailable": true,
  "grade": "B",
  "overallScore": 86,
  "gradeBasis": "content",
  "verdict": "…",
  "publishable": true,
  "systemConformance": {
    "score": 94,
    "assessed": 11,
    "unverifiable": 0,
    "reachedPublisher": true,
    "checks": [
      { "standard": "EN 18219", "requirement": "…", "status": "pass", "pass": true, "detail": "…" }
    ]
  },
  "passportHeader": { "score": 100, "presentCount": 7, "totalCount": 7 },
  "contentCoverage": {
    "score": 85,
    "presentCount": 11,
    "totalCount": 13,
    "schema": { "id": "espr-base", "name": "ESPR Base" },
    "notApplicableCount": 2,
    "topMissing": [ { "name": "…", "citation": "…" } ]
  },
  "cirpass2": {
    "title": "CIRPASS-2 data model",
    "note": "For information. CIRPASS-2 is an EU-funded project, and its data model is not a legal requirement. …",
    "docsUrl": "https://cirpass-2.github.io/ontologies-docs/ontologies.html",
    "rows": [
      {
        "term": "product_name",
        "name": "Product Name",
        "status": "matches",
        "eudpp": [ { "name": "productName", "uri": "https://w3id.org/eudpp#productName", "match": "exact" } ],
        "matchNote": null
      }
    ]
  },
  "categoryDetected": "textile",
  "product": { "name": "…", "brand": "…", "gtin": "…", "category": "textile" },
  "narrative": "…",
  "detail": "public"
}
  • systemConformance — each check names the standard and clause it tests and whether it passed. Checks that cannot be judged from outside are counted in unverifiable rather than failed. reachedPublisher: false means the address did not answer; no grade is given then.
  • passportHeader — how many of the EN 18223 header attributes the passport publishes.
  • contentCoverage — how many of the ESPR fields for the detected category are filled. basis says whether a delegated act for the category has been adopted.
  • cirpass2 — for information only. CIRPASS-2 is an EU-funded project, and its data model is not a legal requirement. Each row is one of our fields that has a term in the CIRPASS-2 ontologies (eudpp), with match exact (same meaning and unit) or close (near, and matchNote says how it differs). status is matches when the passport fills the field, missing when it does not, and not_verifiable when the passport data could not be read. term is the term in our vocabulary, /vocab/dpp/v1. Only fields that exist for the detected category are listed. This section never changes grade, overallScore, contentCoverage or any other count, and anonymous and keyed callers get the same rows.

Anonymous callers ("detail": "public") get the scores, every system check and up to six missing fields by name. With an API key ("detail": "full") the present and missing fields are listed with their keys and citations.

Score a document before publishing

POST /api/v1/validate?schema=auto
Content-Type: application/json
{
  "document": {
    "productIdentification": { "gtin": "09506000134352", "category": "electronics" },
    "productData": {
      "espr.pid.product_name": "Kompas Portable Speaker",
      "espr.mfr.name": "Example Audio A/S"
    }
  },
  "categoryHint": "electronics"
}

document is a passport in the JSON shape the resolver serves. schema (query or body) is auto (default: chosen from the category), espr-base, cirpass, agec, battery-regulation, toy-safety, food-information or all. Nothing is fetched or logged; the response is the coverage report with "detail": "report", and carries the same cirpass2 section beside it.

Limits

Caller Limit
Anonymous 20 requests a minute and 60 address checks an hour per IP address
With an API key the anonymous limits do not apply

An anonymous caller over the hourly limit gets 429 with Retry-After: 3600. Responses to anonymous address checks carry X-RateLimit-Limit and X-RateLimit-Remaining for the hourly count.

From a script or an agent

const r = await fetch('https://dppagent.com/api/v1/validate?url=' + encodeURIComponent(passportUrl));
const report = await r.json();