Skip to main content
The other pages document every endpoint and rule. This page puts them in order: which calls an ERP integration makes, in which sequence, and what it should store. Each flow links to the page with the details. Base URL, key and headers are as in the Quickstart.

Order sync

ERP to Polygon One, and the DDS references back — the main EUDR flow. Your ERP pushes master data and orders; in Polygon One the orders are linked to due diligence statements (DDS); your ERP reads back, per order and position, whether the order is covered and which TRACES reference belongs to it.
1

Push master data first: suppliers, then articles

Send suppliers and articles as batches before the orders, so that order rows resolve to articles that already exist — hsCode and articleDescription on an order row are only read when the article does not exist yet (create-only). Suppliers are upserted on externalSupplierId or name, articles on internalArticleNr. A purchase article needs a supplier (supplierId or supplierName).
Each answers 202 with a jobId. Wait until the supplier job has finished before you send the articles, and until the article job has finished before you send the orders (poll as in step 3). Jobs of one workspace can run at the same time: an article row whose supplierName is not stored yet creates a new supplier with only that name.
2

Push orders as a batch — every line of an order in the same request

One row is one order line; rows with the same orderNumber form one order. Importing an order replaces its whole line set, so a request that names an order must carry all of its lines. If your ERP has position numbers, send them as positionId on every row of the order.
quantity is the net mass in kilograms. At most 5,000 rows per request — page a larger sync across several requests, keeping each order’s lines together.
3

Poll the import job and store the result

Poll about every 2 seconds, backing off for large jobs (e.g. exponentially up to ~30 s), until status is completed or failed. Persist result on your side — the job record is deleted ~30 days after creation. Failed rows carry stable error keys in failed[].error.key. A failed job carries its reason in error, a string; for lineReplacementRequired it holds JSON with the orderNumbers — a request would have deleted stored lines: resend the listed orders with their complete line sets.
4

Delta-poll the orders on a schedule

Poll changedSince = the start of your previous poll minus a few minutes (an ISO 8601 timestamp with Z or an offset), with limit=1000. Hourly is recommended. fields keeps the response to what the decision in step 5 needs.
A full page (1000 orders) means changed orders were left out. Page the same changedSince window with offset until a page is shorter than limit, and poll more often. An order can appear twice; one that changes while you page comes back in the next delta.
5

Decide per order and position

Decide on what the current read returns, never on an earlier one:
  • Whether the whole order is covered: status (done, open or notRelevant). An article awaiting the manual EUDR-relevance check keeps the order open.
  • Unblocking a position (e.g. an SAP block): release a position only when
    1. the order’s status is done (every EUDR-relevant line is covered by a filed DDS) or notRelevant, or
    2. for a single line, lines[].article.eudrRelevant is false and lines[].article.needsManualCheck is false (the line’s article is not EUDR-relevant).
  • Which TRACES reference to print: only after that decision, from ddsReferences (referenceNumber, verificationNumber), for the documents.
Do not release goods because ddsReferences is non-empty. ddsReferences belongs to the whole order and lists every linked DDS that is not withdrawn or rejected — including drafts without a referenceNumber, external and archived statements. When an order carries several references, the API does not say which one covers which position.
status can be open while ddsReferences is non-empty (e.g. a draft or an external DDS). A withdrawn or rejected DDS drops out of ddsReferences, and the order comes back in the next delta.
6

Run a nightly full sync as recovery

Deleted orders, orders of a deleted supplier and unlinked DDS never show up in a delta. Once a night, read the full list and reconcile, paging with offset until a page is shorter than limit. Treat an order missing from the full list as deleted.
Some status changes do not move the order into a delta (e.g. another order raising its quantity on the same DDS, or an article’s manual check being resolved). Re-read with ?status=open&limit=1000 to reconcile them, paging with offset the same way.
What goes wrong most often
  • Splitting an order across requests. The second request would delete the lines of the first; the check fails the whole job with lineReplacementRequired instead. Send complete line sets, or "replaceLines": true when the line set is meant to shrink.
  • positionId on some rows only. All rows of an order carry one, or none (positionIdMixed), and each only once per order (duplicatePositionId). An order stored without position ids that is sent with them for the first time needs "replaceLines": true.
  • Releasing goods on a reference. Release only on status or on lines[].article.eudrRelevant and lines[].article.needsManualCheck both false in the current read — never because ddsReferences is non-empty, and never on an earlier read.
  • Stopping at a full delta page. The orders beyond it are not returned again unless they change; page the window with offset.
Details: Orders (completeness rule, positions, status, delta sync) and Batch Import (async).

Variant: per-order origin from the ERP

An order line points at its own copy of the article when the article is a per-order template: an article a purchase-order import created, an article sent with sourceScope: "perOrder", or — with the workspace setting “order import creates per-order copies” (off by default, switched on by Polygon One on request) — an existing article a purchase-order row names. Your ERP can attach plots and documents to that copy directly, without the supplier portal: find the copy, create plots with its supplierArticleId, upload evidence. This needs plots:write and evidence:write; the ERP integration key preset includes both (a key created with it before 1 October 2026 lacks them — check its permissions). Steps and responses: Per-order origin via ERP. Once all current plots are uploaded, call POST /api/v1/orders/{id}/origin-confirmation with the selected order-line supplierArticleIds; the supplier then confirms this order’s current origin data. Previous-order data is never copied, and buyer authorship stays unchanged. Details: Have the supplier confirm the origin.

Plots and geo analysis

Send a plot geometry, wait for the deforestation analysis, read the result, download the report. No article or supplier is needed — a plot with a commodity group is enough.
1

Optional: check the geometry

A dry run with the same body as create — writes nothing, answers valid with the codes create would answer. commodityGroup is optional here, but if you send "wood" the dry run also requires a scientific species, as create does. It does not check your plot limit.
2

Create the plot

201 with the plot under created. Take created.id. 409 with code: "PLOT_LIMIT_REACHED" means your plot limit or monthly quota is reached: nothing is created, and a retry does not help.
3

Poll the analysis status

Every 15 to 30 seconds while analysisStatus is pending or in_progress:
4

Download the report once

What goes wrong most often
  • Sending a FeatureCollection. One request creates one plot — send each feature on its own.
  • Polling the report instead of the status. Every report call renders the PDF anew; download it once, when the result exists.
  • No plot reference in name. Same name, country, geometry and supplierArticleIds again is recognised ("deduplicated": true) — that is what makes a retry safe.
  • No country. Without it, the low-risk skip never applies and the plot is always analyzed.
Details: Geo analysis (status, idempotency, error codes, recipe).

PPWR volume reporting

A short form of the Integration recipe ERP to LUCID. Steps 1–3 are one-time master data; steps 4–6 are the recurring job. The master-data endpoints answer synchronously (200); the quantity feed is an import job. The articles (SKUs) must already exist in Polygon One: create missing ones with POST /api/v1/articles (articles:write). An unknown article number is an error row in article-links and in the quantity job.
1

Components with material and mass

2

Packaging units — always with an internalRef

A unit row without internalRef always creates a new unit. notes is mandatory.
3

Bill of materials and SKU links

Link components to units with POST /api/v1/ppwr/links, and articles (SKUs) to units with POST /api/v1/ppwr/article-links, which reports an unknown article number as an error row.
4

Recurring: push quantities per SKU, market and year

202 with a jobId — poll GET /api/v1/import-jobs/{jobId} as in the order sync. Each row’s quantity is the absolute total for that SKU, year and market and replaces the stored value — never send increments, returns or credit notes as rows of their own; net them into the total. Send replaceScope: "period-market" when the batch is the complete list for every year and market it names; leave the field out when the batch carries only the SKUs that changed.
5

Generate the report

A 422 lists item by item which data is not declarable yet — fix, re-push, generate again.
6

Fetch the file and upload it to LUCID

List the current report, then download it with GET /api/v1/ppwr/mengenmeldung/{id}/xml:
Uploading the XML in LUCID is the one step that cannot be automated.
What goes wrong most often
  • Unit rows without internalRef. Each repeat creates another unit.
  • Linking SKUs via the unit row’s articleNumbers. An unknown article number there is skipped; the response only counts such numbers (articleNumbersUnmatched) and does not name them. POST /api/v1/ppwr/article-links reports each one as an error row.
  • Overwriting a stored value without overwrite: true. The whole batch is refused with 409 overwriteRequired, nothing is written.
  • Expecting the feed to replace a hand-entered figure. It never does; affected units come back in conflicts.
To write issued Declarations of Conformity back to your item master, see the ERP recipe under Reading Declarations of Conformity.

Before you go live

  • Key permissions per flow. Order sync: suppliers:write, articles:write, orders:write (Batch Import); per-order origin additionally plots:write and evidence:write; plots: plots:write (Geo analysis); PPWR: packaging:write, plus articles:write if you create the SKUs over the API (PPWR). Write permissions include read. The ERP integration key preset includes all of these (a key created with it before 1 October 2026 lacks plots:write and evidence:write).
  • Modules and exports. A key acts as a user. The PPWR endpoints need the PPWR module for that user. The plot report needs the EUDR module for that user and exports enabled for your workspace (otherwise 403).
  • The rate limit is shared per IP. 1000 requests per 60 seconds per client IP, across all /api paths and including people using the web app from the same network. Spread scheduled jobs and use the batch endpoints (Rate limit).
  • Retry only what is safe to retry. 429 with JSON and 5xx with exponential backoff and jitter; batch imports are safe to replay. A repeated single-object supplier POST answers 409 (same name or externalSupplierId). A repeated single-object order POST answers 409, and so does renaming an order onto an existing order number — look the order up first, or send orders as one-row batches (Retries).
  • There is no sandbox. Every call acts on your live workspace (Not in v1).
  • Log x-vercel-id on every failed call — support needs it (Request reference).
  • Ignore unknown response fields. v1 adds fields over time (Versioning).