Programmatic access to product passports — for webshop/ERP sync and bulk automation.
From the Pro plan
During the open beta the API is available to every beta account — the Pro requirement applies after the beta.
Every request authenticates with an API key, in the Authorization header:
Authorization: Bearer vk_<your key>
You create a key under Settings → Developer API. The plaintext key is shown only once, at creation — store it safely. The key grants read/write access to all products in your account, so keep it confidential.
The key goes only in the Authorization header — not as a URL parameter, for security (it would end up in browser history and server logs). Try it for example with curl:
curl -H "Authorization: Bearer vk_<your key>" https://veridyn.eu/api/v1/categories
When creating a key you choose a scope: Full (read + write) or Read-only. A read-only key may only call GET — POST/PATCH/DELETE return 403 read_only. Use a read-only key for ERP or display integrations.
Per key, 120–600 requests / minute depending on plan (Pro 120 · Business 300 · Enterprise 600; 300 during the open beta). Every response includes the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix time) headers. Exceeding the limit returns 429 rate_limited with a Retry-After header — wait the indicated time, then retry.
You can add a unique Idempotency-Key header to POST requests. If you resend it with the same key (e.g. after a network error), you get back the original response — no duplicate is created. A replayed response is marked with the Idempotency-Replayed: true header.
Machine-readable API description (OpenAPI 3.1) — for Postman import and SDK/code generation:
https://veridyn.eu/api/v1/openapi.json
Browsable, searchable endpoint reference — rendered directly from the spec above.
The machine-readable passport is JSON-LD: standard fields come from schema.org, DPP-specific fields from the dpp: namespace. That namespace is resolvable — open it to see what every field means, or send Accept: application/ld+json to get the machine-readable @context document.
https://veridyn.eu/ns/dpp/v1/
A single passport as data: its public URL with ?format=jsonld (or an Accept: application/ld+json header).
A ready-to-import collection with pre-set variables and example requests. After importing, just fill in the base_url and api_key collection variables.
From the OpenAPI spec you can generate an official client in any language with openapi-generator — no hand-maintained SDK, always up to date. E.g. PHP:
npx @openapitools/openapi-generator-cli generate \
-i https://veridyn.eu/api/v1/openapi.json \
-g php -o ./veridyn-sdk
The -g value can be typescript-fetch, python, java and many more.
PHP
$ch = curl_init('https://veridyn.eu/api/v1/products?limit=5');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer vk_...'],
]);
$products = json_decode(curl_exec($ch), true)['data'];
JavaScript (fetch)
const res = await fetch('https://veridyn.eu/api/v1/products?limit=5', {
headers: { 'Authorization': 'Bearer vk_...' }
});
const { data } = await res.json();
https://veridyn.eu/api/v1
Responses are application/json, UTF-8 encoded.
| Method | Path | Description |
|---|---|---|
| GET | /categories | Available product categories. |
| GET | /schema/{category} | The category's full field list — required + optional, type, enum, localized fields. |
| GET | /products | List of products (summary). Parameters: q, status, page, limit. |
| POST | /products | New product passport. Body: { category, data }. |
| GET | /products/{id} | A product's full data + passport URL. |
| PATCH | /products/{id} | Update (partial) — creates a new version. Body: { data, change_type? }. change_type is optional: correction (the model held wrong data → already-issued batch/unit passports become out of date and can be refreshed) or change (the product changed from a given date → earlier units keep their correct data and are not overwritten). |
| GET | /products/{id}/qr | QR code (SVG) for the public passport. |
| GET | /products/{id}/children | List of the batch/unit child passports. |
| POST | /products/{id}/children | Batch/unit passport. Single: { level, lot|serial, data } · bulk: { level, items:[…] } (max 500). Inherits the parent's data. |
| GET | /products/{id}/scans | Per-product scan summary: total, unique, byCountry, byDevice. Parameter: days=7|30|90|365. |
| GET | /scans | Tenant-wide scan summary: the above + byCategory, byLevel. |
Every category has a required core — without it, schema validation rejects the request (422). All other fields are optional, but you can send them in the same data object anytime (more data = a better passport and more future-proof compliance). The exact, machine-readable field list is returned by /schema/{category} — with required: true/false, type, enum and localized markers.
curl https://veridyn.eu/api/v1/schema/textile \
-H "Authorization: Bearer vk_<key>"
✓ = required · — = optional · ↳ nested sub-field · 🌐 per-language (localized) · 🔒 visible only at authority level. This list is generated from the schema — always up to date.
| Field | Type | Req. | Description |
|---|---|---|---|
productName | string | ✓ | Product name — The product name as the buyer will see it on the passport. |
sku | string | ✓ | SKU — Internal item number / identifier — from your own system. |
brandName | string | ✓ | Brand |
gtin | string | — | GTIN (optional) — The barcode number (GTIN-8/12/13/14). The system validates the check digit. Leave empty if you don't have one. |
commodityCode | string | — | Commodity code (CN/HS, optional) — Customs/tariff classification: Combined Nomenclature (CN, 8 digits) or HS code. Mandatory metadata of the EU DPP register at registration — it appears on your supplier/customs documents. |
economicOperator | object | ✓ | Economic operator |
↳ role | string (enum) | ✓ | Role — Who places the product on the EU market and is responsible for it. For most manufacturers: Manufacturer. (manufacturer · importer · authorized_representative · distributor · dealer · fulfilment_service_provider) |
↳ legalName | string | ✓ | Legal name |
↳ address | string | ✓ | Address |
↳ country | string | ✓ | Country (ISO 3166-1 alpha-2) — Two-letter ISO country code, in capitals — e.g. HU, DE, IT |
↳ contactEmail | string | ✓ | Contact email — This is where the buyer or an authority can turn. |
↳ operatorId | string | — | Unique operator ID (GS1 GLN) — 13-digit GS1 GLN — the EU DPP unique economic operator identifier. Leave empty if you don't have one. |
↳ eori 🔒 | string | — | EORI number — The economic operator's EORI number (customs / EU DPP registry identifier) — the registry identifies you by this. Visible only at authority access level. Leave empty if you don't have one. |
languages | array[string] | ✓ | Languages (BCP-47) — Which languages the passport should appear in. The localised fields (care, life cycle) must be filled in for every language listed here. e.g. hu, en |
fiberComposition | array[object] | ✓ | Fibre composition — What the product is made of. The percentages must add up to exactly 100. e.g. cotton 95 + elastane 5 |
↳ fiber | string | ✓ | Fibre |
↳ percentage | number | ✓ | Percentage |
recycledContentPercentage | number | — | Recycled content — The share of recycled content in the product. Leave empty if not relevant. |
countryOfManufacture | string | ✓ | Country of manufacture (ISO) — Where the finished product was made. Two-letter ISO code, e.g. PT, TR, HU |
supplyChainStages | array[object] | — | Supply chain stages — Where each production step took place (spinning, weaving, dyeing, assembly…). Optional, but it builds trust. The step and the country are public; the facility name and ID are protected — only a legitimate interest party (and authorities) can see them, via a token link. |
↳ stage | string (enum) | ✓ | Stage (spinning · weaving · knitting · dyeing · finishing · assembly) |
↳ country | string | ✓ | Country (ISO) |
↳ facilityName 🔒 | string | — | Facility name |
↳ facilityId 🔒 | string | — | Unique facility ID (GS1 GLN) |
careInstructions 🌐 | object | ✓ | Care instructions — Washing, drying, ironing — one text per language. |
repair | object | ✓ | Repairability — Whether and how it can be repaired. The “repairable” and “spare parts available” fields are mandatory. |
↳ repairable | boolean | ✓ | Repairable |
↳ instructions 🌐 | object | — | Repair instructions — How it can be repaired — per language (optional). |
↳ sparePartsAvailable | boolean | ✓ | Spare parts available (e.g. button, zip) |
substancesOfConcern | array[object] | — | Substances of concern — Substances of concern (e.g. REACH SVHC), if present in the product. Can be left empty for most products. |
↳ name | string | ✓ | Name |
↳ casNumber | string | — | CAS number |
↳ concentrationRange | string | — | Concentration range |
durability | object | — | Durability — Durability data, if you have measurements (optional). |
↳ testResults | string | — | Test results |
↳ pefScore | number | — | PEF score — Product Environmental Footprint score, if available. (optional) |
carbonFootprint | number | — | Carbon footprint (kg CO₂e) — The product's total carbon footprint in kg CO₂ equivalent. Shown in the “Impact” theme as an “≈ km by car” comparison. |
waterFootprint | number | — | Water footprint (litres) — The amount of water used in manufacturing, in litres. Optional. |
weightGrams | number | — | Weight (grams) — The product's weight in grams. Optional. |
endOfLife | object | ✓ | End of life — What should happen to the product after use — filled in for each language. |
↳ recyclingInstructions 🌐 | object | ✓ | Recycling instructions — How it can be recycled — per language. |
↳ disposalInstructions 🌐 | object | ✓ | Disposal instructions — If it is not recyclable — per language. |
complianceDocuments | array[object] | — | Compliance documents — Links to certificates and conformity documents (optional). e.g. OEKO-TEX, GOTS. |
↳ type | string | ✓ | Type |
↳ url | string | ✓ | URL |
| Field | Type | Req. | Description |
|---|---|---|---|
productName | string | ✓ | Product name — The battery name as the buyer will see it on the passport. |
sku | string | ✓ | SKU — Internal item number / identifier — from your own system. |
brandName | string | ✓ | Brand |
gtin | string | — | GTIN (optional) — The barcode number (GTIN-8/12/13/14). The system validates the check digit. Leave empty if you don't have one. |
commodityCode | string | — | Commodity code (CN/HS, optional) — Customs/tariff classification: Combined Nomenclature (CN, 8 digits) or HS code. Mandatory metadata of the EU DPP register at registration — it appears on your supplier/customs documents. |
economicOperator | object | ✓ | Economic operator |
↳ role | string (enum) | ✓ | Role — Who places the product on the EU market and is responsible for it. For most manufacturers: Manufacturer. (manufacturer · importer · authorized_representative · distributor · dealer · fulfilment_service_provider) |
↳ legalName | string | ✓ | Legal name |
↳ address | string | ✓ | Address |
↳ country | string | ✓ | Country (ISO 3166-1 alpha-2) — Two-letter ISO country code, in capitals — e.g. HU, DE, IT |
↳ contactEmail | string | ✓ | Contact email — This is where the buyer or an authority can turn. |
↳ operatorId | string | — | Unique operator ID (GS1 GLN) — 13-digit GS1 GLN — the EU DPP unique economic operator identifier. Leave empty if you don't have one. |
↳ eori 🔒 | string | — | EORI number — The economic operator's EORI number (customs / EU DPP registry identifier) — the registry identifies you by this. Visible only at authority access level. Leave empty if you don't have one. |
languages | array[string] | ✓ | Languages (BCP-47) — Which languages the passport should appear in. The localised fields (safety, life cycle) must be filled in for every language listed here. e.g. hu, en |
batteryCategory | string (enum) | ✓ | Battery category — The category under (EU) 2023/1542. The mandatory passport applies first to EV, industrial (>2 kWh) and LMT batteries (18 February 2027). (portable · lmt · ev · industrial · sli) |
cellChemistry | string (enum) | ✓ | Cell chemistry — The battery's cell chemistry type. (nmc · nca · lfp · lmo · lto · nimh · lead_acid · sodium_ion …) |
weightKg | number | ✓ | Weight (kg) — The battery's weight in kilograms. |
countryOfManufacture | string | ✓ | Country of manufacture (ISO) — Where the battery was made. Two-letter ISO code, e.g. DE, HU, CN |
manufacturingDate | string | ✓ | Manufacturing date / year — The year or year-month of manufacture, in ISO format: YYYY, YYYY-MM or YYYY-MM-DD. |
ratedCapacity | number | ✓ | Rated capacity (Ah) — Nominal capacity in ampere-hours (Ah). |
energyWh | number | — | Energy (Wh) — Total energy content in watt-hours (Wh). Optional. |
nominalVoltage | number | — | Nominal voltage (V) — Nominal voltage in volts. Optional. |
expectedLifetimeCycles | number | — | Expected lifetime (charge cycles) — The guaranteed / expected total number of charging cycles. Optional. |
stateOfHealth 🔒 | number | — | State of Health — The battery's state of health as a % of the original capacity (100 for a new battery). UNIT-SPECIFIC and dynamic — its ideal place is the unit level (not the model template). Optional. |
carbonFootprint | number | — | Carbon footprint (kg CO₂e / kWh) — The battery's carbon footprint over the full life cycle, in kg CO₂ equivalent per kWh of total energy. |
carbonFootprintClass | string | — | Carbon footprint class (A–G) — The CF performance class under the regulation, if available. Optional. |
carbonFootprintStudyUrl | string | — | Carbon footprint study (URL) — Link to the study / documentation underlying the carbon footprint calculation (per the CF delegated act). Optional. |
carbonFootprintBreakdown | array[object] | — | Carbon footprint by lifecycle stage — Breakdown of the carbon footprint by life cycle stage (kg CO₂e / kWh) — this is what the CF delegated act expects. Optional. |
↳ stage | string (enum) | ✓ | Lifecycle stage (raw_material · main_production · distribution · recycling) |
↳ value | number | ✓ | Value (kg CO₂e / kWh) |
recycledContent | array[object] | — | Recycled raw-material content — The share of recycled critical raw materials per material (cobalt, lithium, nickel, lead). Optional, but the regulation expects it ever more strictly. |
↳ material | string (enum) | ✓ | Material (cobalt · lithium · nickel · lead) |
↳ percentage | number | ✓ | Recycled share |
hazardousSubstances | array[object] | — | Hazardous substances — Hazardous substances present in the battery (beyond mercury, cadmium and lead). Relevant for most data sheets. |
↳ name | string | ✓ | Name |
↳ casNumber | string | — | CAS number |
↳ concentrationRange | string | — | Concentration range |
safetyInformation 🌐 | object | — | Safety information — Handling, storage and emergency information — one text per language. |
dueDiligenceUrl 🔒 | string | — | Supply chain due diligence report (URL) — Link to the due diligence policy/report required by the regulation. Legitimate interest access level (Art. 77(4)) — not public. Optional. |
endOfLife | object | ✓ | End of life — Collection, recycling, disassembly — filled in for each language. |
↳ recyclingInstructions 🌐 | object | ✓ | Collection / recycling — Where it can be returned and how it can be recycled — per language. |
↳ disposalInstructions 🌐 | object | ✓ | Disposal / warnings — Prohibitions, hazards — per language. |
complianceDocuments | array[object] | — | Compliance documents — Links to certificates, conformity and test documents (optional). e.g. CE, UN 38.3. |
↳ type | string | ✓ | Type |
↳ url | string | ✓ | URL |
| Field | Type | Req. | Description |
|---|---|---|---|
productName | string | ✓ | Product name — The product name as the buyer will see it on the passport. |
sku | string | ✓ | SKU — Internal item number / identifier — from your own system. |
brandName | string | ✓ | Brand |
gtin | string | — | GTIN (optional) — The barcode number (GTIN-8/12/13/14). The system validates the check digit. Leave empty if you don't have one. |
commodityCode | string | — | Commodity code (CN/HS, optional) — Customs/tariff classification: Combined Nomenclature (CN, 8 digits) or HS code. Mandatory metadata of the EU DPP register at registration — it appears on your supplier/customs documents. |
economicOperator | object | ✓ | Economic operator |
↳ role | string (enum) | ✓ | Role — Who places the product on the EU market and is responsible for it. For most manufacturers: Manufacturer. (manufacturer · importer · authorized_representative · distributor · dealer · fulfilment_service_provider) |
↳ legalName | string | ✓ | Legal name |
↳ address | string | ✓ | Address |
↳ country | string | ✓ | Country (ISO 3166-1 alpha-2) — Two-letter ISO country code, in capitals — e.g. HU, DE, IT |
↳ contactEmail | string | ✓ | Contact email — This is where the buyer or an authority can turn. |
↳ operatorId | string | — | Unique operator ID (GS1 GLN) — 13-digit GS1 GLN — the EU DPP unique economic operator identifier. Leave empty if you don't have one. |
↳ eori 🔒 | string | — | EORI number — The economic operator's EORI number (customs / EU DPP registry identifier) — the registry identifies you by this. Visible only at authority access level. Leave empty if you don't have one. |
languages | array[string] | ✓ | Languages (BCP-47) — Which languages the passport should appear in. The localised fields (care, assembly, life cycle) must be filled in for every language listed here. e.g. hu, en |
materialComposition | array[object] | ✓ | Material composition — What the furniture is made of. The percentages must add up to exactly 100. e.g. solid oak 80 + metal 20 |
↳ material | string (enum) | ✓ | Material (wood · engineeredWood · metal · plastic · glass · textile · foam · leather …) |
↳ percentage | number | ✓ | Percentage |
woodCertification | object | — | Wood origin & certification — Sustainable sourcing and certification of the wood used, if relevant (optional). |
↳ scheme | string (enum) | — | Certification scheme — Sustainable forest management certification of the wood. (fsc · pefc · none) |
↳ country | string | — | Country of origin (ISO) — Country of origin of the wood. Two-letter ISO code, e.g. AT, SE, RO. Optional. |
dimensions | object | — | Dimensions & weight — Overall dimensions and weight of the furniture (optional). |
↳ width | number | — | Width (cm) |
↳ depth | number | — | Depth (cm) |
↳ height | number | — | Height (cm) |
↳ weightKg | number | — | Weight (kg) |
countryOfManufacture | string | ✓ | Country of manufacture (ISO) — Where the finished product was made. Two-letter ISO code, e.g. PL, RO, HU |
careInstructions 🌐 | object | ✓ | Care instructions — Cleaning, care, surface maintenance — one text per language. |
assemblyInstructions 🌐 | object | — | Assembly instructions — How the furniture is assembled — per language (optional). A link can also be given. |
repair | object | ✓ | Repairability — Whether and how it can be repaired. The “repairable” and “spare parts available” fields are mandatory. |
↳ repairable | boolean | ✓ | Repairable |
↳ instructions 🌐 | object | — | Repair instructions — How it can be repaired — per language (optional). |
↳ sparePartsAvailable | boolean | ✓ | Spare parts available (e.g. fittings, leg) |
↳ sparePartsUrl | string | — | Spare parts (URL) — Where spare parts can be ordered (optional). |
warrantyMonths | number | — | Warranty (months) — The length of the manufacturer's warranty in months. Optional. |
substancesOfConcern | array[object] | — | Substances of concern — Substances of concern (e.g. REACH SVHC / SCIP), if present in the product. Can be left empty for most products. |
↳ name | string | ✓ | Name |
↳ casNumber | string | — | CAS number |
↳ note | string | — | Note |
flameRetardants | string (enum) | — | Flame retardants — Whether the product (especially the upholstery / foam) contains flame retardant chemicals. Optional. (present · absent · unknown) |
complianceDocuments | array[object] | — | Compliance documents — Links to certificates and conformity documents (optional). e.g. EN 12520, EN 1728, fire safety certificate. |
↳ type | string | ✓ | Type |
↳ url | string | ✓ | URL |
packaging | string (enum) | — | Packaging recyclability — Recyclability of the product's packaging. Optional. (recyclable · partiallyRecyclable · notRecyclable) |
endOfLife | object | ✓ | End of life — What should happen to the furniture after use — filled in for each language. |
↳ recyclingInstructions 🌐 | object | ✓ | Recycling instructions — How it can be taken apart and recycled — per language. |
↳ disposalInstructions 🌐 | object | ✓ | Disposal instructions — If it is not recyclable — per language. |
The languages field (e.g. ["hu","en"]) declares which languages have free-text content. The localized fields (care, end-of-life… — "localized": true in /schema) must be filled only in the first (primary) language; the rest are optional, falling back to an available language if missing. The passport's UI labels are translated to 24 languages automatically, independently of this.
curl -X POST https://veridyn.eu/api/v1/products \
-H "Authorization: Bearer vk_<key>" \
-H "Content-Type: application/json" \
-d '{
"category": "textile",
"data": {
"productName": "Organic cotton tee",
"sku": "TEE-001",
"brandName": "Lumora",
"languages": ["hu","en"],
"fiberComposition": [{"fiber":"pamut","percentage":100}],
"countryOfManufacture": "PT",
"careInstructions": {"hu":"Mosás 30 °C","en":"Wash at 30 °C"},
"repair": {"repairable": true, "sparePartsAvailable": false},
"endOfLife": {
"recyclingInstructions": {"hu":"Textilgyűjtő","en":"Textile bin"},
"disposalInstructions": {"hu":"Ne a kukába","en":"Not household waste"}
},
"economicOperator": {
"role":"manufacturer","legalName":"Lumora Kft.","address":"Budapest",
"country":"HU","contactEmail":"[email protected]"
}
}
}'
Response (201):
{
"data": {
"id": "a1993ef7-7562-43bb-92ff-eb63f02dcde9",
"category": "textile",
"status": "active",
"version_no": 1,
"passport_url": "https://veridyn.eu/<account>/p/a1993ef7-…",
"qr_url": "https://veridyn.eu/api/v1/products/a1993ef7-…/qr",
"data": { "productName": "Organic cotton tee", … }
}
}
curl https://veridyn.eu/api/v1/products?limit=25 \
-H "Authorization: Bearer vk_<key>"
curl https://veridyn.eu/api/v1/products/{id} \
-H "Authorization: Bearer vk_<key>"
curl -X PATCH https://veridyn.eu/api/v1/products/{id} \
-H "Authorization: Bearer vk_<key>" \
-H "Content-Type: application/json" \
-d '{"data": {"recycledContentPercentage": 30}}'
The provided fields are merged onto the existing data (partial update), then validated, and a new, retained version is created — the full change history is preserved.
Create batch or unit passports under a model; the child inherits the parent's data — you pass only the instance-specific fields (lot/serial). Many at once (items[], max 500); the response reports success/error per item.
curl -X POST https://veridyn.eu/api/v1/products/{id}/children \
-H "Authorization: Bearer vk_<key>" \
-H "Content-Type: application/json" \
-d '{"level":"item","items":[{"serial":"SN-0001"},{"serial":"SN-0002"}]}'
The {id} may be a model or a batch. POST to a batch id with level:"item" and the unit is created under that batch, inheriting its lot — its GS1 link is then …/10/lot/21/serial rather than …/21/serial. This is how the full model → batch → unit chain is built.
Aggregated QR-scan statistics — per product or account-wide — broken down by country and device. Privacy: no raw IP; unique visitors are an anonymous hashed estimate.
curl "https://veridyn.eu/api/v1/products/{id}/scans?days=30" \
-H "Authorization: Bearer vk_<key>"
{
"data": {
"product_id": "a1993ef7-…",
"range_days": 30,
"total": 189, "unique": 142,
"byCountry": { "HU": 142, "DE": 38, "AT": 9 },
"byDevice": { "mobile": 168, "tablet": 9, "desktop": 12 }
}
}
Success: the payload is under the data key. On error:
{ "error": "Invalid data.", "code": "validation", "errors": [ … ] }
| HTTP | code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing/invalid API key. |
| 403 | plan_required | The API is available from the Pro plan (during the open beta, every beta account can use it). |
| 404 | not_found | No such product / route. |
| 409 | gtin_taken | The GTIN is already taken. |
| 403 | plan_limit | You reached the level's quota (plan + extra). In bulk calls, the overflowing items go into errors. |
| 422 | validation | Invalid data (details in errors). |
Under Settings → Webhooks you register an HTTPS URL. When a product passport is created / updated / archived, Veridyn POSTs a signed JSON to that URL — so your system is notified in real time, without polling.
product.created · product.updated · product.archived · product.restored · scan.milestone · scan.clone_suspected
The scan.milestone fires when a passport's scan count crosses a milestone (10, 50, 100, 250, 500, 1000, …) — e.g. "your passport was opened 1000 times". Data: { product_id, count, milestone }.
The scan.clone_suspected flags possible counterfeiting: a unique (serialised) passport was scanned unusually often and from many different countries/devices — which may indicate a copied QR code. Data: { product_id, count, countries, unique_devices }.
POST https://your-system.example/veridyn-webhook
Content-Type: application/json
X-Veridyn-Event: product.updated
X-Veridyn-Signature: sha256=<hmac>
{
"event": "product.updated",
"data": { "id": "a1993ef7-…", "category": "textile", "version_no": 2 },
"sent_at": "2026-07-02T13:19:11+00:00"
}
The X-Veridyn-Signature is the HMAC-SHA256 of the raw body with the webhook secret (shown in Settings). Verify it — e.g. in PHP:
$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_VERIDYN_SIGNATURE'] ?? '')) {
http_response_code(401); exit; // invalid signature}
Respond with a 2xx status. If delivery fails (non-2xx / timeout), we automatically retry with exponential backoff (about 1 min → 5 min → 30 min → 2 h → 6 h, up to 6 attempts), giving up on persistent failure. The status of the last delivery is shown in Settings.
/schema/{category} endpoint. Currently available: textile, battery and furniture. The schema grows as the EU finalises categories.