# Public Orders List

Cursor-paginated list of orders for the authed organisation.

Endpoint: GET /orders
Version: 1.79.2

## Query parameters:

  - `cursor` (string | null)
    Opaque cursor from prior page

  - `modified_since` (string | null)
    ISO-8601 lower bound on `updated_at`. Applied only on the first page; the cursor is a strict super-set thereafter.

  - `completed_since` (string | null)
    ISO-8601 lower bound on `completed_at`. When set, returns the **completion feed**: only orders completed at/after this instant, ordered by `completed_at` — e.g. everything completed in the last 5 minutes. `completed_at` is refreshed on every completion, so a re-exported order reappears. Keep this parameter present on every page of the feed (its value is ignored once a cursor is supplied).

  - `limit` (integer | null)

## Header parameters:

  - `authorization` (string | null)
    Bearer <mrc_live_…> API key

## Response 200:

  - `200` (unknown)
    Successful Response

## Response 200 fields (application/json):

  - `data` (array, required)

  - `data.id` (string, required)
    Public id; equals `request_id` for an order.

  - `data.request_id` (string, required)
    Mercura request id backing this order.

  - `data.status` (string, required)
    Lifecycle state of an order (open enum — new values are SemVer MINOR).
A translated subset of the internal `OrderWorkflowStatus` (see README
→ Design decisions → Public offer status as a translated subset). The
internal `INCOMPLETE_METADATA` is folded into `NEEDS_REVIEW` — both
mean "a human still has to act" — so the public surface exposes a single
action-required state. The mapping lives in
`services.orders_service._ORDER_STATUS_MAP` and a test asserts it
covers every internal value, so a new internal state is a loud failure
rather than a silent passthrough.
    Enum: "PROCESSING", "NEEDS_REVIEW", "READY_TO_EXPORT", "EXPORT_FAILED", "EXPORTED", "ARCHIVED"

  - `data.completed_at` (string | null)
    When the order was last completed (finalised / exported). Refreshed on every completion; null if never completed. Use it with the `GET /orders?completed_since=…` completion feed.

  - `data.customer` (object)
    Customer the offer was made to. Sourced from `Request.customer_bp`.
On the wire the customer's partner-id is named `customer_id` — the
same field name the customers resource uses, so a partner ingesting
an offer can resolve the reference via `GET /customers/{customer_id}`.
The Python attribute is still `external_id` to keep the shared
projection (`features/customer_offers/offer_projection.py`)
indifferent to the rename. `serialize_by_alias=True` plus the
field's `serialization_alias` make every JSON path (FastAPI
response, `model_dump(mode='json')` in the webhook emitter) emit
`customer_id`.

  - `data.customer.customer_id` (string | null)
    Partner-side customer id — the same value the customers resource exposes as `customer_id` (stored internally as `BusinessPartner.external_id`).

  - `data.customer.name` (string, required)

  - `data.customer.address` (object)
    Postal address attached to a customer business partner.
Distinct from `schemas.common.Address` — no `kind` / `is_default`
fields, because an offer's customer address is the single resolved
address that went on the document. All fields nullable to match the
underlying `address_table` (street can be NULL on minimally
populated rows). No `region` field — the internal address model
doesn't carry one today; adding it later is MINOR additive.

  - `data.customer.address.street` (string | null)

  - `data.customer.address.street_number` (string | null)

  - `data.customer.address.postal_code` (string | null)

  - `data.customer.address.city` (string | null)

  - `data.customer.address.country` (string | null)
    ISO 3166-1 alpha-2 country code

  - `data.customer.contact_person` (string | null)
    Free-text contact person on the request, e.g. 'Hans Müller'.

  - `data.customer.custom_fields` (object | null)
    Organisation-defined custom fields for this customer, keyed by the field's display **label** (not the internal column id). Null when the customer carries no custom fields.

  - `data.project` (object)
    Project / object the offer relates to. Sourced from `Request.project`.

  - `data.project.object_number` (string | null)

  - `data.project.name` (string | null)

  - `data.project.custom_fields` (object | null)
    Organisation-defined custom fields for the project, keyed by the field's display **label**. Null when the project carries none.

  - `data.positions` (array)

  - `data.positions.position_number` (string, required)
    Display number (request-scoped, e.g. '01.10').

  - `data.positions.article_number` (string | null)

  - `data.positions.description` (string)

  - `data.positions.quantity` (number | null)

  - `data.positions.unit` (string | null)

  - `data.positions.list_price` (string | null)
    Per-unit list price (gross of discounts), EUR.

  - `data.positions.net_price` (string | null)
    Per-unit net price after position-level discounts, EUR. Equivalent to `Selection.net_price_ea` in the internal model.

  - `data.positions.line_net` (string | null)
    `net_price * quantity`, rounded to 2 dp. Same math as the PDF.

  - `data.positions.line_gross` (string | null)
    `list_price * quantity`, rounded to 2 dp. Same math as the PDF.

  - `data.positions.is_alternative` (boolean)
    If true, this row is an alternative selection — excluded from totals.

  - `data.positions.delivery_schedule` (array)
    Ordered requested delivery allocations; empty when the line has none.

  - `data.positions.delivery_schedule.quantity` (string, required)
    Quantity requested for this delivery date.

  - `data.positions.delivery_schedule.unit` (string, required)
    Unit for the scheduled quantity.

  - `data.positions.delivery_schedule.requested_delivery_date` (string, required)
    Requested delivery date.

  - `data.totals` (object, required)
    Aggregate amounts for the offer, all in EUR.
Mirrors `pdf_offer_service._build_real_data`: `positions_subtotal`
is the sum of `line_net` across non-alternative positions;
`net_total` prefers `Request.net_total` if set (header-level
discount applied), otherwise equals `positions_subtotal`;
`gross_total` prefers `Request.gross_total` if set, otherwise
is the sum of `line_gross` across non-alternative positions.

  - `data.totals.positions_subtotal` (string, required)
    Sum of `line_net` across non-alternative positions, EUR.

  - `data.totals.additional_discount_percentage` (string | null)
    Header-level discount applied on top of position prices, 0–100.

  - `data.totals.net_total` (string, required)
    Final net (after additional discount), EUR.

  - `data.totals.gross_total` (string, required)
    Sum of `line_gross` across non-alternative positions, EUR.

  - `data.order_metadata` (object)
    Order-level metadata extracted from the order document.
Sourced from the internal `OrderMetadata` row (one-to-one with the
order). Internal-only fields (extraction confidence, the redundant
`extracted_*` / `customer_address_*` blocks that duplicate the
resolved `customer`) are deliberately dropped. All fields nullable —
an order that is still parsing may carry none.

  - `data.order_metadata.order_date` (string | null)

  - `data.order_metadata.customer_reference` (string | null)
    The customer's own order reference / PO number.

  - `data.order_metadata.quote_number` (string | null)

  - `data.order_metadata.project_number` (string | null)

  - `data.order_metadata.requested_delivery_date` (string | null)

  - `data.order_metadata.shipping_terms` (string | null)

  - `data.order_metadata.contact_person` (string | null)
    Contact at the ordering customer, e.g. 'Hans Müller'.

  - `data.order_metadata.special_instructions` (string | null)

  - `data.order_metadata.additional_metadata` (object)
    Organisation-defined metadata keyed by the configured API field name. Legacy fields without one remain keyed by their label.

  - `data.order_metadata.delivery_recipient` (string | null)
    Goods-recipient name (Warenempfänger) as extracted from the document, when different from the customer. Free text and without an account number — the resolved recipient partner, when there is one, is the order's `recipient` block.

  - `data.erp_offer_id` (string | null)
    The partner-side ERP order id, when known — from your acknowledgement's `external_id` or a previous Mercura-managed export; otherwise null.

  - `data.acknowledgement` (object)
    The partner's last reported ERP import outcome for an order.
Used in two places with the same shape:
- as the `acknowledgement` field on `PublicOrderOut` — `null` until
the partner has POSTed an acknowledgement;
- as the standalone response body of
`POST /orders/{order_id}/acknowledgements` — the recorded receipt, so
partners can confirm what Mercura persisted (resolved status, sticky-aware
`external_id`, server-side `acknowledged_at`) without refetching the
whole order.

`status` / `message` / `metadata` reflect the most recent
acknowledgement; `external_id` is kept once provided (last-provided-wins)
and mirrors the order's `erp_offer_id`.
    Example: {"acknowledged_at":"2026-07-08T09:06:00Z","external_id":"SO-2026-5567","message":"Bestellung als Kundenauftrag angelegt.","metadata":{"sales_order":"SO-2026-5567"},"status":"SUCCESS"}

  - `data.acknowledgement.status` (string, required)
    Outcome a partner reports after importing an order into their ERP.
Its own type (parallel to `PublicTenderAckStatus` / the deprecated
`PublicOfferAckStatus`) so the orders surface stays self-contained.
Translated from the internal `RequestAckStatus` via
`orders.service._ORDER_ACK_STATUS_MAP` — a test asserts the map covers
every internal value, so a new internal state is a loud failure rather than
a silent passthrough. A *separate axis* from `PublicOrderStatus`: that is
Mercura-side workflow, this is the partner's import result.
    Enum: "SUCCESS", "FAILED"

  - `data.acknowledgement.external_id` (string | null)
    Partner-side ERP id of the created order / record, when provided.

  - `data.acknowledgement.message` (string | null)
    Free-text note about the outcome. On `FAILED` this is the failure reason (required); on `SUCCESS` an optional note (e.g. queue id, warehouse, operator comment).

  - `data.acknowledgement.metadata` (object | null)
    Optional extra references / raw payload the partner echoed back for traceability.

  - `data.acknowledgement.acknowledged_at` (string, required)
    When the partner reported this outcome (server receipt time).

  - `data.created_at` (string, required)

  - `data.updated_at` (string, required)

  - `next_cursor` (string | null)

## Response 422:

  - `422` (unknown)
    Validation Error

## Response 422 fields (application/json):

  - `detail` (array)

  - `detail.loc` (array, required)

  - `detail.msg` (string, required)

  - `detail.type` (string, required)

  - `detail.input` (any)

  - `detail.ctx` (object)

