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

# Create a Booking

> Book an Open Shipment with a carrier and provision its shipping labels.

A Booking turns an **Open Shipment** — a Shipment created with the information a carrier needs, but no tracking number yet — into a confirmed carrier booking. On success you receive the carrier-generated tracking number, the shipping label document(s), and, for PUDO deliveries, the confirmed location. Full field specs: [API reference](/api-reference/v5/booking/create-booking).

<Info>
  Booking requires account enablement — contact your account team. The carrier configuration you book with must be set up for outbound or return booking.
</Info>

## The flow

<Steps>
  <Step title="Create the Open Shipment">
    Use [Create Shipment](/guides/create-shipment) with the data the carrier requires — typically full to/from addresses (name, line1, postal code, city, country, phone), weight, and line items. Don't set a tracking number; the booking assigns it.
  </Step>

  <Step title="Book it">
    Identify the Shipment by `shipment_uuid` or `shipment_id`, and name the carrier configuration:

    ```bash theme={null}
    curl --request POST \
      --url https://api.perform.ai/v5/booking/ \
      --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
      --header 'Content-Type: application/json' \
      --data '{ "shipment_id": "SHP-2026-000115", "carrier_reference": "dhl-ecommerce-de" }'
    ```

    `carrier_reference` selects the carrier configuration to book with (from **Settings > Carriers**; it must support booking). It's **optional when the Open Shipment already has a carrier** — the booking then uses the Shipment's own `carrier_reference`. When you provide one, the request's value is used for the carrier booking — so either omit it or keep it consistent with the Shipment's carrier; don't send a conflicting value. Never send it as `null` or an empty string — omit it entirely instead.

    For a pickup/drop-off delivery, add `pudo_location_uuid` from the [PUDO locations](/guides/pudo-locations) endpoint; the label is then routed to that location and the response echoes the confirmed location details.
  </Step>

  <Step title="Use the result">
    A successful booking returns `booking_uuid`, the carrier `tracking_number`, the booking `status` (`booked`, or `return_initiated` for returns), `type_of_shipment` (`outbound`/`return`), and the label(s) under `documents` — each with a `download_url` (1-hour expiry) **and** `base64_content`. Some carriers issue no label; `documents` is then an empty array.
  </Step>
</Steps>

## Labels that arrive late (HTTP 299)

Some carriers generate labels asynchronously. The booking still succeeds — tracking number assigned, status `booked` — but the response is **HTTP 299** and `download_url` / `base64_content` inside `documents` are `null`, with warnings telling you so. Fetch the label later:

* [Retrieve a Document](/guides/shipment-documents) — download URL **and** Base64.
* [List Labels](/guides/retrieve-labels) — download URLs only.

Never rebook to "retry" a 299 — the booking exists.

## Booking validation errors

Booking uses its own error envelope: `errors` sits inside `data` next to the Shipment identifiers, and each entry carries a `standard_error_message`:

```json theme={null}
{
  "api_response": "4030",
  "message": "Validation error",
  "data": {
    "errors": {
      "carrier_reference": [
        {
          "api_response": "4030",
          "standard_error_message": "Value provided is not defined for outbound or return. Please provide the correct carrier_reference or reach out to Perform.AI Customer Success team for assistance."
        }
      ]
    },
    "shipment_id": "SHP-2026-000115",
    "shipment_uuid": "026c09d3-4243-4056-b412-c62411827878"
  }
}
```

Errors for fields inside objects nest one level deeper (for example `from_address.postal_code`). Most booking errors mean the Open Shipment is missing something the carrier requires — fix it with [Update Shipment](/guides/update-shipment) and book again. Carrier-side failures surface as 500/503/504.

<Tip>
  Carriers enforce their own field limits at booking — commonly \~35 characters per address line — stricter than what Create Shipment accepts. If a booking fails on address fields that passed creation, shorten them.
</Tip>
