> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.dyspach.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fulfilment activity structure

> The structure every fulfilment activity push uses, as JSON fields or file columns.

An **activity** is one warehouse charge event — a pick, a pack, a receipt, a storage
charge. It is what your customer's 3PL bills for.

There is a single published structure. You choose how to deliver it —
[as data](/api-reference/import-fulfilment-activities) or
[as a file](/api-reference/import-fulfilment-activities-file) — and the fields are the
same either way. In a file it is **one activity per row**; column order does not matter
and optional columns may be left out. A column we do not publish is rejected rather than
ignored.

<Note>
  **Starter template.** Copy this into a `.csv` and replace the example rows.

  ```csv template.csv theme={null}
  customerId,warehouseCode,chargeCode,chargeDescription,quantity,reference,additionalReference1,additionalReference2,additionalReference3,activityTimestamp,status
  ACME,SYD01,Picking,,3,ORD-598782,,,,2026-08-10T16:00:00Z,Approved
  ACME,SYD01,Packing,,1,ORD-598782,,,,2026-08-10T16:00:00Z,Approved
  ```
</Note>

## Fields

Four fields are required. They are exactly the four an activity cannot be imported
without, so anything missing is refused when you send it — named — rather than accepted
and thrown away minutes later.

<ParamField body="warehouseCode" type="string" required>
  Where the activity happened, as configured in Dyspach.
</ParamField>

<ParamField body="chargeCode" type="string" required>
  The charge being recorded, matching your customer's rate card — `Picking`, `Packing`,
  `Receipt - Unit`, and so on. Free text.
</ParamField>

<ParamField body="quantity" type="integer" required>
  Units of the charge. **Signed** — see below. `0` is not a quantity and is refused.
</ParamField>

<ParamField body="activityTimestamp" type="date-time" required>
  When it happened, ISO-8601. This decides which billing period the charge falls into.
</ParamField>

The rest are optional:

| Field                  | Type   | Notes                                                                                |
| ---------------------- | ------ | ------------------------------------------------------------------------------------ |
| `customerId`           | string | The customer this activity belongs to, using the customer code configured in Dyspach |
| `chargeDescription`    | string | Free text                                                                            |
| `reference`            | string | Your reference for the activity: order number, receipt number                        |
| `additionalReference1` | string |                                                                                      |
| `additionalReference2` | string |                                                                                      |
| `additionalReference3` | string |                                                                                      |
| `status`               | string | Your own status, recorded as sent                                                    |

<Note>
  On a file upload, all four required columns must be present in the header row. A row
  that leaves one of them **blank** is still refused, but per row, with the reason on the
  [errors endpoint](/api-reference/get-fulfilment-import-errors) — the file is not
  rejected whole because of one bad line.
</Note>

## Value formats

<AccordionGroup>
  <Accordion title="Quantity is signed">
    Negatives are expected, not an error — they are how credits and reversals are
    recorded. `-35` is as valid as `35`.
  </Accordion>

  <Accordion title="Timestamps must be ISO-8601">
    `2026-08-10T16:00:00Z`. This decides which billing period the charge falls into, so
    it is never inferred. Do not send `8/10/26` — that is August to some systems and
    October to others.
  </Accordion>

  <Accordion title="Every push appends">
    An activity has no identity: the same charge code, reference and timestamp can
    legitimately occur several times — four identical picks on one order in the same
    second is normal. So nothing is de-duplicated, and **sending a batch twice records it
    twice.** Each row is billable, so send a run once and check the import rather than
    retrying blindly.
  </Accordion>
</AccordionGroup>
