Every published passport is readable as JSON, so that another system, a
marketplace feed, a label printer's software or a future registry
connector, can read the same data a customer reads on the page. The plugin
adds two routes to the WordPress REST API under the dptag/v1 namespace,
and every public page carries schema.org structured data.
One passport, public
GET /wp-json/dptag/v1/passport/{uuid}
Public, no authentication. The identifier is the permanent one the QR code encodes, the last segment of the public address. The response is the passport as the public page renders it, in its source language:
{
"uuid": "3f2a9c1e-7b4d-4e8a-9c21-5d6f7a8b9c0d",
"name": "Organic cotton T-shirt",
"brand": "Linho & Co",
"sku": "TS-001",
"gtin": "",
"image": "https://shop.example/wp-content/uploads/tee.jpg",
"template": "textile",
"updated": "2026-09-07",
"data": {
"materials": [
{ "name": "organic cotton", "percent": 95 },
{ "name": "elastane", "percent": 5 }
],
"country_of_origin": "Portugal",
"care_instructions": "Machine wash at 30°C. Do not tumble dry.",
"recycling": "Drop off at any textile collection point.",
"certifications": "GOTS"
},
"url": "https://shop.example/dpp/3f2a9c1e-7b4d-4e8a-9c21-5d6f7a8b9c0d/",
"lang": "en",
"source_lang": "en",
"languages": ["en"]
}
data holds the template's fields by key, only the filled ones; the keys
are those of the textile and
furniture templates. brand is
the site's title. gtin is the product's global unique identifier when
WooCommerce has one. languages lists the languages the passport exists in;
lang is the language of this response, which is always the source
language on this route. A translated rendering is available on the public
page itself, with ?lang= followed by the language code.
An unknown or unpublished identifier answers a 404 with a dptag_not_found
code. Responses may be cached by intermediaries for up to an hour.
The list, for the site's users
GET /wp-json/dptag/v1/passports
Authenticated: the caller must be a logged-in user allowed to edit posts, through a cookie and nonce from the admin or an application password. It returns up to one hundred passports, drafts included, each as:
{
"id": 1042,
"title": "Organic cotton T-shirt",
"status": "publish",
"score": 100,
"uuid": "3f2a9c1e-7b4d-4e8a-9c21-5d6f7a8b9c0d",
"url": "https://shop.example/dpp/3f2a9c1e-7b4d-4e8a-9c21-5d6f7a8b9c0d/"
}
url is empty for a draft, which has no public page. A user only sees the
passports they may edit, which is what the passports list shows them too.
For a whole catalogue with every field, the Pro add-on's
CSV export is the tool.
Structured data on the public page
Each public page embeds a schema.org Product in an application/ld+json
block, built from the same payload:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Organic cotton T-shirt",
"brand": { "@type": "Brand", "name": "Linho & Co" },
"sku": "TS-001",
"image": "https://shop.example/wp-content/uploads/tee.jpg",
"material": "organic cotton 95%, elastane 5%",
"countryOfOrigin": "Portugal",
"url": "https://shop.example/dpp/3f2a9c1e-7b4d-4e8a-9c21-5d6f7a8b9c0d/",
"inLanguage": "en"
}
gtin is added when the product has one, and empty values are left out.
This is the block search engines read on any product page; it is what makes
a passport findable by name and is not required by the regulation.
Stability
The route names, the field keys and the shape above are meant to stay. New
templates add new keys under data; existing keys keep their meaning. When
the delegated acts fix official formats, the plugin will add what they
require rather than rename what is here, so that a consumer written today
keeps working.