Get Audit Trail Task

A render that stops without reporting. A generate_audit_trail_pdf task still pending or processing 15 minutes after it was created is reported as failed — that is the hard limit the renderer job runs under, so it has already been stopped. The window starts when the task is created, so time spent queued counts toward it. Ask for a new render.

Poll the status of an async DDS task — the record-less status endpoint returned by Create Audit Trail.

POST /regulations/eudr/audit-trail is asynchronous: it returns 202 with a dds_record_task envelope and a Location header pointing here. The worker then resolves the filter, runs EUDR / art9 / art10 / hazard / infrastructure assessments, and inserts the dds_record + version 1 atomically — the record only appears once this task reaches completed. Poll this endpoint until the task is terminal.

This handler is org-scoped: the task is only visible to the organisation that created it (ctx.effectiveOrgIds). Polling with a key from a different org returns 404 — use the same key / x-acting-as you created the task with.

Status lifecycle

statusMeaning
pendingEnqueued, worker not yet started
processingWorker running (plot resolution, assessments, insert)
completedRecord + v1 written; result carries the new ids
failedAborted before any rows were written; see error_message. A render past the window above is also reported here, whether or not its worker got to write anything

Response attributes

FieldTypeDescription
operation"create_record" | "create_version" | "submit" | "generate_audit_trail_pdf"What the task does
statusenumSee lifecycle above
error_messagestring | nullFailure reason when status: "failed" (e.g. "Filter matched no plots"). For a render aged out by the window above, this is written by us from the task's age, not by the worker — which by then has usually stopped without saying anything
resultobject | nullSet only on completed. For create_record: { record_id, version_id, version_number, records[] } — see below. For create_version: { version_id, version_number }. For submit: adds the traces_* / submitted_at block. For generate_audit_trail_pdf: see the row below
end_date, include_unaccounted_plots, rerun_deforestation—Echo of the originating request's parameters
dds_record_iduuid | nullThe record the task belongs to. Stamped from the 202 onward for every operation, including a v1 create, whose record id is minted before dispatch. Null only on tasks enqueued before that was the case
created_at, updated_atISO 8601Task timestamps

result for generate_audit_trail_pdf

content_type and size_bytes describe the PDF; each sibling artefact repeats that pair under its own prefix. The other unprefixed fields describe the render as a whole.

FieldDescription
content_type, size_bytesThe PDF
dds_hashReport fingerprint the render was keyed on
cache_hittrue when an unchanged version reused the stored render
version_idThe version rendered
download_urlPath to the PDF download route
package_content_type, package_size_bytes, package_download_urlThe export ZIP
html_content_type, html_size_bytes, html_download_urlThe interactive HTML
geojson_content_type, geojson_part_count, geojson_total_size_bytes, geojson_dds_hash, geojson_download_urlThe TRACES GeoJSON. part_count because it is the only multi-part artefact; total_size_bytes is the sum across its parts

Two ways a field goes missing, and they are different:

  • A whole block is absent when its artefact does not exist. The interactive HTML lags the task's completion, and geojson_* is absent on a task that completed before that export existed.
  • Every *_download_url is absent, including the PDF's, when the task is missing either the record id or the version id — the route is built from both. The metadata still describes real artefacts; fetch them through the record's Export routes.

The *_download_url values are route paths, not files. Follow one to get a short-lived signed link.

result.records — the auto-split per-group list (create_record)

Create auto-splits the resolved plot set into one record per distinct (country_code, commodity) (EUDR records are single-country / single-commodity). result.records is the authoritative list of what was produced; the top-level result.record_id / version_id / version_number are a backward-compatible pointer to the first successfully created group — so the top-level version_id is always a real id on a completed task (a task only completes if ≥1 group was created; if every group failed the task is failed with result: null). The pointer deliberately skips failed groups: in the rare multi-group split where group 0 failed but a later group succeeded, the top-level pointer points at that later record, while group 0 (and the id minted on the 202) appears in result.records with status: "failed". Single-group callers can keep reading the top-level fields; multi-record callers should read result.records for the per-group truth.

FieldTypeDescription
record_iduuidThe group's DDS record. The first group reuses the id from the 202
version_iduuid | nullThe group's v1; null when the group failed
version_numberint1 on create
country_codestringThe group's country (part of the grouping key)
commoditystringThe group's normalised commodity (part of the grouping key)
status"created" | "failed"Per-group outcome — failures are isolated, so one bad group doesn't sink the rest
error_messagestringPresent only when status: "failed"

A single homogeneous plot set yields exactly one entry (and the legacy top-level pointer). Synchronous body errors (Zod, incomplete operator, unsupported separate_by) are returned as 400 by the create call itself, not here.

Note: a common failed reason is "Filter matched no plots" — the create filter resolved zero plots visible to the caller. The DDS commodity filter is alias-aware (e.g. wood matches timber, palm matches palm oil), but it still has to match plots you've actually ingested. Check the commodity / country of your staged plots.

Required scope: eudr:read