# Nabídka a objednávka v jednom toku

Tato příručka prochází API Uniorder (`/api/v1/uniorder/...`) požadavek po požadavku v pořadí, v jakém se integrace vytváří: přihlášení, cenová nabídka, vytvoření se zvoleným `rate_id`, tisk štítku, čtení, sledování a zrušení objednávky a zpracování zásilek v dávkách. Jedna cenová nabídka uvádí každý způsob, jakým může účet odeslat balík: doručení samotnou firmou a na požádání každou službu dopravců štítků. Objednání s `rate_id` vytvoří objednávku pro danou službu: objednávku doručení nebo objednávku štítku se štítkem koupeným u oceněné služby dopravce. Je určena vývojářům internetových obchodů, systémů pro správu objednávek a ERP, které odesílají přes firemní účet.

## 1. Co můžete vytvořit

Následující příklady sledují jednu firmu: **Fleurs du Plateau**, květinářství na adrese 4500 Rue Saint-Denis, Montreal (H2J 2L3), které prodává kytice online. Typickým balíkem je jedna krabice o hmotnosti 1,2 kg a rozměrech 40 × 25 × 25 cm pro Jane Recipient na adrese 6841 Rue Saint-Denis, Montreal (H2S 2S3), pod webovou objednávkou `WEB-10045`.

- **Pokladna, která nabízí všechny možnosti dopravy.** Obchod ocení balík jednou a zobrazí místní doručení v tentýž den vedle každé služby štítků dopravce na účtu, každou s cenou, a poté vytvoří objednávku pro možnost, kterou zákazník zvolil.
- **Automatický tisk štítků.** Po vytvoření objednávky obchod stáhne PDF štítku a pošle ho do tiskárny balicího pracoviště bez ohledu na to, zda balík doručuje firma nebo dopravce.
- **Stránka objednávky s aktuálním sledováním.** Stránka objednávky zákazníka zobrazuje stav a časovou osu událostí zásilky a po doručení kytice také doklad o doručení.
- **Noční dávka z ERP.** Velkoobchodní objednávky dne se ocení a vytvoří v jedné úloze ve frontě s nejvýše 500 řádky a každý výsledek se přiřadí ke svému řádku objednávky podle `reference`.

## 2. Co tato příručka pokrývá

Toto je podrobná příručka API Uniorder krok za krokem. Přehled toho, co Uniorder nabízí a proč, najdete v příručce **Uniorder: jedno API pro každou zásilku**; tato příručka uvádí požadavky, odpovědi a kontroly pro každé volání.

Uniorder je doporučeným jediným vstupním bodem pro nové integrace, které odesílají balíky místním doručením nebo se štítkem dopravce: nahrazuje samostatná volání API místního doručení a API štítků dopravce jedním tvarem požadavku. Starší endpointy popsané v příručkách **Vyzvednutí a doručení (vlastní flotila)** a **Štítky dopravce** zůstávají dostupné a nezměněné. Uniorder se nevztahuje na přepravní služby rezervované zákaznickým účtem ani na objednávky skladování a výdeje; pro ně použijte příručky **Přepravní služby** a **Skladování a výdej**.

## 3. Než začnete

- **Účet.** Použijte firemní (klientský) účet nebo zaměstnanecký účet firmy s oprávněním k API. Uniorder může volat také zákaznický účet firmy; cenová nabídka a vyúčtování se mu vždy vystavují na jeho vlastní jméno. Vytvoření objednávky doručení vyžaduje oprávnění k zadávání objednávek.
- **Zákazníci.** Klientský nebo zaměstnanecký účet může získat cenovou nabídku a objednávat pro jednoho ze svých zákazníků pomocí `customer_id` nebo `customer_code` v cenové nabídce; `rate_id` pak nese tohoto zákazníka a cena se řídí plánem zákazníka.
- **Služby štítků.** Pro získání sazeb `label_service` potřebuje účet (nebo uvedený zákazník) alespoň jeden nastavený účet dopravce štítků.
- **Testovací data.** Pro sazby `self_delivery` použijte adresu v oblasti doručení firmy a testovací reference, například `WEB-10045`, které lze poté zrušit.
- **Tokeny.** Přístupový token si vyžádejte ze svého serveru a uchovávejte ho tam. Nikdy ho neposílejte do prohlížeče ani do mobilní aplikace.
- **Zástupné hodnoty.** Nahraďte `YOUR_HOST` hostitelem API vašeho prostředí a `ACCESS_TOKEN` tokenem z kroku 4.

## 4. Přihlášení

Každé volání Uniorder se provádí jménem účtu. Přihlaste se jednou ze svého serveru, uložte vrácený token a posílejte ho při každém požadavku.

**REST:** `POST /api/v1/user/login` — [Příručka REST](/api/documentation#/paths/v1-user-login/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/user/login \
  -H "Content-Type: application/json" \
  -d '{"email":"orders@fleursduplateau.example","password":"your_password"}'
```

```json
{
  "result": true,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
}
```

- `access_token`: vložte ho do hlavičky každého dalšího požadavku:

```
Authorization: Bearer ACCESS_TOKEN
```

GraphQL používá stejnou hlavičku na `POST /api/graphql`.

**GraphQL:** `userLogin` ([Příručka GraphQL](/api/graphql/documentation#/user/userLogin))

**Ověření:** přihlášení vrátí `access_token`. Následující požadavky bez tohoto tokenu vrátí `401`.

## 5. Cenová nabídka pro každou službu

Cenová nabídka uvádí každý způsob, jakým lze balík odeslat, s cenou a `rate_id` pro každý z nich. Pokladna zobrazí sazby jako možnosti; nic se nevytvoří ani nerezervuje.

**REST:** `POST /api/v1/uniorder/rate` — [Příručka REST](/api/documentation#/paths/v1-uniorder-rate/post)

Odesílatel a příjemce jsou úplné adresy; volitelné jsou pouze `from_address_2` a `to_address_2`. Každý balík potřebuje `weight`, `length`, `width` a `height`. Nastavte `quote_labels` na `true`, chcete-li přidat služby dopravců štítků; jména a telefony obou stran jsou pak povinné. Klientský nebo zaměstnanecký účet může získat cenovou nabídku pro jednoho ze svých zákazníků pomocí `customer_id` nebo `customer_code`. Časové okno doručení (`time_window_start`, `time_window_end`, formát `YYYY-MM-DD HH:MM:SS`) se zohlední, pokud na něm závisí cena.

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "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_address_2": "Apt 2",
    "to_city": "Montreal",
    "to_province": "QC",
    "to_country": "CA",
    "to_postcode": "H2S2S3",
    "time_window_start": "2026-10-02 13:00:00",
    "time_window_end": "2026-10-02 17:00:00",
    "quote_labels": true,
    "packages": [{
      "weight": 1.2,
      "weight_unit": 2,
      "length": 40,
      "width": 25,
      "height": 25,
      "dimension_unit": 2
    }]
  }'
```

`weight_unit`: `1` g, `2` kg, `3` oz, `4` lb. `dimension_unit`: `1` mm, `2` cm, `3` m, `4` in.

```json
{
  "result": true,
  "rates": [
    {
      "rate_id": "eyJpdiI6Ik1rT2Z...",
      "type": "self_delivery",
      "channel_id": null,
      "shipping_price": "14.60",
      "currency": "CAD",
      "price_details": { "shipping_fee": 12.92, "sub_total": "12.92" },
      "warning": null
    },
    {
      "rate_id": "eyJpdiI6IlpxR0...",
      "type": "label_service",
      "shipping_price": "18.40",
      "currency": "CAD",
      "shipping_method_id": 72,
      "shipping_method_name": "UPS",
      "carrier_name": "ups",
      "service_code": "ups_standard",
      "service_name": "UPS STANDARD",
      "transit_days": 3
    }
  ],
  "errors": []
}
```

- `type` `self_delivery`: doručení firmou. Nejvýše jedna na cenovou nabídku.
- `type` `label_service`: jedna pro každou službu každého účtu štítků. Zákazníkovi zobrazte `service_name`, `shipping_price` a `transit_days`.
- `errors` uvádí, co se nepodařilo ocenit, s jeho `type`. Adresa mimo oblast doručení je chyba typu `self_delivery` s kódem `OUT_OF_DELIVERY_AREA`; zobrazte pouze služby štítků.
- `rate_id` platí 30 minut a pouze pro účet, který si cenovou nabídku vyžádal. Uchovávejte ho spolu s relací pokladny.
- `result` je `true`, pokud byla nalezena alespoň jedna sazba.

**GraphQL:** `uniorderRate` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderRate)). Odpovědí je JSON skalár, takže operace nemá selection set.

```graphql
mutation QuoteBouquet($packages: [Json]!) {
  uniorderRate(
    type: "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: $packages
  )
}
```

Proměnné:

```json
{ "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }] }
```

**Ověření:** `rates` obsahuje sazbu `self_delivery` pro adresu v oblasti a při `quote_labels` jednu sazbu `label_service` pro každou službu dopravce. Nic se nevytvoří.

## 6. Vytvoření objednávky za zvolenou sazbu

Když zákazník zaplatí, obchod vytvoří objednávku s `rate_id` zvolené možnosti a se stejnou zásilkou. Službu určuje `rate_id`; nic jiného v požadavku ji nevybírá.

**REST:** `POST /api/v1/uniorder` — [Příručka REST](/api/documentation#/paths/v1-uniorder/post)

Při každém vytvoření pošlete hlavičku `Idempotency-Key`, jedinečnou pro každou objednávku. Opakování se stejným klíčem a stejným tělem vrátí první odpověď s `replayed` `true` a nevytvoří druhou objednávku.

```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",
    "type": "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_email": "jane@example.com",
    "to_address": "6841 Rue Saint-Denis",
    "to_address_2": "Apt 2",
    "to_city": "Montreal",
    "to_province": "QC",
    "to_country": "CA",
    "to_postcode": "H2S2S3",
    "time_window_start": "2026-10-02 13:00:00",
    "time_window_end": "2026-10-02 17:00:00",
    "delivery_instruction": "Ring the bell at the side door.",
    "packages": [{
      "ref": "WEB-10045-1",
      "weight": 1.2,
      "weight_unit": 2,
      "length": 40,
      "width": 25,
      "height": 25,
      "dimension_unit": 2
    }]
  }'
```

Sazba `self_delivery` vytvoří objednávku doručení. U `type` `D` je zastávkou příjemce; nastavte `need_pick_up` na `1`, má-li být balík vyzvednut u odesílatele. U `type` `P` je zastávkou odesílatel.

```json
{
  "result": true,
  "type": "self_delivery",
  "id": 123456,
  "ref": "WEB-10045",
  "tracking_numbers": ["SR26092800001"],
  "shipping_price": "14.60",
  "quoted_price": "14.60",
  "currency": "CAD"
}
```

Sazba `label_service` vytvoří objednávku štítku a koupí štítek u oceněné služby dopravce. `type` musí být `D` a `from_name`, `from_telephone`, `to_name` a `to_telephone` jsou povinné. Pokud by si zákazník zvolil UPS STANDARD, odpověď by byla:

```json
{
  "result": true,
  "type": "label_service",
  "id": 123457,
  "ref": "WEB-10045",
  "tracking_numbers": ["SR26092800002"],
  "shipping_price": "18.40",
  "quoted_price": "18.40",
  "currency": "CAD",
  "label": {
    "carrier_name": "ups",
    "service_code": "ups_standard",
    "main_tracking_number": "1Z999AA10123456784",
    "label_status": "ready",
    "shipping_label": "JVBERi0xLjQK..."
  }
}
```

- `id`: uložte ho k webové objednávce; používá ho každé další volání.
- `tracking_numbers`: vlastní sledovací čísla zásilky, jedno na balík.
- `shipping_price`: účtovaná cena. Objednávka se ocení při vytvoření; `quoted_price` je cena z cenové nabídky. Tyto dvě ceny se mohou lišit.
- `label.main_tracking_number` a `label.shipping_label` (pouze objednávka štítku): sledovací číslo dopravce a PDF štítku v base64.
- `result` `false` s kódem `LABEL_PURCHASE_FAILED` (pouze objednávka štítku): objednávka existuje, ale nemá štítek. Uchovejte `id` a pokračujte krokem 11.

**GraphQL:** `uniorderCreate` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderCreate))

```graphql
mutation CreateBouquetOrder($packages: [Json]!) {
  uniorderCreate(
    rate_id: "eyJpdiI6Ik1rT2Z..."
    ref: "WEB-10045"
    type: "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"
    packages: $packages
  )
}
```

Proměnné:

```json
{ "packages": [{ "ref": "WEB-10045-1", "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }] }
```

**Ověření:** `result` je `true` a `id` je nastaveno. `rate_id`, jehož platnost vypršela nebo patří jinému účtu, vrátí `400` s kódem `RATE_ID_INVALID` a nic se nevytvoří.

## 7. Tisk štítku

Balicí pracoviště vytiskne štítek hned, jakmile objednávka existuje. Stejné volání vrátí vlastní štítek firmy pro objednávku doručení a koupený štítek dopravce pro objednávku štítku.

**REST:** `GET /api/v1/uniorder/{orderId}/label` — [Příručka REST](/api/documentation#/paths/v1-uniorder-orderId--label/get)

```bash
curl "https://YOUR_HOST/api/v1/uniorder/123456/label?hide_sender_address=0" \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

```json
{
  "result": true,
  "type": "self_delivery",
  "id": 123456,
  "format": "pdf",
  "tracking_numbers": ["SR26092800001"],
  "pdf_data": "JVBERi0xLjQK..."
}
```

- `pdf_data`: PDF štítku v base64. Dekódujte ho a soubor pošlete do tiskárny.
- `hide_sender_address`, `hide_receiver_address` (`1` pro skrytí): platí pro vlastní štítek firmy u objednávky doručení.
- `label_status` (objednávka štítku): `ready`, když je soubor vrácen. Pokud dopravce soubor ještě nevytvořil, odpovědí je `200` s `result` `false` a `label_status` `pending`; štítek si vyžádejte znovu později.
- Toto volání nikdy nekupuje štítek: štítek, který nebyl koupen, vrátí `409` s kódem `LABEL_PURCHASE_FAILED`. Kupte ho v kroku 11.

**GraphQL:** `uniorderLabel` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderLabel))

```graphql
query {
  uniorderLabel(order_id: 123456, hide_sender_address: 0)
}
```

**Ověření:** `result` je `true` a dekódované `pdf_data` se otevře jako PDF se sledovacím číslem objednávky.

## 8. Čtení objednávky

Obchod načte objednávku, aby zobrazil její stav, adresy a balíky na stránce objednávky nebo na obrazovce zákaznického servisu.

**REST:** `GET /api/v1/uniorder/{orderId}` — [Příručka REST](/api/documentation#/paths/v1-uniorder-orderId/get)

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

```json
{
  "result": true,
  "type": "self_delivery",
  "id": 123456,
  "ref": "WEB-10045",
  "order_type": "D",
  "status": "pending",
  "created_at": "2026-10-02 09:14:05",
  "time_window_start": "2026-10-02 13:00:00",
  "time_window_end": "2026-10-02 17:00:00",
  "from": { "name": "Fleurs du Plateau", "address": "4500 Rue Saint-Denis", "city": "Montreal", "postcode": "H2J2L3" },
  "to": { "name": "Jane Recipient", "address": "6841 Rue Saint-Denis", "address_2": "Apt 2", "city": "Montreal", "postcode": "H2S2S3" },
  "packages": [
    { "id": 998877, "ref": "WEB-10045-1", "tracking_number": "SR26092800001", "weight": 1.2 }
  ],
  "shipping_price": "14.60",
  "currency": "CAD"
}
```

- `type`: `self_delivery` nebo `label_service`; ostatní pole mají pro oba stejný tvar.
- `status`: `pending`, `in_transit`, `out_for_pickup`, `out_for_delivery`, `ready_for_self_pickup`, `delivered`, `exception` nebo `cancelled` pro objednávku doručení a `label_pending`, `label_purchased` nebo `cancelled` pro objednávku štítku.
- `label` (pouze objednávka štítku): dopravce, služba, `carrier_tracking_numbers` a `label_status` (`not_purchased`, `pending`, `ready` nebo `failed`).

**GraphQL:** `uniorder` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorder))

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

**Ověření:** objednávka vrátí svůj `status` a `packages` a `ref` odpovídá webové objednávce.

## 9. Sledování objednávky

Stránka objednávky zobrazuje časovou osu zásilky. Načtěte ji, když zákazník otevře stránku, nebo ji udržujte aktuální pomocí webhooků.

**REST:** `GET /api/v1/uniorder/{orderId}/tracking` — [Příručka REST](/api/documentation#/paths/v1-uniorder-orderId--tracking/get)

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

```json
{
  "result": true,
  "type": "self_delivery",
  "id": 123456,
  "status": "delivered",
  "tracking_numbers": ["SR26092800001"],
  "events": [
    { "code": "delivered", "description": "Delivered", "location": "Montreal", "time": "2026-10-02 15:42:10", "time_zone": "America/Toronto", "source": "shipper" }
  ],
  "proofs": [
    { "type": "photo", "url": "https://YOUR_HOST/storage/pod/123456.jpg", "uploaded_at": "2026-10-02 15:42:08" }
  ]
}
```

- `events`: časová osa od nejnovější události, každá s `code`, `description`, `location` a časem.
- `proofs`: soubory dokladu o doručení. Zobrazte je, když je `status` `delivered`.
- `carrier` (pouze objednávka štítku): název dopravce, sledovací číslo a odkaz na sledování (`tracking_url`).

**GraphQL:** `uniorderTracking` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderTracking))

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

**Ověření:** volání sledování vrátí `result` `true`, `status` objednávky a její `events`.

## 10. Zrušení objednávky

Když zákazník zruší webovou objednávku, obchod zruší zásilku stejným voláním pro objednávku doručení i pro objednávku štítku. Štítek se nejprve zneplatní u jeho dopravce.

**REST:** `POST /api/v1/uniorder/{orderId}/cancel` — [Příručka REST](/api/documentation#/paths/v1-uniorder-orderId--cancel/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/123456/cancel \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cancel-10045" \
  -d '{}'
```

```json
{
  "result": true,
  "type": "self_delivery",
  "id": 123456,
  "already_cancelled": false,
  "message": "The order has been cancelled."
}
```

- `already_cancelled`: `true`, když byla objednávka zrušena před tímto voláním. Považujte to za úspěch.
- Pokud objednávka není zrušena, odpovědí je `409` a objednávka zůstává nezměněná: `ORDER_STATUS_NOT_CANCELLABLE` (na zrušení je pozdě), `ORDER_CANCEL_REFUSED` (nyní ji nelze zrušit) nebo `LABEL_CANCEL_FAILED` (dopravce štítek nezneplatnil). Webovou objednávku ponechte otevřenou a zásilku řešte ručně.

**GraphQL:** `uniorderCancel` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderCancel))

```graphql
mutation {
  uniorderCancel(order_id: 123456)
}
```

**Ověření:** `result` je `true`. Opětovné zrušení téže objednávky vrátí `already_cancelled` `true`.

## 11. Pozdější nákup štítku (pouze po LABEL_PURCHASE_FAILED)

Tento krok platí pouze pro objednávku štítku, jejíž vytvoření odpovědělo `LABEL_PURCHASE_FAILED`. Odpovědí bylo `200` s `result` `false`, kódem `LABEL_PURCHASE_FAILED` a `id` objednávky: objednávka byla zachována bez štítku. Objednávku neodesílejte znovu; kupte štítek pro tuto objednávku.

**REST:** `POST /api/v1/uniorder/{orderId}/label` — [Příručka REST](/api/documentation#/paths/v1-uniorder-orderId--label/post)

Vytvoření, při kterém se nepodařilo koupit štítek, odpovědělo:

```json
{
  "result": false,
  "code": "LABEL_PURCHASE_FAILED",
  "type": "label_service",
  "id": 123458,
  "ref": "WEB-10046",
  "tracking_numbers": ["SR26092800003"],
  "quoted_price": "18.40",
  "message": "The quoted service is not offered for this shipment."
}
```

Kupte štítek pro objednávku `123458`:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/123458/label \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: label-10046" \
  -d '{}'
```

Štítek se koupí u služby zvolené při vytvoření objednávky. Chcete-li ho koupit u jiné služby téhož účtu, pošlete v těle nové `rate_id` typu `label_service` z kroku 5 (`{"rate_id": "eyJpdiI6IlpxR0..."}`). Štítek, který je již koupen, se vrátí a znovu se nekupuje.

```json
{
  "result": true,
  "type": "label_service",
  "id": 123458,
  "ref": "WEB-10046",
  "shipping_price": "18.40",
  "label": {
    "carrier_name": "ups",
    "service_code": "ups_standard",
    "main_tracking_number": "1Z999AA10123456791",
    "label_status": "ready",
    "shipping_label": "JVBERi0xLjQK..."
  }
}
```

- `label.shipping_label`: PDF štítku v base64; vytiskněte ho jako v kroku 7.
- `result` `false` opět s `LABEL_PURCHASE_FAILED`: dopravce stále odmítá. Opakujte později nebo kupte u jiné služby s novým `rate_id`.

**GraphQL:** `uniorderPurchaseLabel` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderPurchaseLabel))

```graphql
mutation {
  uniorderPurchaseLabel(order_id: 123458)
}
```

**Ověření:** `result` je `true` a `label.shipping_label` obsahuje PDF, nebo `label.label_status` je `pending`, dokud dopravce soubor vytváří.

## 12. Dávky

Dávky ocení nebo vytvoří mnoho zásilek jedním voláním, například velkoobchodní objednávky z ERP. Každý řádek projde jednotlivým voláním a vrátí to, co by vrátilo toto volání; neúspěšný řádek nezastaví ostatní řádky.

**REST:** `POST /api/v1/uniorder/rate/batch` — [Příručka REST](/api/documentation#/paths/v1-uniorder-rate-batch/post) · `POST /api/v1/uniorder/batch` — [Příručka REST](/api/documentation#/paths/v1-uniorder-batch/post)

Nejvýše 20 řádků na volání, zodpovězených v téže odpovědi: `shipments` pro dávku cenových nabídek, `orders` pro dávku vytvoření. Každý řádek má stejná pole jako jednotlivé volání a navíc volitelnou `reference`, která se vrátí s jeho výsledkem.

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/batch \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: erp-2026-10-01" \
  -d '{
    "orders": [
      {
        "reference": "ERP-7781",
        "rate_id": "eyJpdiI6Ik1rT2Z...",
        "ref": "ERP-7781",
        "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 }]
      }
    ]
  }'
```

```json
{
  "result": true,
  "count": 1,
  "results": [
    { "index": 0, "reference": "ERP-7781", "status": 200, "body": { "result": true, "type": "self_delivery", "id": 123460 } }
  ]
}
```

- `results`: jeden na řádek, s `index` řádku, jeho `reference` a se `status` a `body`, které by vrátilo jednotlivé volání. Každý výsledek přiřaďte k jeho řádku objednávky podle `reference`.

**REST:** `POST /api/v1/uniorder/rate/batch-async` — [Příručka REST](/api/documentation#/paths/v1-uniorder-rate-batch-async/post) · `POST /api/v1/uniorder/batch-async` — [Příručka REST](/api/documentation#/paths/v1-uniorder-batch-async/post) · `GET /api/v1/uniorder/jobs/{jobId}` — [Příručka REST](/api/documentation#/paths/v1-uniorder-jobs-jobId/get)

Nejvýše 500 řádků zařazených do fronty jako jedna úloha. Volání vrátí `job_id`; čtěte úlohu, dokud `status` není `done`, a poté načtěte `results`. Stejná dávka odeslaná znovu, zatímco první ještě čeká ve frontě, vrátí první úlohu s `duplicate` `true`.

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

```json
{
  "result": true,
  "job_id": 8813,
  "kind": "create",
  "status": "done",
  "count": 1,
  "results": [
    { "index": 0, "reference": "ERP-7781", "status": 200, "body": { "result": true, "type": "self_delivery", "id": 123460 } }
  ]
}
```

- `status`: `queued`, `done` nebo `failed` s `message`, když úlohu nebylo možné zpracovat.
- Úloha se spustí jednou a neopakuje se. `rate_id`, jehož platnost vyprší před zpracováním jeho řádku, vrátí pro tento řádek `RATE_ID_INVALID`; úlohu vytvoření odešlete brzy po dokončení úlohy cenové nabídky.

**GraphQL:** `uniorderRateBatch` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderRateBatch)) · `uniorderCreateBatch` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderCreateBatch)) · `uniorderRateBatchAsync` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderRateBatchAsync)) · `uniorderCreateBatchAsync` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderCreateBatchAsync)) · `uniorderJob` ([Příručka GraphQL](/api/graphql/documentation#/orders/uniorderJob))

```graphql
query {
  uniorderJob(job_id: 8813)
}
```

**Ověření:** dávka vrátí jeden výsledek na řádek; asynchronní úloha dosáhne `status` `done`.

## 13. Zpracování chyb

| Situace | Stav HTTP | Kód | Co integrace udělá |
|---|---|---|---|
| Povinné pole chybí nebo má nesprávný formát | 400 | `VALIDATION_FAILED` | Opravte pole uvedené v `message` a požadavek odešlete znovu. |
| Příjemce je mimo oblast doručení (cenová nabídka) | 200 | `OUT_OF_DELIVERY_AREA` v `errors` | Nabídněte pouze sazby `label_service`. |
| Platnost `rate_id` vypršela, má nesprávný formát nebo patří jinému účtu | 400 | `RATE_ID_INVALID` | Vyžádejte novou cenovou nabídku a vytvořte objednávku s jejím `rate_id`. Nic nebylo vytvořeno. |
| Objednávka štítku byla vytvořena, ale její štítek nebyl koupen | 200 (`result` `false`) | `LABEL_PURCHASE_FAILED` | Uchovejte `id`; kupte štítek pomocí `POST /api/v1/uniorder/{orderId}/label`. Objednávku nikdy nevytvářejte znovu. |
| Štítek je vyžádán dříve, než byl koupen | 409 | `LABEL_PURCHASE_FAILED` | Kupte štítek pomocí `POST /api/v1/uniorder/{orderId}/label`. |
| Objednávka je příliš daleko na zrušení | 409 | `ORDER_STATUS_NOT_CANCELLABLE` | Objednávku ponechte beze změny; vrácení řešte samostatně. |
| Objednávku nyní nelze zrušit | 409 | `ORDER_CANCEL_REFUSED` | Objednávku ponechte beze změny; opakujte později nebo kontaktujte firmu. |
| Dopravce štítek nezneplatnil | 409 | `LABEL_CANCEL_FAILED` | Objednávka je nezměněná; zrušení opakujte později. |
| Objednávka nebo úloha neexistuje nebo patří jinému účtu | 404 | `ORDER_NOT_FOUND` | Zkontrolujte `id` uložené k webové objednávce. |
| `Idempotency-Key` je použit znovu s jiným tělem | 409 | `IDEMPOTENCY_CONFLICT` | Pro jiný požadavek použijte nový klíč. |
| Token chybí nebo vypršel, nebo účet nesmí zadávat objednávky | 401 | — | Přihlaste se znovu; zkontrolujte oprávnění účtu. |

## Seznam testů

Použijte testovací `ref`, například `WEB-10045`:

- [ ] Cenová nabídka vrátí sazbu `self_delivery` pro adresu v oblasti.
- [ ] S `quote_labels` cenová nabídka vrátí sazby `label_service`, každou s `rate_id`.
- [ ] Objednání s `rate_id` typu `self_delivery` vrátí `id` a `tracking_numbers`.
- [ ] Objednání s `rate_id` typu `label_service` vrátí štítek oceněné služby.
- [ ] Stejný `Idempotency-Key` nevytvoří druhou objednávku.
- [ ] `rate_id` starší než 30 minut vrátí `RATE_ID_INVALID`.
- [ ] Štítek každé objednávky se dekóduje na PDF vhodné k tisku.
- [ ] Objednávku, její štítek a sledování lze načíst pomocí `id` z vytvoření.
- [ ] Zrušení testovací objednávky vrátí `result: true`; její opětovné zrušení vrátí `already_cancelled: true`.
- [ ] Po `LABEL_PURCHASE_FAILED` koupí `POST /api/v1/uniorder/{orderId}/label` štítek pro tutéž objednávku.
- [ ] Dávka se dvěma řádky vrátí dva výsledky s jejich `reference`.
