# LLM Integration Guide

This guide takes an AI agent, or a developer, through a whole integration with the Stream API: signing in, pricing a shipment, booking it, getting it collected and following it to delivery. Every call is linked to its reference page, which has the fields, real examples and code in six languages.

## Read these first

- [`/llms.txt`](/llms.txt) lists every page. [`/llms-full.txt`](/llms-full.txt) holds every page in one file.
- Every page has a Markdown version: add `.md` to its address, such as [`/reference/create-a-quote.md`](/reference/create-a-quote.md).
- The [OpenAPI file](/openapi/stream-api.yaml) describes every call, field and answer.

## Before you start

- **Two environments.** Build and test against the sandbox, `https://sandbox-api.vatit-stream.com/client_api/v2`, then switch to production, `https://orders-production.vatit-stream.com/client_api/v2`. The sandbox's carriers are stand-ins, so nothing is shipped or charged.
- **The client's own logins.** We send each client a sandbox login when they're ready to connect, and a production login when they go live. Ask the client for them. Never put them in code; read them from the environment.
- **JSON in, JSON out.** Send `Content-Type: application/json` with a body, except when the order carries files (see step 4).
- **Call from a server.** Keep the login and token out of browsers and mobile apps.

## The integration, step by step

### 1. Sign in

[Sign in](/reference/sign-in) with the login's email and password. Send the `token` from the answer on every other call as `Authorization: Bearer <token>`. A token lasts a week. When a call answers `401`, sign in again and repeat the call. See [Authentication](/authentication).

### 2. Look up the ids you need

Many fields take an id from another call, and **ids differ between the sandbox and production**. Look them up in the environment you are calling, and keep them per environment.

| You need | Look it up with |
|---|---|
| A country, for addresses and items | [List countries](/reference/list-countries) `?q=South Africa` |
| An incoterm (`purpose_of_shipment_id`) | [List purposes of shipment](/reference/list-purposes-of-shipment) `?code=DDP`. It lists only the incoterms the client may use. |
| A reason for shipment | [List reasons for shipment](/reference/list-reasons-for-shipment) `?q=commercial` |
| A currency | [List currencies](/reference/list-currencies). Currencies go by their three-letter code, such as `ZAR`. |
| A nature of transaction for an item | [List natures of transaction](/reference/list-natures-of-transaction) `?code=11` |

### 3. Price the shipment

[Create a quote](/reference/create-a-quote) with the addresses, packages, items (`hs_codes`) and value. The answer's `estimates` hold one price for each carrier that can take the shipment. Keep the quote's `id` and the `estimate_id` you choose.

- Set `estimate_taxes_and_duties` to `true` to include the import duties and taxes we estimate. See [Taxes and duties](/guides/taxes-and-duties).
- Set `hub_transit` to `true` to route through one of our hubs. See [Hub shipments](/guides/hub-shipments).

### 4. Book the shipment

[Create an order](/reference/create-an-order) from the chosen `estimate_id`. **Send a `customer_reference` that is unique to this order**, such as the client's purchase order number. It is how you find the order again if the call times out (see [Retrying safely](/retries)).

To send a commercial invoice, packing list or safety data sheet, include them in this request as a `multipart/form-data` upload. The API can't add them later. See [Sending documents](/guides/sending-documents). Without a commercial invoice, we make one from the shipment's items.

Keep the order's `id`. It starts as `order_received`.

### 5. Get the waybill

**Call [Create a waybill](/reference/create-a-waybill) yourself.** An order made through the API waits in `order_received` until you do.

- **A success** answers with the order, and its `waybill` holds the carrier's tracking number.
- **When it can't be made yet,** the answer says why, and the order moves to one of these:
  - `awaiting_additional_documentation`: a document is missing;
  - `awaiting_prepayment`: a pro forma invoice needs paying;
  - `awaiting_waybill_generation`: our team is checking it.

  [Order statuses](/order-statuses) says what to do in each.
- **Never send it twice without checking.** A second call books the carrier again. After a timeout, read the order with [Get an order](/reference/get-an-order), and call again only if it has no `waybill`. On a [hub order](/guides/hub-shipments) check every `shipments[].waybill`: the order's own `waybill` is its last shipment's, but the call books every leg.

### 6. Book the collection

[List pickup slots](/reference/list-pickup-slots) gives the days and times the carrier collects. [Book a pickup](/reference/book-a-pickup) in one of them. The order moves to `awaiting_pickup`, then to `in_transit` once the carrier has the goods.

Booking again cancels the earlier pickup. To check what's booked, use [List pickups](/reference/list-pickups).

### 7. Follow it to delivery

- **Webhooks** tell you about some status changes and every waybill change; [Receiving webhooks](/guides/webhooks#the-events) says which. Set one up with [Create a webhook](/reference/create-a-webhook), and read [Receiving webhooks](/guides/webhooks) for what we send and how to check it.
- **Poll as well.** Webhooks say nothing for the other statuses, and events can be lost if your server is down. Check open orders with [List orders](/reference/list-orders), filtered with `?status=`, or [Get an order](/reference/get-an-order).
- **Tracking:** [List tracking events](/reference/list-tracking-events) gives the carrier's scans, newest first. The order's `tracking_link` opens the carrier's own tracking page.

### 8. Cancel if needed

[Cancel an order](/reference/cancel-an-order) works while every shipment is still before pickup.

## What each status asks of you

[Order statuses](/order-statuses) says what each status means and what to do in it.

## Errors

Every error answers `{ "success": false, "error": "...", "errors": [...] }`. [Errors](/errors) says what each status means and what to retry, and [Retrying safely](/retries) covers timeouts.

## Before going live

- Test the whole flow on the sandbox with the client's sandbox login.
- Send a unique `customer_reference` with every order.
- Never retry Create a waybill without reading the order first.
- Check the `token` on every webhook event, answer `2xx` quickly, and ignore repeats.
- Poll open orders as well as receiving webhooks.
- Keep ids, logins and tokens per environment. Switch the base URL and the login together.
