# Uniorder: eine API für jede Sendung

Uniorder ist eine einheitliche Gruppe von Endpunkten, über die eine Integration jede Sendung des Kontos anfragt, anlegt, druckt, verfolgt und storniert, unabhängig davon, wie die Sendung ausgeführt wird. Ein Angebot liefert die Zustellung durch das Unternehmen selbst und auf Anforderung jeden Label-Service der Carrier des Kontos, jeweils mit einer `rate_id`. Der Auftrag wird angelegt, indem die gewählte `rate_id` zurückgesendet wird; kein anderer Teil der Anfrage wählt den Service.

## 1. Was Sie damit bauen können

- **Einen Checkout, der alle Versandoptionen auf einmal anbietet.** Der Kunde gibt eine Adresse ein, der Checkout ruft einen Endpunkt auf, und die Seite listet die lokale Zustellung neben UPS, Canada Post und jedem weiteren Carrier des Kontos, jeweils mit Preis.
- **Einen Konnektor für Auftragsverwaltung oder ERP mit einem einzigen Codepfad.** Aufträge aus allen Kanälen durchlaufen dieselben Aufrufe zum Anlegen, Abrufen, für Label, Tracking und Stornierung. Der Konnektor benötigt keine getrennte Logik für lokale Zustellung und für Carrier-Labels.
- **Nächtliche Massenverarbeitung.** Bis zu 500 Sendungen werden in einem eingereihten Job angefragt oder angelegt, und die Ergebnisse werden über die Job-ID abgerufen.
- **Eine Kundendienstansicht.** Ein Mitarbeiter sucht einen Auftrag, druckt dessen Label erneut, liest die Tracking-Zeitleiste und storniert den Auftrag, mit denselben vier Aufrufen für jeden Auftrag.

## 2. Was Uniorder für Sie übernimmt

| Ohne Uniorder | Mit Uniorder |
|---|---|
| Eine API für lokale Zustellaufträge und eine andere für Carrier-Labels, jeweils mit eigenen Feldern und Antworten | Eine Anfragestruktur (`from_*`, `to_*`, `packages`) und eine Antwortstruktur für jeden Service |
| Die Integration entscheidet, welche Carrier-API aufgerufen wird | Das Angebot listet jeden Service; die `rate_id` des gewählten Tarifs entscheidet |
| Getrennte Endpunkte für Label, Tracking und Stornierung je Service | `GET /label`, `GET /tracking` und `POST /cancel` gelten für jeden Auftrag |
| Stapelanlage nur für lokale Zustellung verfügbar | Stapelangebot und Stapelanlage für jeden Service, synchron oder eingereiht |

Uniorder ersetzt die bestehenden Endpunkte nicht; diese bleiben verfügbar und unverändert. Uniorder ist der empfohlene Einstiegspunkt für eine neue Integration.

## 3. Übersicht der Endpunkte in der Reihenfolge ihrer Verwendung

| Schritt | Zweck | REST | GraphQL |
|---|---|---|---|
| 1 | Zugriffstoken abrufen | `POST /api/v1/user/login` | `userLogin` |
| 2 | Angebot für jeden Service anfragen | `POST /api/v1/uniorder/rate` | `uniorderRate` |
| 3 | Auftrag zum gewählten Tarif anlegen | `POST /api/v1/uniorder` | `uniorderCreate` |
| 4 | Label drucken | `GET /api/v1/uniorder/{orderId}/label` | `uniorderLabel` |
| 5 | Auftrag abrufen | `GET /api/v1/uniorder/{orderId}` | `uniorder` |
| 6 | Auftrag verfolgen | `GET /api/v1/uniorder/{orderId}/tracking` | `uniorderTracking` |
| 7 | Auftrag stornieren | `POST /api/v1/uniorder/{orderId}/cancel` | `uniorderCancel` |
| — | Ein Label kaufen, das beim Anlegen nicht gekauft werden konnte | `POST /api/v1/uniorder/{orderId}/label` | `uniorderPurchaseLabel` |
| — | Bis zu 20 Zeilen auf einmal anfragen oder anlegen | `POST /api/v1/uniorder/rate/batch`, `POST /api/v1/uniorder/batch` | `uniorderRateBatch`, `uniorderCreateBatch` |
| — | Bis zu 500 Zeilen einreihen | `POST /api/v1/uniorder/rate/batch-async`, `POST /api/v1/uniorder/batch-async`, `GET /api/v1/uniorder/jobs/{jobId}` | `uniorderRateBatchAsync`, `uniorderCreateBatchAsync`, `uniorderJob` |

[REST-Handbuch](/api/documentation#/paths/v1-uniorder-rate/post) · [GraphQL-Handbuch](/api/graphql/documentation#/orders/uniorderRate)

Die einzelnen Anfragen, Antworten und Prüfungen Schritt für Schritt enthält der Leitfaden **Angebot und Auftrag in einem Ablauf**.

## 4. Beispiel: ein Checkout mit allen Optionen

Ein Blumengeschäft in Montreal verkauft online. Im Checkout fragt es das Paket einmal an, einschließlich der Label-Carrier:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "quote_labels": true,
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

Die Antwort listet einen `self_delivery`-Tarif und je einen `label_service`-Tarif pro Carrier-Service. Der Checkout zeigt sie als Optionen an; der Kunde wählt die lokale Zustellung am selben Tag. Der Auftrag wird mit der `rate_id` dieses Tarifs und denselben Adressen und Paketen angelegt:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: checkout-10045" \
  -d '{
    "rate_id": "eyJpdiI6Ik1rT2Z...",
    "ref": "WEB-10045",
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

Die Antwort liefert die Auftrags-`id` und die Tracking-Nummern. Der Shop druckt das Label mit `GET /api/v1/uniorder/{orderId}/label` und zeigt die Zeitleiste aus `GET /api/v1/uniorder/{orderId}/tracking` auf der Auftragsseite des Kunden an.

## 5. Beispiel: ein ERP, das jede Nacht versendet

Ein ERP exportiert die Aufträge des Tages um 22:00 Uhr. Es sendet die Adressen an `POST /api/v1/uniorder/rate/batch-async`, liest den Job, bis `status` den Wert `done` hat, wählt für jede Zeile nach eigenen Regeln einen Tarif und sendet die gewählten Zeilen an `POST /api/v1/uniorder/batch-async`. Jedes Ergebnis enthält die `reference` der Zeile, sodass das ERP jedes Ergebnis seiner eigenen Auftragszeile zuordnet. Eine `rate_id` ist 30 Minuten gültig; der Anlage-Job wird daher kurz nach Abschluss des Angebots-Jobs gesendet.

## 6. Beispiel: eine Kundendienstansicht

Bei einem Kundenanruf ruft die Ansicht des Mitarbeiters `GET /api/v1/uniorder/{orderId}` für Status und Adressen auf, `GET /api/v1/uniorder/{orderId}/tracking` für die Zeitleiste und den Zustellnachweis und `POST /api/v1/uniorder/{orderId}/cancel`, wenn der Kunde storniert. Dieselben Aufrufe gelten für einen Zustellauftrag und einen Label-Auftrag; das Feld `type` unterscheidet sie.

```bash
curl https://YOUR_HOST/api/v1/uniorder/123456/tracking \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

Derselbe Auftrag über GraphQL ([GraphQL-Handbuch](/api/graphql/documentation#/orders/uniorder)):

```graphql
query {
  uniorder(order_id: 123456)
}
```

## 7. Regeln für den Entwurf

- **Die `rate_id` bestimmt den Service.** Sie ist 30 Minuten gültig und nur für das Konto, das das Angebot angefragt hat.
- **Der Auftrag wird beim Anlegen bepreist.** `shipping_price` ist der berechnete Preis; `quoted_price` ist der Preis des Angebots. Beide können voneinander abweichen.
- **Ein Label-Auftrag geht nie verloren.** Kann das Label beim Anlegen nicht gekauft werden, bleibt der Auftrag erhalten, und die Antwort ist `LABEL_PURCHASE_FAILED` mit der Auftrags-`id`; das Label wird später mit `POST /api/v1/uniorder/{orderId}/label` gekauft.
- **Wiederholungen sind sicher.** Senden Sie bei jedem Anlage-Aufruf einen `Idempotency-Key`-Header; eine Stornierung eines bereits stornierten Auftrags liefert `already_cancelled` `true`.
- **Die Status sind einheitlich.** Ein Zustellauftrag meldet `pending`, `in_transit`, `out_for_delivery`, `delivered`, `exception` oder `cancelled`; ein Label-Auftrag meldet `label_pending`, `label_purchased` oder `cancelled`, und das Tracking seines Carriers meldet, wo sich das Paket befindet.

## 8. Checkliste für den Produktivstart

- [ ] Das Konto hat API-Berechtigung, und das Token ist auf dem Server gespeichert, nicht in einem Browser.
- [ ] Angebote werden mit vollständigen Absender- und Empfängeradressen angefragt.
- [ ] Aufträge werden innerhalb von 30 Minuten nach dem Angebot mit einem `Idempotency-Key` angelegt.
- [ ] `LABEL_PURCHASE_FAILED` wird behandelt, indem das Label später gekauft wird, niemals durch erneutes Anlegen des Auftrags.
- [ ] Das Tracking wird über `GET /api/v1/uniorder/{orderId}/tracking` gelesen oder über Webhooks empfangen.
- [ ] Die Stornierung behandelt `ORDER_STATUS_NOT_CANCELLABLE` und `ORDER_CANCEL_REFUSED`, indem der Auftrag unverändert bleibt.
