# For AI agents

> For a coding agent building on the Cubic API — what to read, the order of operations, and the mistakes to avoid.

## What to read

| Need | Read |
|---|---|
| Everything, in one fetch | [`https://developers.gocubic.io/llms-full.txt`](https://developers.gocubic.io/llms-full.txt) |
| Exact schemas, enums, parameters, error examples | [`https://developers.gocubic.io/openapi.json`](https://developers.gocubic.io/openapi.json) |
| An index of pages | [`https://developers.gocubic.io/llms.txt`](https://developers.gocubic.io/llms.txt) |
| One page as markdown | Append `.md` to any page URL: `https://developers.gocubic.io/recipes.md` |

## Order of operations

1. Read the key from `CUBIC_API_KEY`. If it is not set, ask your user for it. Never write it into source code.
2. Call `GET https://api.gocubic.io/v1/me`. `200` confirms the key and names the account. On `401`, stop and report the `hint` to your user.
3. Call `GET /v1/shipments?limit=5` and look at real data before writing parsing code.
4. Build on the [client in Recipes](https://developers.gocubic.io/recipes.md#client): it handles auth, cursors and `429`.
5. On any error, read `code` and `hint` in the response body and follow the hint.

The four endpoints on the [Overview](https://developers.gocubic.io/index.md) are the whole API. If your user asks for anything else, say the API does not offer it.

## Common mistakes

| Mistake | Instead |
|---|---|
| Treating `null` as an error. | `null` means "not known yet" or "does not apply". Handle it on every nullable field. |
| Reading `milestones[3]`, or assuming a `DestinationPort` milestone exists. | Find milestones by `type` and handle "not found". `portOfDischarge` can be `null`. |
| Assuming every milestone has a `place`. | `IntermediaryWarehouse` and `BorderCrossing` milestones always have `place: null`. |
| Assuming kilograms, centimetres or cubic metres. | Read `unit` on every measurement and convert explicitly. |
| Treating `packages[].weight` as the line total. | It is the weight of one package. Multiply by `quantity`. |
| Counting containers on LCL, DDP or DDU shipments as the customer's. | They are shared consolidation containers. Only FCL containers are the customer's own. |
| Flagging `incoterms: EXW` on a DDP shipment as inconsistent. | `incoterms` only says who arranges the origin pickup. |
| Converting milestone dates to UTC or another time zone. | They are local calendar dates. Compare them as strings. |
| Treating an `arrival` with `actual: false` as having happened. | It is an estimate and can change. |
| Working out where a shipment is from the `completed` flags. | Read `milestones[currentMilestoneIndex]`. |
| Rejecting unknown `parcels.carrier` values. | It is an open set. |
| Fetching `GET /v1/shipments/{reference}` for each list item. | List items are already full Shipment objects. |
| Reusing a cursor with different filters. | Cursors are tied to the filters that produced them. On `invalid_cursor`, restart without `cursor`. |
| Looking up a PO number with `GET /v1/shipments/{reference}`. | That path takes the Cubic reference only. Use `?tag=` (exact) or `?search=` for POs, containers, master bills and FBA IDs. |
| Reading `404 shipment_not_found` as "does not exist". | It means "not on this key's account". |
| Retrying `400`, `401` or `404`. | Retry only `429` (after `Retry-After`) and `5xx` (with backoff). |
| Re-reading every shipment on a schedule. | Poll with `updatedSince` and upsert by `reference`. |
