# Bestellungen

> Erstellen Sie Kundenaufträge, erfassen Sie die Erfüllung und Korrekturen und fügen Sie optionales Lieferverfolgung hinzu.

Käufer platzieren Bestellungen in ihrem Handelsportal. Lieferanten bearbeiten die Rechnungen. Sie können optional [Stripe-hosted Kunden-Zahlungen](/docs/guides/customer-payments) nach Bestätigung des Endbetrags anbieten. Bestellanfragen und Handelskredit bleiben verfügbar. Nutzen Sie die API, um Bestellungen in Ihr ERP-System oder Buchhaltungssystem zu laden und deren Status aktuell zu halten.

## Bestellstatus

| Status         | Bedeutung                                  |
| -------------- | ------------------------------------------ |
| `new`          | Platziert                                  |
| `pending`      | Auf Halteposition                          |
| `acknowledged` | Gesehen und wird ausgewählt                |
| `processed`    | Bereit zur Versendung                      |
| `dispatched`   | Von dem Lieferanten versendet              |
| `completed`    | Lieferant hat abgeschlossen                |
| `cancelled`    | Storniert mit einem kundensichtbaren Grund |

Mitarbeiter können Zwischenschritte beim Auswählen überspringen. Das Zurückgehen oder Neustarten einer abgeschlossenen oder stornierten Bestellung erfordert einen internen `correctionReason`. Die vorherige Geschichte bleibt erhalten. Korrekturen verändern niemals den Zahlungsstatus. Lesen Sie `revision` und senden Sie `expectedRevision` mit Updates, um veraltete Änderungen abzulehnen.

## Eine Bestellung für einen Kunden erstellen

**POST** `/api/v1/orders/preview`

Erfordert `orders.create` und Zugriff auf den Kunden. Senden Sie `customerAccountId` und `lines`, die `productId` und eine ganze Zahl `qty` (1 bis 1.000.000) enthalten. Wählen Sie jedes gewöhnliche Produkt maximal 100 Mal pro Zeile. Eine Zeile kann optionale `configuration` enthalten, die die zurückgegebene Messung und Auswahl-Schlüssel enthalten; verschiedene Konfigurationen eines Produkts bleiben getrennt. Ein optionaler `lineKey` gibt jeder Vorschauzeile eine stabile Caller-Identität. Ein zusätzlicher `additionalDiscountBp` benötigt Preisberechtigung und `discountReason`. Konfigurierte Bestell-Lesungen enthalten ein unveränderliches `configuration`-Snapshot mit Eingaben, Spezifikation, Version, Rundung und Gebühren. Die Vorschau gibt `total`, `fingerprint`, aktuelle Kundenpreise und Währung zurück. Angehaltene Konten, nicht verfügbare Produkte und Mengen unter dem Minimum werden abgelehnt. Ein Preis-Katalog allein akzeptiert weiterhin eine Bestellung, die hier eingegeben wird; Käufer können keine selbstständig senden, solange dies deaktiviert ist.

**POST** `/api/v1/orders`

Senden Sie denselben Kunden und Zeilen, `expectedTotal` aus der Vorschau, `expectedBasket` auf seinen Fingerabdruck gesetzt und eine neue UUID `requestKey`. Optionale `poNumber` (60 Zeichen) und kundensichtbare `notes` (2.000 Zeichen) werden unterstützt. Wiederholen Sie genau dasselbe Anfrage und Schlüssel nach einer unklaren Antwort: ein erfolgreicher Wiederholungsversuch gibt die ursprüngliche Bestellung zurück, anstatt eine weitere zu erstellen. Geänderte Preise erfordern eine weitere Überprüfung. Das Ergebnis ist `placed` mit `orderId` und `reference`, oder `prices_changed`, `empty` oder `unavailable`.

Mitarbeiter handeln unter ihrer eigenen Identität. Der Warenkorb des Kunden bleibt unverändert. Kunden-E-Mail-Kontakte können eine Kopie erhalten; dies verursacht keine Zahlung. Der Verkäufer hat standardmäßig Erstellungsrechte; Verarbeitungs- und Lieferupdates erfordern die separate `orders.process`-Berechtigung.

## Lieferung und Verfolgung

**PATCH** `/api/v1/orders/{id}/delivery`

Erfordert `orders.process`. Senden Sie `deliveryMethod`: `unspecified`, `courier`, `own_delivery` oder `collection`. Optionale `courierName` (100 Zeichen), `trackingReference` (120 Zeichen) und `trackingUrl` (1.000 Zeichen) sind kundensichtbar. Tracking-Links müssen HTTPS verwenden, ohne eingebettete Anmeldeinformationen. Weggelassene optionale Details löschen sie. Senden Sie `expectedRevision` aus der aktuellen Bestellung. Es gibt einen Lieferprotokoll pro Bestellung; Carrier-Integrationen und geteilte Sendungen sind nicht enthalten. Leere Lieferdetails bedeuten niemals eigene Lieferung.

## Bestellungen auflisten

**GET** `/api/v1/orders`

Neueste Bestellungen zuerst. Die Liste umfasst die **200 neuesten** Bestellungen, die Ihren Filtern entsprechen. Um neue Bestellungen abzuholen, abfragen Sie mit `status=new`.

| Name       | Type    | Required | Beschreibung                         |
| ---------- | ------- | -------- | ------------------------------------ |
| `status`   | string  | nein     | Einer der oben genannten Status.     |
| `customer` | string  | nein     | Kunden-ID oder Kontocodierung.       |
| `limit`    | integer | nein     | 1 bis 100. Standardwert 100.         |
| `offset`   | integer | nein     | Zeilen überspringen. Standardwert 0. |

```bash
curl "https://tradecatalog.app/api/v1/orders?status=new" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/orders?status=new', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: orders } = await res.json()
```

```json
{
  "data": [
    {
      "id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
      "reference": "SO-000142",
      "status": "new",
      "total": 31460,
      "currency": "GBP",
      "poNumber": "PO-88231",
      "createdAt": "2026-09-27T07:48:12.000Z",
      "customer": "Oakfield Joinery"
    }
  ],
  "nextOffset": null
}
```

| Feld        | Typ                    | Beschreibung                                                                              |
| ----------- | ---------------------- | ----------------------------------------------------------------------------------------- |
| `id`        | Zeichenfolge           | Bestellnummer.                                                                            |
| `reference` | Zeichenfolge           | Bestellnummer, die Käufer sehen, wie `SO-000142`.                                         |
| `status`    | Zeichenfolge           | Siehe [Status](#order-statuses).                                                          |
| `total`     | Ganzzahl               | Gesamtbetrag der Bestellung nach Preis (siehe `taxLabel`), in Kleinheiten von `currency`. |
| `currency`  | Zeichenfolge           | Festgelegt, als die Bestellung aufgegeben wurde.                                          |
| `poNumber`  | Zeichenfolge oder null | Die Bestellnummer des Käufers.                                                            |
| `createdAt` | Zeichenfolge           | Wann wurde sie aufgegeben.                                                                |
| `customer`  | Zeichenfolge           | Name des Kunden. Holen Sie sich die Kontokennung aus `GET /orders/{id}`.                  |

## Einen Auftrag abrufen

**GET** `/api/v1/orders/{id}`

Der Auftrag mit seinen Zeilen und Statusverlauf.

| Name | In   | Typ          | Erforderlich | Beschreibung   |
| ---- | ---- | ------------ | ------------ | -------------- |
| `id` | Pfad | Zeichenfolge | ja           | Bestellnummer. |

```bash
curl https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: order } = await res.json()
```

```json
{
  "data": {
    "id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
    "customerAccountId": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
    "placedBy": "Fq3L0cZ7tN1pW9aYx2Rk",
    "reference": "SO-000142",
    "poNumber": "PO-88231",
    "notes": "Deliver to the Hebden Road site, gate 2.",
    "status": "new",
    "cancelReason": null,
    "currency": "GBP",
    "total": 31460,
    "createdAt": "2026-09-27T07:48:12.000Z",
    "updatedAt": "2026-09-27T07:48:12.000Z",
    "customer": { "name": "Oakfield Joinery", "accountCode": "OAK01" },
    "lines": [
      {
        "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "orderId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
        "productId": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
        "sku": "HX-8510",
        "name": "Hex bolt M10 x 50 zinc",
        "unitLabel": "Box of 100",
        "qty": 20,
        "unitPrice": 1573,
        "listPrice": 1850,
        "priceLabel": "Brand discount −15%",
        "lineTotal": 31460
      }
    ],
    "events": [
      {
        "status": "new",
        "note": null,
        "createdAt": "2026-09-27T07:48:12.000Z",
        "actor": "Sam Patel"
      }
    ]
  }
}
```

| Feld                                        | Beschreibung                                                                                                                                                       |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `customerAccountId`                         | Die ID des Kunden.                                                                                                                                                 |
| `placedBy`                                  | ID der Person, die sie eingegeben hat. `source` unterscheidet Käufer- und Mitarbeiterbestellungen.                                                                 |
| `notes`                                     | Die Liefer- oder Bestellnotizen des Käufers.                                                                                                                       |
| `cancelReason`                              | Warum wurde sie storniert, falls dies der Fall war.                                                                                                                |
| `lines`                                     | Was bestellt wurde und zu welchem Preis. Ein Snapshot: spätere Preisänderungen ändern dies nicht. `productId` ist `null`, wenn das Produkt seitdem gelöscht wurde. |
| `lines[].priceLabel`                        | Die Regel, die den Preis festgelegt hat, wie auf [Preise](/docs/api/prices#the-price-object).                                                                      |
| `lines[].priceLabel` ist `Price on request` | Das Produkt hat keinen Preis: `unitPrice` und `lineTotal` sind `0` und die Zeile ist nicht in `total`. Der Lieferant bestätigt seinen Preis.                       |
| `events`                                    | Statusverlauf, ältesten zuerst. `actor` ist der Name der Person oder `null`.                                                                                       |

## Auftrag aktualisieren oder korrigieren

**PATCH** `/api/v1/orders/{id}`

Ändert den Status einer Bestellung. Gibt die aktualisierte Bestellung in der gleichen Form wie `GET /orders/{id}` zurück.

- Normale Weiterbewegungen folgen den Erfüllungsstufen oben. Andere Bewegungen erfordern einen privaten Korrektionsgrund; eine wiederholte aktuelle Statusmeldung hat keine Auswirkung.
- Stornieren erfordert einen `note`, der dem Käufer als Grund angezeigt wird.
- Statusänderungen können E-Mails an Kundenkontakte senden, einschließlich der öffentlichen Notiz. Eine generierte E-Mail beweist keine Lieferung.
- Die Änderung wird im Bestellverlauf gegen die Person aufgezeichnet, die den Schlüssel erstellt hat.

| Name     | In     | Typ          | Erforderlich | Beschreibung                                        |
| -------- | ------ | ------------ | ------------ | --------------------------------------------------- |
| `id`     | Pfad   | Zeichenfolge | ja           | Bestellnummer.                                      |
| `status` | Körper | Zeichenfolge | ja           | Der neue Status.                                    |
| `note`   | Körper | Zeichenfolge | nein         | Bis zu 1.000 Zeichen. Erforderlich bei Stornierung. |

```bash
curl -X PATCH https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "dispatched", "note": "DPD tracking 1554 2210 0931" }'
```

```js
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ status: 'acknowledged' }),
})
const { data: order } = await res.json()
```

| Status | Code                 | Wann                                                                                                                                                     |
| ------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `invalid_request`    | Die Bewegung ist nicht erlaubt, z. B. „Eine Bestellung, die empfangen wurde, kann nicht als abgeschlossen markiert werden“, oder Stornierung ohne Notiz. |
| 403    | `insufficient_scope` | Der Schlüssel ist schreibgeschützt.                                                                                                                      |
| 404    | `not_found`          | Es gibt keine solche Bestellung in diesem Arbeitsbereich.                                                                                                |

Das Wiederholen eines `PATCH`, das bereits erfolgreich war, gibt `400 invalid_request` zurück, da der Auftrag weitergegangen ist. Prüfen Sie den Auftragsstatus, bevor Sie dies als Fehler behandeln.

## Nicht verfügbar

Die API kann keine Aufträge für einen Käufer platzieren oder die Zeilen, Mengen oder Preise eines Auftrags ändern. Käufer platzieren Aufträge in ihrem Handelsportal; Änderungen an einem Auftrag werden mit dem Kunden abgesprochen und in Ihrem eigenen System abgerechnet.

Auftragslisten enthalten auch `paymentStatus`: null für gewöhnliche Kontobedingungen oder den optionalen Stripe-Zahlungsstatus. Die Erfüllungsstatus ist separat und beweist niemals die Zahlung. Die Auftragsdetailsseite ist der Ort, um den endgültigen bestätigten Gesamtbetrag zu überprüfen und die Zahlung zu genehmigen. Die API kann keine Zahlung erstellen oder genehmigen.

`internalNote` (2.000 Zeichen) und `correctionReason` (1.000 Zeichen) sind Felder für das Personal. `note` bleibt öffentlich, einschließlich alter Notizen. Senden Sie `expectedRevision`, um eine gleichzeitige Bearbeitung mit einer ungültigen Anfrage abzulehnen. Staff-API-Lesungen enthalten private Geschichte; Käufer-Tools und Portal-Lesungen exkludieren die Identitäten des Personals und die privaten Felder. Das Stornieren erfordert immer einen öffentlichen Grund und die Rücknahme oder vollständige Erstattung einer ausstehenden Zahlung.
