---
title: "Build a compliance export"
method: POST
path: "/compliance-exports"
tags: ["Compliance exports"]
---

# Build a compliance export

`POST /compliance-exports`

## Request

- Base URL: `https://api.purplegrain.com/api/v1`
- URL: `https://api.purplegrain.com/api/v1/compliance-exports`
- Auth: HTTP bearer

Queues the build of one zip (COMPLIANCE_EXPORT.md §2): `manifest.json` at the root and one
folder per property holding `mob_register.csv`, `husbandry.csv`, `paddock_register.csv`,
`field_work.csv`, `tasks.csv`, `weather.csv` (each only when it has rows) and
`photos/<record_id>_<n>.<ext>` as original files. UTF-8 with BOM, CRLF, RFC 4180; ISO dates
in Australia/Melbourne; an empty cell for anything the record does not hold; enumerations as
API values; `;` between multiple values; rows ordered property, asset name, date, product
position. Derived dates are copied from the records' own derived fields; `wool_safe_on` is
the one computed column and the manifest says so.

Answers at once with the export (`status: queued`); poll `GET /compliance-exports/{id}` for
`progress` and `status`, then download `file_url`. A build that takes longer than 20 seconds
also emails the requester a link when it is done. The zip is kept 30 days (`expires_at`); the
row stays as the log of who exported what, when, with the manifest's SHA-256.

Read-only: nothing is written but the export row. One owner, the same rule as the preview
(`organization_id`, blank for the caller's own account). A property id that owner does not
own is a 422 and nothing is created. A property of that owner the caller cannot read is
excluded and reported with its 403 detail; when every such property is refused the answer
is a 403 carrying each detail and nothing is created. First-party web and mobile only.

## Request body

- ComplianceExportRequest: What to export. Flat JSON body (`application/json`); there is no JSON:API form. One owner: `organization_id`, or the caller's own account when it is blank.
  - `organization_id` string, nullable: The organisation this export is for. Blank (omitted or null) is the caller's own account. The server stores it on the export. `properties: all`, and a missing `properties`, is every property of that one owner — never every organisation the caller belongs to, and never the personal account beside an organisation. An id that owner does not own is rejected.
  - `properties` union: `"all"` (every property of `organization_id`, or of the caller's own account when `organization_id` is blank) or a list of property ids belonging to that same owner. Omitted means `"all"`.
    - 'all'
    - string[]
  - `scope` ComplianceExportScope: Within the property. `property` (the default) is everything filed on it; `block` is the live paddocks of one block; `paddocks` and `mobs` are the chosen ones. For a block or paddocks, `include_mobs_in_paddocks` (default true) adds the mobs currently in those paddocks. A kind other than `property` is only valid with exactly one property.
    - `kind` 'property' | 'block' | 'paddocks' | 'mobs'
    - `ids` string[]: The block (one id), paddocks or mobs, all on the property. Empty for `property`.
    - `include_mobs_in_paddocks` boolean
  - `from` string, date: First record date (the Melbourne day the work was done). Defaults to twelve months before `to`.
  - `to` string, date: Last record date. Defaults to today in Melbourne.
  - `record_types` string[]: Which kinds to export. Defaults to every kind. `general` is a completed task with no typed detail.
  - `include_open` boolean: Also export open tasks that already carry drafted details, each row marked `status = open`; the zip name ends `-incl-open`.

## Response `201`

Queued.

- ComplianceExportResponse
  - `export` ComplianceExport, required: An export and its build. `status` runs queued → building → ready (or failed); `progress` is tasks read of tasks in scope; `file_url` is a signed download, good for 15 minutes without a bearer, set while the zip is there to download and null before it is ready or after `expires_at`; `pagination` says how the records were read and whether every page came back. `emailed_to` is the masked address the ready email went to, when the build took longer than 20 seconds.
    - `id` string, required
    - `status` 'queued' | 'building' | 'ready' | 'failed', required
    - `progress` object, required
      - `done` integer, required
      - `total` integer, required
    - `zip_name` string, required: `purplegrain-compliance-<Melbourne time, minute>-<property slug|all-properties>[-incl-open].zip`. When the caller already has an export of that name, the six-hex suffix of this export's id is added before `.zip`, so two exports never share a name.
    - `parameters` ComplianceExportRequest, required: What to export. Flat JSON body (`application/json`); there is no JSON:API form. One owner: `organization_id`, or the caller's own account when it is blank.
      - `organization_id` string, nullable: The organisation this export is for. Blank (omitted or null) is the caller's own account. The server stores it on the export. `properties: all`, and a missing `properties`, is every property of that one owner — never every organisation the caller belongs to, and never the personal account beside an organisation. An id that owner does not own is rejected.
      - `properties` union: `"all"` (every property of `organization_id`, or of the caller's own account when `organization_id` is blank) or a list of property ids belonging to that same owner. Omitted means `"all"`.
        - 'all'
        - string[]
      - `scope` ComplianceExportScope: Within the property. `property` (the default) is everything filed on it; `block` is the live paddocks of one block; `paddocks` and `mobs` are the chosen ones. For a block or paddocks, `include_mobs_in_paddocks` (default true) adds the mobs currently in those paddocks. A kind other than `property` is only valid with exactly one property.
        - `kind` 'property' | 'block' | 'paddocks' | 'mobs'
        - `ids` string[]: The block (one id), paddocks or mobs, all on the property. Empty for `property`.
        - `include_mobs_in_paddocks` boolean
      - `from` string, date: First record date (the Melbourne day the work was done). Defaults to twelve months before `to`.
      - `to` string, date: Last record date. Defaults to today in Melbourne.
      - `record_types` string[]: Which kinds to export. Defaults to every kind. `general` is a completed task with no typed detail.
      - `include_open` boolean: Also export open tasks that already carry drafted details, each row marked `status = open`; the zip name ends `-incl-open`.
    - `properties` ComplianceExportProperty[], required
      - `property_id` string, required
      - `name` string, nullable
      - `pic` string, nullable
      - `folder` string
      - `access` 'ok' | 'denied', required
      - `code` string
      - `detail` string
    - `counts` ComplianceExportCounts, required: Rows per file, and photos.
      - `tasks` integer, required
      - `mob_register_rows` integer, required
      - `husbandry_rows` integer, required
      - `paddock_register_rows` integer, required
      - `field_work_rows` integer, required
      - `weather_rows` integer, required
      - `photos` integer, required
    - `missing_fields` object, nullable, required: The manifest's missing fields, counts only, per register and field.
    - `manifest_sha256` string, nullable, required
    - `created_at` string, date-time, required
    - `ready_at` string, date-time, nullable, required
    - `expires_at` string, date-time, nullable, required
    - `file_url` string, nullable, required: Where to download the zip, relative to the API host: `/api/v1/compliance-exports/{id}/file?sig=<token>`. The signed token works with no bearer (open it in a browser) for 15 minutes from this read, and every read mints a new one. A credential: do not log or share it. Null before the zip is ready and after `expires_at`.
    - `error` string, nullable, required
    - `emailed_to` string, nullable, required
    - `pagination` object, nullable, required
      - `pages_fetched` integer
      - `page_size` integer
      - `complete` boolean

## Other responses

- `400`: As for the preview.
- `401`: Unauthenticated
- `403`: No accepted role in `organization_id`, no properties for this owner, every property of this owner was refused, or an assistant / API key. Nothing is created.
- `422`: A property id that does not belong to the chosen owner. Nothing is created. The detail names no property.

## Changes

- **2026-10-24** `e285768a8f1d`: 24 warning
  - added the new `map_boundaries` enum value to the `_meta/onboarding/completed_steps/items/` response property for the response status `400` (media type: application/json)
  - added the new `map_boundaries` enum value to the `_meta/onboarding/completed_steps/items/` response property for the response status `400` (media type: application/vnd.api+json)
  - added the new `map_boundaries` enum value to the `_meta/onboarding/completed_steps/items/` response property for the response status `422` (media type: application/json)
  - added the new `map_boundaries` enum value to the `_meta/onboarding/completed_steps/items/` response property for the response status `422` (media type: application/vnd.api+json)
  - …20 more
- **2026-10-24** `92f716edc989`: 16 info
  - added the optional property `_meta` to the response with the `400` status (media type: application/json)
  - added the optional property `_meta` to the response with the `400` status (media type: application/vnd.api+json)
  - added the optional property `_meta` to the response with the `422` status (media type: application/json)
  - added the optional property `_meta` to the response with the `422` status (media type: application/vnd.api+json)
  - …12 more
- …earlier changes not shown

[Operation history](https://skmtc.dev/purplegrain/apis/purple-grain-api/changes/compliance-exports/post.md)

---

[API](https://skmtc.dev/purplegrain/apis/purple-grain-api.md) - [All operations](https://skmtc.dev/purplegrain/apis/purple-grain-api/llms.txt) - [OpenAPI document](https://skmtc.dev/purplegrain/apis/purple-grain-api/revisions/7c60f959fec9?raw)
