# Cargoplot API changelog

Changes to the public v1 API, newest first. Written for someone integrating
against it: what moved, and what they have to do about it.

The spec at `/openapi.json` is served from the running API, so a change reaches
consumers the moment it deploys. The spec says what the contract is now; this
file is the only place that says what changed.

A regeneration that a consumer cannot observe still gets a line saying so, so
the next reader can tell "nothing to see here" from "nobody wrote it down".

Changes land under an `## Unreleased` heading while they are only on dev. That
heading is renamed to the release date in the very commit that gets built and
deployed: this file ships inside the image, so a heading corrected after the
deploy corrects nothing anyone is reading. The api's AGENTS.md carries the three
steps, under "Rolling the changelog over at release".

It lives beside the spec it describes rather than with the protos that generate
it, because `go:embed` cannot reach outside its own package directory and this
file is served, not just read.

## 2026-09-14

Every status and milestone the API returns now says what it means, and the spec
points at a glossary for the codes it does not define itself.

- **Statuses and milestones carry their meaning.** Each value of a shipment,
  inquiry and invoice status, and of the sea and air tracking vocabularies, is
  now described in the spec beside the value itself. `STATUS_IN_COLLECTION`,
  `CONTAINER_TRACKING_STATUS_EMPTY_TO_SHIPPER` and the rest read as sentences in
  the reference and in any client generated from `/openapi.json`. No value was
  added, removed or renamed, and no response changed.
- **The spec links a glossary.** Its `externalDocs` points at
  <https://developers.cargoplot.com/glossary>, which explains the code systems
  the API returns as plain strings -- UN/LOCODE, IATA, ISO 6346 container
  numbers, Incoterms -- and the vocabularies that reach the spec from the shared
  domain model with no description of their own: cargo and container types,
  transport modes, document types, VAT categories.

## 2026-09-07

Shipment tracking reached production, for sea and air freight.

- **Shipment tracking.** `GET /v1/shipment/{shipment}/tracking` returns a
  shipment's movement timeline, addressed by Cargoplot reference or numeric id,
  unpaginated. The timeline sits under a key naming the transport mode and a
  response carries exactly one of them: `sea` holds one entry per container,
  each with its own events; `air` holds one overall status and one list of
  events, because air cargo has no container to hang milestones on. `etd` and
  `eta` sit beside the mode key. Events run oldest to newest and say whether
  they are expected or actual; an empty list is a valid answer, not an error.
  Air events name their airport by IATA code and carry the flight they happened
  on, and `cargo` says how much of the consignment each leg carried — omitted
  where more than one shipment shares the waybill, since the figures would then
  cover more than yours. Road, rail and express return `400`.

## 2026-08-24

Invoices and shipment documents reached production, so the API answers what
a shipment cost and what paperwork it still needs, not only where it is.

- `GET /v1/invoice` lists your organization's invoices, and
  `GET /v1/shipment/{shipment}/invoice` narrows that to one shipment. Both page,
  sort and filter exactly like the shipment and inquiry lists, so the cursor you
  already handle works unchanged.
- `GET /v1/invoice/{invoice}` reads one, addressed by reference (`F2401-C2AE-XEE1`) or
  numeric id, and renders it identically to one element of the list.
- `GET /v1/invoice/{invoice}/download` returns the invoice PDF.
- The API serves invoices that have been issued to your organization.
- Amounts arrive as an object carrying the currency alongside the number, so no
  field has to be read in combination with another to be meaningful.
- `GET /v1/shipment/{shipment}/document` returns a shipment's document
  checklist: every slot, with the uploaded file where there is one. A slot with
  no `document` member is still outstanding — that is how the two are told
  apart. The list is not paginated; a checklist is small and bounded.
- `GET /v1/document/{document}/download` returns an uploaded file, with the
  content type it was stored as. The id is the `document.id` from the checklist,
  not the slot id.
- The checklist covers the document types this API serves.
- **Fixed:** `GET /v1/inquiry` sorted by `reference` or `cargo_ready_date` dropped
  inquiries whose value for that field is empty, from the second page onwards —
  silently, with no error and no short page. If you paged that endpoint sorted
  either way, the set you collected is incomplete and worth re-fetching. Sorting
  by `status` or `create_time` was never affected, and no other endpoint was.
- The money schema is now named `apiV1Money` in the spec, where it was `v1Money`.
  No response changed shape, and nothing needs doing if you read the JSON
  directly — but a generated client will see the type renamed, and the shipment
  and inquiry `insured_goods_value` fields point at the new name too.
- **Fixed:** the documented media types for `MEDIA_TYPE_DOCX` and
  `MEDIA_TYPE_XLSX` were each other's — DOCX was documented as Excel's
  `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` and XLSX as
  Word's `…wordprocessingml.document`. The enum values themselves never changed
  and no response moved, so nothing breaks; but if you mapped either to a content
  type from these docs, you have the two the wrong way round.
- The media type schema is named `DocumentMediaType` on the reference page, where
  it previously showed its list of values in place of a name.

## 2026-08-21

First public release. The documentation site at
<https://developers.cargoplot.com> is live, and the profile tab that mints the
Personal Access Tokens the API needs is available in production.

- Read-only — every documented operation is a GET, which a test over the
  generated spec enforces rather than trusting.
- Authenticate with a Personal Access Token in the `X-Cargoplot-Key` header.
- Shipments, inquiries, organizations and users, with one cursor-pagination
  contract shared by every list endpoint.
- Request limits and the conventions the spec cannot express are published at
  `/api-info.json`, along with the base URL to call.
- The API has a base URL of its own -- `https://api.cargoplot.com` in
  production, and whatever `/api-info.json` reports as `base_url` in any other
  environment.
- `/openapi.json` now states the host and scheme it is served from, so a
  generated client or an agent handed only that file has somewhere to send
  requests. It previously carried `basePath: /api` and no host, which left the
  host to be guessed.
- Every rate-limited response carries `RateLimit-Limit`, `RateLimit-Remaining`
  and `RateLimit-Reset`, so you can pace yourself instead of discovering the
  ceiling by hitting it. `RateLimit-Reset` counts seconds from now, not a
  timestamp. A 429 still carries `Retry-After` as well, which answers the
  narrower question of when to retry this request.
- Every description in `/openapi.json` documents the contract: what an endpoint
  returns and what you have to handle. Notes aimed at Cargoplot's own
  engineers are kept out of it, so the reference stays about integrating rather
  than about how the service is built.
