openapi: 3.2.0 info: title: Load Optimizer Libraries API version: 2.0.0 summary: Asynchronous box-packing / load-optimization API. description: 'Submitting work is **asynchronous**: every solve is a *job* you create, poll, then collect. This is the documentation of the **Load Optimizer API v2**: it describes what `/v2` supports; what it does not describe is not supported. ## Job lifecycle `queued → running → succeeded | failed | cancelled` (last three terminal). Poll `GET /jobs/{id}` until terminal. ## Conventions Values from a fixed set (enum values, in a request body or in list filters such as `status`, `saved` and `sort`) are matched without regard to case, and come back in the spelling published here: `"MM"` reads back as `"mm"`. Names and codes keep their case: group names, a SKU''s code (`externalId`, also as a filter) and system-catalogue slugs are case-sensitive. The idempotency fingerprint compares the body as you sent it: an enum value in another case makes a different body, so while the key is kept (see `Idempotency-Key`) the same key gets the `409`.' contact: email: contact@loadoptimizer.ai servers: - url: https://api.loadoptimizer.ai/v2 description: Production environment of the Load Optimizer API. x-internal: false security: - ApiKey: [] tags: - name: Libraries description: Store your own carriers and cargo units (SKUs) once and reference them from a job by id. paths: /libraries: get: tags: - Libraries operationId: listLibraries summary: List the libraries you can use description: 'The libraries your account can use. The system catalogue is listed first: it needs no company-library entitlement, so any plan that can use the customer API can read and reference it, and `libraries_not_entitled` never applies to a slug. Every company library type follows it when it is available, carriers first and then SKUs: a type is available when the plan includes company libraries and its own size limit is not 0. A type that is not available is left out of the list rather than answered with an error, so without company libraries the list holds only the system catalogue.' parameters: - name: type in: query required: false description: 'Only libraries of this kind: `carrier` or `sku`. Any other value is rejected with `400`.' schema: type: string enum: - carrier - sku - name: scope in: query required: false description: Only `company` or only `system` libraries. `system` returns the catalogue. Any other value is rejected with `400`. schema: type: string enum: - system - company responses: '200': description: The libraries you can use. content: application/json: schema: $ref: '#/components/schemas/LibraryList' examples: companyLibrary: summary: A plan with company libraries, two carriers and three SKUs saved value: items: - id: carrier_system_default name: Standard carriers type: carrier scope: system overridable: true entryCount: 49 access: reference - id: 5f0c6a8e-3b1d-5e2a-9c47-81d2f6a0b3e4 name: Company carriers type: carrier scope: company overridable: true entryCount: 2 access: edit - id: 9b3d7e21-4c6a-5f18-8e2b-6a0d4c9f1e73 name: Company SKUs type: sku scope: company overridable: true entryCount: 3 access: edit companySkuLibrary: summary: 'Only the SKU library: `?type=sku`' value: items: - id: 9b3d7e21-4c6a-5f18-8e2b-6a0d4c9f1e73 name: Company SKUs type: sku scope: company overridable: true entryCount: 3 access: edit noCompanyLibraries: summary: 'A plan without company libraries: only the system library' value: items: - id: carrier_system_default name: Standard carriers type: carrier scope: system overridable: true entryCount: 49 access: reference '400': $ref: '#/components/responses/ValidationProblem' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' /libraries/{id}: parameters: - $ref: '#/components/parameters/LibraryId' get: tags: - Libraries operationId: getLibrary summary: Get one library description: One library you can use, by the id that `GET /libraries` lists for it. responses: '200': description: The library, as `GET /libraries` lists it. content: application/json: schema: $ref: '#/components/schemas/LibrarySummary' examples: companyLibrary: summary: Your company carrier library value: id: 5f0c6a8e-3b1d-5e2a-9c47-81d2f6a0b3e4 name: Company carriers type: carrier scope: company overridable: true entryCount: 2 access: edit companySkuLibrary: summary: Your company SKU library value: id: 9b3d7e21-4c6a-5f18-8e2b-6a0d4c9f1e73 name: Company SKUs type: sku scope: company overridable: true entryCount: 3 access: edit systemLibrary: summary: The system catalogue value: id: carrier_system_default name: Standard carriers type: carrier scope: system overridable: true entryCount: 49 access: reference '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/LibrariesForbidden' '404': $ref: '#/components/responses/LibraryNotFound' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' /libraries/{id}/entries: parameters: - $ref: '#/components/parameters/LibraryId' get: tags: - Libraries operationId: listLibraryEntries summary: List the entries in a library description: 'The entries in the order they were created, as full definitions, a page at a time: carriers in a carrier library, SKUs in the SKU library. Follow `nextCursor` until it is null. Never lists an archived entry: a retired one stops appearing here as well as by id. `q` and `externalId` combine: an entry must match both.' parameters: - name: q in: query required: false description: 'Only entries whose `name` or `externalId` contains this text, ignoring case. Matched literally: `%` and `_` are ordinary characters. At most 256 characters.' schema: type: string maxLength: 256 - name: externalId in: query required: false description: Only entries with exactly this externalId, case-sensitive. On a SKU library at most one, because a SKU code is unique there; a carrier library allows duplicate labels, so it may return several. At most 128 characters. schema: type: string maxLength: 128 - name: limit in: query required: false description: Entries per page, 1 to 200. Default 100. schema: type: integer format: int32 minimum: 1 maximum: 200 default: 100 - name: cursor in: query required: false description: The `nextCursor` of the previous page of the same library, passed back unchanged. A value this API did not issue, or one issued for another library, is rejected with `400`. At most 4,096 characters. schema: type: string maxLength: 4096 responses: '200': description: One page of entries. content: application/json: schema: $ref: '#/components/schemas/LibraryEntryPage' examples: firstPage: summary: The first page, one more to follow value: items: - id: 0197c3a0-5b1e-7c2d-9a41-2f6e8d0b11a1 type: carrier scope: company externalId: TRL-136 name: Trailer 13.6 note: Tail lift for the northern route category: trailer region: null units: length: mm weight: kg geometry: type: box dimensionX: 13600 dimensionY: 2480 dimensionZ: 2700 tareWeight: 7200 revision: 1 updatedAt: '2026-09-24T08:12:33.412+00:00' nextCursor: ZTE6NWYwYzZhOGUtM2IxZC01ZTJhLTljNDctODFkMmY2YTBiM2U0OjM systemLibrary: summary: Browsing the system catalogue value: items: - id: carrier_eur_pallet type: carrier scope: system externalId: null name: EPAL / EUR 1 note: null category: pallet region: europe units: length: mm weight: kg geometry: type: box dimensionX: 1200 dimensionY: 800 dimensionZ: 1800 tareWeight: null revision: 1 updatedAt: '2026-09-25T00:00:00.001+00:00' nextCursor: null skuByCode: summary: 'One SKU looked up by its code: `?externalId=SKU-4711`' value: items: - id: 0197d1e4-2a6b-7f3c-8d15-4b9e0c7a2f68 type: sku scope: company externalId: SKU-4711 name: Carton 60x40 note: null category: box units: length: mm weight: kg geometry: type: box dimensionX: 600 dimensionY: 400 dimensionZ: 400 weight: 18 constraints: allowStackingOnTop: true allowedRotations: - Z revision: 1 updatedAt: '2026-09-30T09:41:07.226+00:00' nextCursor: null '400': $ref: '#/components/responses/ValidationProblem' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/LibrariesForbidden' '404': $ref: '#/components/responses/LibraryNotFound' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' post: tags: - Libraries operationId: createLibraryEntry summary: Save an entry in a company library description: 'Stores an entry in its own units (metres and kilograms when `units` is omitted). The library decides the body: your company carrier library takes a `LibraryEntryInput`, your company SKU library a `SkuEntryInput`. A member of the other type is ignored like any unknown member, so a body sent to the wrong library fails on that library''s own required fields: a SKU needs `weight`, a carrier needs a carrier `category`. On a carrier `category` is required, unlike on an inline job carrier, because a stored carrier must not change kind with the job that uses it. A SKU''s `externalId`, when set, is unique within the SKU library and case-sensitive: a code another SKU already has answers `409 external_id_taken`. A replay of the same `Idempotency-Key` and body is checked first and still answers `201`; the key holds as long as the entry it created exists. The same size rules apply as to an inline carrier or cargo line. A response body posted back as a request creates a copy: the read-only fields are ignored (on the SKU library, change or drop its `externalId` first).' parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: description: 'The library in the path decides which of the two shapes applies: your company carrier library takes a `LibraryEntryInput`, your company SKU library a `SkuEntryInput`. Members of the other shape are ignored, and an optional member sent as `null` counts as absent.' anyOf: - $ref: '#/components/schemas/LibraryEntryInput' - $ref: '#/components/schemas/SkuEntryInput' examples: trailerInMillimetres: summary: A trailer, written in millimetres and kilograms value: externalId: TRL-136 name: Trailer 13.6 note: Tail lift for the northern route category: trailer units: length: mm weight: kg geometry: type: box dimensionX: 13600 dimensionY: 2480 dimensionZ: 2700 tareWeight: 7200 palletInMetres: summary: A pallet with units omitted (metres and kilograms) value: name: Our EUR pallet category: pallet geometry: type: box dimensionX: 1.2 dimensionY: 0.8 dimensionZ: 1.8 skuCarton: summary: 'A SKU, to your company SKU library: a carton in millimetres and kilograms' value: externalId: SKU-4711 name: Carton 60x40 category: box units: length: mm weight: kg geometry: type: box dimensionX: 600 dimensionY: 400 dimensionZ: 400 weight: 18 constraints: allowStackingOnTop: true responses: '201': description: Saved. `Location` is the entry's URL. headers: Location: description: URL of the new entry. schema: type: string format: uri content: application/json: schema: $ref: '#/components/schemas/LibraryEntry' examples: trailer: summary: The saved trailer, echoed in its own units value: id: 0197c3a0-5b1e-7c2d-9a41-2f6e8d0b11a1 type: carrier scope: company externalId: TRL-136 name: Trailer 13.6 note: Tail lift for the northern route category: trailer region: null units: length: mm weight: kg geometry: type: box dimensionX: 13600 dimensionY: 2480 dimensionZ: 2700 tareWeight: 7200 revision: 1 updatedAt: '2026-09-24T08:12:33.412+00:00' sku: summary: 'The saved SKU, its constraints stored filled in: `allowedRotations` was omitted and reads `["Z"]`' value: id: 0197d1e4-2a6b-7f3c-8d15-4b9e0c7a2f68 type: sku scope: company externalId: SKU-4711 name: Carton 60x40 note: null category: box units: length: mm weight: kg geometry: type: box dimensionX: 600 dimensionY: 400 dimensionZ: 400 weight: 18 constraints: allowStackingOnTop: true allowedRotations: - Z revision: 1 updatedAt: '2026-09-30T09:41:07.226+00:00' '400': $ref: '#/components/responses/ValidationProblem' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/LibraryWriteForbidden' '404': $ref: '#/components/responses/LibraryNotFound' '409': description: 'Either the `Idempotency-Key` was reused with a different body, which answers with an empty body (no code, no detail), or, on the SKU library, another SKU already has this `externalId` (`code: "external_id_taken"`). The replay check comes first, so a genuine retry still answers `201`.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: externalIdTaken: $ref: '#/components/examples/ExternalIdTaken' '413': $ref: '#/components/responses/PayloadTooLarge' '415': $ref: '#/components/responses/UnsupportedMediaType' '422': description: The library already holds as many entries as your plan allows. Delete one, or upgrade the plan. content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: librarySizeExceeded: summary: The library is full value: type: https://tools.ietf.org/html/rfc4918#section-11.2 title: Library size limit reached. status: 422 code: max_library_entries detail: The library has reached its limit of 25 entries. '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' /libraries/{id}/entries/{entryId}: parameters: - $ref: '#/components/parameters/LibraryId' - $ref: '#/components/parameters/EntryId' get: tags: - Libraries operationId: getLibraryEntry summary: Get one entry description: The full definition of one entry, a carrier or a SKU, in the units it was written in. responses: '200': description: The entry's full definition. content: application/json: schema: $ref: '#/components/schemas/LibraryEntry' examples: trailer: summary: A saved trailer value: id: 0197c3a0-5b1e-7c2d-9a41-2f6e8d0b11a1 type: carrier scope: company externalId: TRL-136 name: Trailer 13.6 note: Tail lift for the northern route category: trailer region: null units: length: mm weight: kg geometry: type: box dimensionX: 13600 dimensionY: 2480 dimensionZ: 2700 tareWeight: 7200 revision: 1 updatedAt: '2026-09-24T08:12:33.412+00:00' systemPallet: summary: A system-catalogue entry, read by slug value: id: carrier_eur_pallet type: carrier scope: system externalId: null name: EPAL / EUR 1 note: null category: pallet region: europe units: length: mm weight: kg geometry: type: box dimensionX: 1200 dimensionY: 800 dimensionZ: 1800 tareWeight: null revision: 1 updatedAt: '2026-09-25T00:00:00.001+00:00' sku: summary: A saved SKU, in the units it was written in value: id: 0197d1e4-2a6b-7f3c-8d15-4b9e0c7a2f68 type: sku scope: company externalId: SKU-4711 name: Carton 60x40 note: null category: box units: length: mm weight: kg geometry: type: box dimensionX: 600 dimensionY: 400 dimensionZ: 400 weight: 18 constraints: allowStackingOnTop: true allowedRotations: - Z revision: 1 updatedAt: '2026-09-30T09:41:07.226+00:00' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/LibrariesForbidden' '404': $ref: '#/components/responses/LibraryEntryNotFound' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' put: tags: - Libraries operationId: replaceLibraryEntry summary: Replace an entry description: 'Replaces a company entry''s whole definition, carrier or SKU, with the body: the same body `POST` takes on that library, under the same rules. It is a full replacement, not a patch: a member you leave out gets the value it would get on a create (`null`, the default units, the default constraints), not the value it had. `revision` goes up by 1 and `updatedAt` changes on every `PUT`; the `id` and the entry''s place in the list stay. There is no `Idempotency-Key`: sending the same body again leaves the same definition. A job that already referenced the entry keeps its own copy, so a `PUT` never changes a submitted job. The body is checked before the entry is looked up, so an invalid body answers `400` even for an unknown id. On the SKU library an `externalId` another SKU already has answers `409 external_id_taken`; the SKU''s own current code is no conflict.' requestBody: required: true content: application/json: schema: description: 'The library in the path decides which of the two shapes applies: your company carrier library takes a `LibraryEntryInput`, your company SKU library a `SkuEntryInput`. Members of the other shape are ignored, and an optional member sent as `null` counts as absent.' anyOf: - $ref: '#/components/schemas/LibraryEntryInput' - $ref: '#/components/schemas/SkuEntryInput' examples: trailerReplaced: summary: The trailer with a new note; every other member sent again as it was value: externalId: TRL-136 name: Trailer 13.6 note: Tail lift, northern and eastern routes category: trailer units: length: mm weight: kg geometry: type: box dimensionX: 13600 dimensionY: 2480 dimensionZ: 2700 tareWeight: 7200 skuHeavier: summary: The SKU with a new weight, keeping its own code value: externalId: SKU-4711 name: Carton 60x40 category: box units: length: mm weight: kg geometry: type: box dimensionX: 600 dimensionY: 400 dimensionZ: 400 weight: 21 constraints: allowStackingOnTop: true responses: '200': description: Replaced. The entry as it is now stored. content: application/json: schema: $ref: '#/components/schemas/LibraryEntry' examples: trailer: summary: The replaced trailer, at revision 2 value: id: 0197c3a0-5b1e-7c2d-9a41-2f6e8d0b11a1 type: carrier scope: company externalId: TRL-136 name: Trailer 13.6 note: Tail lift, northern and eastern routes category: trailer region: null units: length: mm weight: kg geometry: type: box dimensionX: 13600 dimensionY: 2480 dimensionZ: 2700 tareWeight: 7200 revision: 2 updatedAt: '2026-09-30T14:05:51.903+00:00' sku: summary: The replaced SKU, at revision 2 value: id: 0197d1e4-2a6b-7f3c-8d15-4b9e0c7a2f68 type: sku scope: company externalId: SKU-4711 name: Carton 60x40 note: null category: box units: length: mm weight: kg geometry: type: box dimensionX: 600 dimensionY: 400 dimensionZ: 400 weight: 21 constraints: allowStackingOnTop: true allowedRotations: - Z revision: 2 updatedAt: '2026-09-30T14:05:51.903+00:00' '400': $ref: '#/components/responses/ValidationProblem' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/LibraryWriteForbidden' '404': $ref: '#/components/responses/LibraryEntryNotFound' '409': description: 'On the SKU library, another SKU already has this `externalId` (`code: "external_id_taken"`).' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: externalIdTaken: $ref: '#/components/examples/ExternalIdTaken' '413': $ref: '#/components/responses/PayloadTooLarge' '415': $ref: '#/components/responses/UnsupportedMediaType' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' delete: tags: - Libraries operationId: deleteLibraryEntry summary: Delete an entry description: Removes the entry, carrier or SKU, for good. Jobs submitted before keep their own copy of what they referenced; a job submitted afterwards that names the id is rejected with `400`, field code `library_entry_not_found`. responses: '204': description: Deleted. Jobs submitted before keep their own copy. '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/LibraryWriteForbidden' '404': $ref: '#/components/responses/LibraryEntryNotFound' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/ServerProblem' components: schemas: EntryUnits: type: object description: The units an entry is written in. Omitted, they are metres and kilograms. The entry keeps them; a job never reinterprets them. properties: length: type: - string - 'null' enum: - mm - cm - m - in - ft - null default: m weight: type: - string - 'null' enum: - g - kg - t - lb - null default: kg ProblemCode: type: string description: Stable, machine-readable top-level error-code registry, by HTTP status. enum: - validation_failed - ambiguous_source - override_field_not_allowed - customer_api_not_entitled - mixed_palletizing_not_entitled - load_planning_not_entitled - link_sharing_not_entitled - request_body_not_supported - libraries_not_entitled - library_read_only - internal_server_error - job_not_found - result_not_ready - artifact_not_ready - saved_load_not_found - library_not_found - library_entry_not_found - job_already_terminal - job_not_succeeded - saved_load_exists - external_id_taken - payload_too_large - unsupported_media_type - max_items_per_job - max_carriers_per_job - max_carrier_instances_per_job - max_saved_loads - max_library_entries - too_many_active_jobs - monthly_quota_exceeded - rate_limit_exceeded LibrarySummary: type: object description: 'One library: what it holds, whose it is, how many entries it has and what you may do with it.' required: - id - name - type - scope - overridable - entryCount - access properties: id: type: string description: The library id. For a company library a UUID that is stable for your account and exists before you save anything. name: type: string type: type: string enum: - carrier - sku description: 'What the library holds: `carrier` entries or `sku` entries (cargo units). The set may grow; tolerate values you do not know.' scope: type: string enum: - system - company description: '`company`: your own, editable. `system`: a standard catalogue, read-only.' overridable: type: boolean description: '`true`: a job may adjust a referenced entry for that job, with `overrides.maxStackHeight` on a carrier (both scopes) and `overrides.constraints` on a SKU.' entryCount: type: integer format: int32 minimum: 0 access: type: string enum: - reference - edit description: '`edit`: you may save and delete entries. `reference`: you may only use them.' LibraryEntryInput: type: object required: - name - category - geometry description: 'The body of `POST` and `PUT` on your company carrier library: one carrier, in its own units.' properties: externalId: type: - string - 'null' minLength: 1 maxLength: 128 description: 'Optional label from your own system. Not a key: duplicates are allowed. A job line that references the entry inherits it unless the line sets its own.' name: type: string minLength: 1 maxLength: 256 note: type: - string - 'null' minLength: 1 maxLength: 1000 description: 'Optional free-text note about the entry, kept in the library: a job that references the entry does not copy it. Not to be confused with a cargo line''s `description`, which is that line''s label.' category: type: string enum: - container - pallet - truck - trailer description: Required on an entry. The set may grow; tolerate values you do not know. region: type: - string - 'null' enum: - europe - north_america - asia - oceania - null description: Optional. Where this entry is typically used, grouping it for display. The set may grow; tolerate values you do not know. units: anyOf: - $ref: '#/components/schemas/EntryUnits' - type: 'null' geometry: $ref: '#/components/schemas/Box' tareWeight: type: - number - 'null' minimum: 0 description: Optional empty weight, in the entry's `units.weight`. At most 100000 kg after conversion. Stored and echoed; not used in solving yet. LibraryList: type: object description: The libraries your account can use, the system catalogue first. required: - items properties: items: type: array items: $ref: '#/components/schemas/LibrarySummary' SkuLibraryEntry: type: object description: A SKU as your company SKU library stores it. Every field is present, `null` when not set. required: - id - type - scope - externalId - name - note - category - units - geometry - weight - constraints - revision - updatedAt properties: id: type: string format: uuid description: 'The entry id (a UUID): the value a cargo line puts in `libraryId`.' type: const: sku scope: type: string enum: - system - company description: '`company` on every SKU today: there is no system SKU catalogue.' externalId: type: - string - 'null' description: 'The SKU code: unique within the SKU library when set, and case-sensitive. Always present, null when not set.' name: type: string note: type: - string - 'null' description: The free-text note about the SKU. Always present, null when not set. category: type: string enum: - box description: 'The kind the SKU is drawn as. Always set: `box` when the entry was written without one. The set may grow; tolerate values you do not know.' units: $ref: '#/components/schemas/EntryUnits' geometry: $ref: '#/components/schemas/CargoGeometry' weight: type: number description: Per-unit weight, in the entry's `units.weight`. constraints: $ref: '#/components/schemas/SkuConstraints' revision: type: integer format: int32 description: Starts at 1; every PUT adds 1. updatedAt: type: string format: date-time CarrierLibraryEntry: type: object description: A carrier as a library stores it. Every field is present, `null` when not set. required: - id - type - scope - externalId - name - note - category - region - units - geometry - tareWeight - revision - updatedAt properties: id: type: string description: 'The entry id: the value a job carrier puts in `libraryId`. A system-catalogue entry''s id is a permanent slug (e.g. `carrier_iso_40hc`) rather than a UUID; retiring an entry never reuses its slug.' type: type: string enum: - carrier scope: type: string enum: - system - company externalId: type: - string - 'null' name: type: string note: type: - string - 'null' description: The free-text note about the entry. Always present, null when not set. category: type: string enum: - container - pallet - truck - trailer region: type: - string - 'null' enum: - europe - north_america - asia - oceania - null description: Where this entry is typically used, grouping it for display. Always present, null when not set. The set may grow; tolerate values you do not know. units: $ref: '#/components/schemas/EntryUnits' geometry: $ref: '#/components/schemas/Box' tareWeight: type: - number - 'null' revision: type: integer format: int32 description: Starts at 1; every PUT adds 1. updatedAt: type: string format: date-time Problem: type: object description: 'RFC 9457 `problem+json`. `code` is stable & machine-readable when present; `errors[].pointer` locates the offending field, query parameter or header. Two `/v2` errors have an **empty body** instead: `401` and the idempotency `409`. See [Errors & troubleshooting](errors.html).' properties: type: type: string format: uri description: 'A reference for the response''s status code, not for the specific failure: two failures with different `code` values share one `type`. RFC 9457 permits `about:blank` here; this API never sends that, and the `429` responses carry no `type` at all. Switch on `code`, never on this field.' title: type: string status: type: integer format: int32 detail: type: string description: Human-readable explanation, present on some errors and often naming the limit value that was exceeded. Never switch on it; switch on code. code: $ref: '#/components/schemas/ProblemCode' errors: type: array description: Field-level problems. Only on a 400 `validation_failed`. items: type: object properties: pointer: type: string description: For a body field, a JSON Pointer (RFC 6901) into the request body, over the member names as this contract spells them (camelCase, also when you sent another case), e.g. /carriers/0/externalId. A name you chose yourself (a group name, an overrides member) appears as sent, with ~ written as ~0 and / as ~1. For a query parameter or a header, its name without a leading /, e.g. limit or Idempotency-Key. An empty string means the body as a whole. code: type: string enum: - validation_failed - ambiguous_source - library_entry_not_found - override_field_not_allowed description: 'validation_failed, unless a field code applies: ambiguous_source or library_entry_not_found on the libraryId of a job carrier or cargo line, or override_field_not_allowed on its overrides.' message: type: string required: - title - status ItemConstraints: type: object description: Per-cargo-line behavioural rules, on an inline cargo line and on a SKU. Carriers have no constraints field in this API; only a cargo entry carries one. `geometry.type` and the fields of `constraints` may grow; tolerate values and fields you do not know. properties: allowStackingOnTop: type: - boolean - 'null' default: false description: Whether other cargo may be placed directly on top of this line. allowedRotations: type: - array - 'null' items: type: string enum: - X - Y - Z uniqueItems: true maxItems: 3 default: - Z description: 'Axes the box may be turned 90° about, relative to the submitted orientation. This is the only rotation control; there is no tilt switch. Omitted → upright, free to spin about Z (equivalent to ["Z"]); [] explicitly locks the box in the submitted orientation with no rotation at all. Each axis at most once: naming one twice is rejected with 400 against this field.' CargoGeometry: description: 'The geometry of a cargo unit, on an inline cargo line and on a SKU (its input and the stored entry): one schema, told apart by `type`, with `box` its only member today. A carrier''s geometry is a `Box`. `geometry.type` and the fields of `constraints` may grow; tolerate values and fields you do not know.' oneOf: - $ref: '#/components/schemas/Box' discriminator: propertyName: type mapping: box: '#/components/schemas/Box' SkuEntryInput: type: object required: - name - geometry - weight description: 'The body of `POST` and `PUT` on your company SKU library: one cargo unit, in its own units. It meets the rules an inline cargo line meets (the same geometry, weight, category and constraints rules), measured in the entry''s own units.' properties: externalId: type: - string - 'null' minLength: 1 maxLength: 128 description: 'Optional code from your own system, such as the SKU code of your product master. Unique within the SKU library when set, and case-sensitive: `SKU-1` and `sku-1` are two codes. A code another SKU already has answers `409 external_id_taken`. A cargo line that references the SKU inherits it unless the line sets its own.' name: type: string minLength: 1 maxLength: 256 description: A cargo line that references the SKU and sends no `description` label is labelled with this name. note: type: - string - 'null' minLength: 1 maxLength: 1000 description: 'Optional free-text note about the SKU, kept in the library: a job that references it does not copy it. Not to be confused with a cargo line''s `description`, which is that line''s label.' category: type: - string - 'null' enum: - box - null description: 'Optional. The kind the SKU is drawn as: `box`, as on a cargo line, matched in any case. Omit it and `box` is stored. A cargo line that references the SKU takes it from here.' units: anyOf: - $ref: '#/components/schemas/EntryUnits' - type: 'null' geometry: $ref: '#/components/schemas/CargoGeometry' weight: type: number minimum: 0 description: Per-unit weight, in the entry's `units.weight`. At most 100000 kg after conversion. constraints: anyOf: - $ref: '#/components/schemas/ItemConstraints' - type: 'null' description: 'Optional. Stored filled in: a member you leave out is stored with its default (`allowStackingOnTop: false`, `allowedRotations: ["Z"]`), so what you read back is exactly what a job referencing the SKU gets.' LibraryEntryPage: type: object description: One page of `GET /libraries/{id}/entries`. required: - items - nextCursor properties: items: type: array items: $ref: '#/components/schemas/LibraryEntry' nextCursor: type: - string - 'null' pattern: ^[A-Za-z0-9_-]+$ description: 'Pass it as `cursor` for the next page; null on the last page. Opaque: do not build or parse it.' SkuConstraints: type: object required: - allowStackingOnTop - allowedRotations description: 'A SKU''s constraints as stored: always filled in, so this is exactly what a job referencing the SKU gets unless the job line overrides a field. The fields may grow; tolerate fields you do not know.' properties: allowStackingOnTop: type: boolean description: Whether other cargo may be placed directly on top of this unit. allowedRotations: type: array items: type: string enum: - X - Y - Z uniqueItems: true maxItems: 3 description: The axes the unit may be turned 90° about, uppercase and in X, Y, Z order. `[]` means it is never turned. LibraryEntry: description: 'One library entry: a carrier or a SKU, told apart by `type`. Every field of either shape is always present, `null` when not set. A carrier library holds only carriers and the SKU library only SKUs.' oneOf: - $ref: '#/components/schemas/CarrierLibraryEntry' - $ref: '#/components/schemas/SkuLibraryEntry' discriminator: propertyName: type mapping: carrier: '#/components/schemas/CarrierLibraryEntry' sku: '#/components/schemas/SkuLibraryEntry' Box: type: object required: - type - dimensionX - dimensionY - dimensionZ description: 'Box geometry on a carrier, a cargo line or a library entry. X = length, Y = width, Z = height, in units.length (an entry''s own units on a library entry). Box is the only geometry this API implements; any other `type` is rejected with 400. `geometry.type` and the fields of `constraints` may grow; tolerate values and fields you do not know. Each dimension must be greater than 0 and at most **100 m** after conversion from `units.length`, so the accepted range depends on the unit you declared: 100000 in `mm`, 100 in `m`, 328.08 in `ft`. The bound is not a fixed `maximum` in this schema for that reason. A value past it is rejected with `400` against the field you sent (e.g. `/cargo/0/geometry/dimensionX`). Dimensions are handled to the nearest millimetre, so a positive value below half a millimetre is rejected too.' properties: type: const: box dimensionX: type: number exclusiveMinimum: 0 dimensionY: type: number exclusiveMinimum: 0 dimensionZ: type: number exclusiveMinimum: 0 examples: NotEntitled: summary: The key is valid; the plan is not value: type: https://tools.ietf.org/html/rfc9110#section-15.5.4 title: The account's plan does not include the customer API. status: 403 code: customer_api_not_entitled detail: 'Upgrade the account to a plan that includes customer-API access: https://app.loadoptimizer.ai/home/billing' ExternalIdTaken: summary: Another SKU in the SKU library already has this externalId value: type: https://tools.ietf.org/html/rfc9110#section-15.5.10 title: An entry with this externalId already exists. status: 409 code: external_id_taken detail: externalId is unique within a SKU library. Update that entry, or use another code. responses: LibraryNotFound: description: No library you can use has this id. Another account's library answers exactly like one that does not exist. On a plan that lacks one of the company library types, a UUID that names none of your libraries answers `403 libraries_not_entitled` instead, because which type it would be cannot be told without a lookup. content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: libraryNotAccessible: summary: Unknown, or not yours value: type: https://tools.ietf.org/html/rfc9110#section-15.5.5 title: Library not found. status: 404 code: library_not_found Forbidden: description: 'The API key is valid, but the account''s plan does not include customer-API access (`code: "customer_api_not_entitled"`). Permanent, not transient: retrying will not help; the account''s plan must add customer-API access first.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: notEntitled: $ref: '#/components/examples/NotEntitled' LibrariesForbidden: description: 'Either your plan has no customer-API access at all (`customer_api_not_entitled`), or it does not include the company library the id names (`libraries_not_entitled`): a type is available when the plan includes company libraries and its own size limit is not 0. The second is answered before any lookup, so it never tells you whether a library exists.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: notEntitled: $ref: '#/components/examples/NotEntitled' librariesNotEntitled: summary: The plan does not include company libraries value: type: https://tools.ietf.org/html/rfc9110#section-15.5.4 title: Company libraries are not available on your plan. status: 403 code: libraries_not_entitled detail: Your plan does not include company libraries. RateLimited: description: 'Too many requests, too fast (`code: "rate_limit_exceeded"`). The account may make **300 requests per minute** across `/v2`, replenished continuously, and every operation except `GET /health` counts. The request was not carried out: wait the number of seconds in the **`Retry-After`** header, then send it again. A full queue and a used-up monthly quota answer `429` on `POST /jobs` only.' headers: Retry-After: description: Seconds to wait before retrying. schema: type: integer format: int32 content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: rateLimited: summary: Too many requests too fast value: title: Too many requests. status: 429 code: rate_limit_exceeded detail: The account exceeded the request rate for the customer API. Retry after the number of seconds in the Retry-After header. LibraryWriteForbidden: description: Either your plan has no customer-API access at all (`customer_api_not_entitled`), or it does not include the company library the id names (`libraries_not_entitled`; a type is available when the plan includes company libraries and its own size limit is not 0), or the id names the system catalogue, which only we change (`library_read_only`). The system-catalogue check needs no lookup and runs first, so a `POST`, `PUT` or `DELETE` on `carrier_system_default` always answers `library_read_only`, before the body or `Idempotency-Key` is read at all. content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: notEntitled: $ref: '#/components/examples/NotEntitled' librariesNotEntitled: summary: The plan does not include company libraries value: type: https://tools.ietf.org/html/rfc9110#section-15.5.4 title: Company libraries are not available on your plan. status: 403 code: libraries_not_entitled detail: Your plan does not include company libraries. libraryReadOnly: summary: The library named by the id is the system catalogue value: type: https://tools.ietf.org/html/rfc9110#section-15.5.4 title: This library is read-only. status: 403 code: library_read_only detail: System libraries can be read and referenced in jobs, not changed. ValidationProblem: description: 'Validation failed: `code: "validation_failed"`, with `errors[]` naming each offending field, query parameter or header. Every 400 on `/v2` has this shape; see Errors & troubleshooting.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: invalidGeometry: summary: A cargo line with a geometry type the API does not implement value: type: https://tools.ietf.org/html/rfc9110#section-15.5.1 title: Validation failed. status: 400 code: validation_failed errors: - pointer: /cargo/0/geometry/type code: validation_failed message: Only 'box' geometry is supported. ServerProblem: description: 'The request could not be completed for a reason the API did not anticipate. Transient as far as a caller can tell: retry, and if it persists contact support. The cause is recorded internally and is deliberately absent from the body, so there is nothing here to branch on beyond `code`.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: internalServerError: summary: An unanticipated failure while handling the request value: type: https://tools.ietf.org/html/rfc9110#section-15.6.1 title: Internal server error. status: 500 code: internal_server_error detail: The request could not be completed. Retry; if it keeps failing, contact support. Unauthorized: description: Missing, invalid or revoked API key. Empty body. UnsupportedMediaType: description: 'The request body was sent under a content type other than `application/json`, or without one (`code: "unsupported_media_type"`). These operations read JSON only.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: wrongContentType: summary: A body sent as something other than JSON value: type: https://tools.ietf.org/html/rfc9110#section-15.5.16 title: Unsupported media type. status: 415 code: unsupported_media_type detail: Send the request body as application/json. LibraryEntryNotFound: description: The library is not one you can use (`library_not_found`), or it has no entry with this id (`library_entry_not_found`). content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: libraryItemNotFound: summary: No such entry in this library value: type: https://tools.ietf.org/html/rfc9110#section-15.5.5 title: Library entry not found. status: 404 code: library_entry_not_found PayloadTooLarge: description: 'The request body is larger than the API will read (`code: "payload_too_large"`). Permanent for that body: split the order into smaller jobs rather than retrying it unchanged.' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' examples: bodyTooLarge: summary: A body past the size the API reads value: type: https://tools.ietf.org/html/rfc9110#section-15.5.14 title: Payload too large. status: 413 code: payload_too_large detail: The request body is larger than this API accepts. Split the order into smaller jobs. parameters: EntryId: name: entryId in: path required: true description: The id of one entry. This is the value a job carrier or cargo line puts in its `libraryId` field. schema: type: string maxLength: 64 LibraryId: name: id in: path required: true description: 'The id of a library, as `GET /libraries` lists it: the system library''s slug `carrier_system_default`, or the id (a UUID) of one of your company libraries, carriers or SKUs. A job carrier or cargo line names an entry, not a library: its `libraryId` field holds an entry''s id, and the id of one of your libraries there is rejected with `400`, field code `library_entry_not_found`, with a message that says it named a library.' schema: type: string maxLength: 64 IdempotencyKey: name: Idempotency-Key in: header required: false description: 'Resubmitting the same key + same body returns the original job (on `POST /jobs`) or the original entry (on `POST /libraries/{id}/entries`) rather than creating a duplicate; same key + a different body → `409` (empty body). The whole submitted body is the fingerprint, values as sent (an enum value in another case is a different body), `units` included: no field is exempt. The replay and the `409` hold while the key is kept: on `POST /jobs` for at least 24 hours after the job was submitted, and for as long as it is queued or running, after which the same key can start a new job; on entry creation as long as that entry exists, so a retry after the entry was deleted creates a new entry. Max 200 chars; an oversized key is rejected with `400` `validation_failed`, pointer `Idempotency-Key`.' schema: type: string maxLength: 200 securitySchemes: ApiKey: type: apiKey in: header name: X-Api-Key description: 'Customer API key bound to your account, e.g. `X-Api-Key: ck_live_...`. Create it in the web app (see https://app.loadoptimizer.ai/docs/api-keys); the full key is shown only once.' x-provenance: method: harvested publisher: LoadOptimizer.ai source: https://www.loadoptimizer.ai/docs/loadoptimizer-openapi.yaml note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json); stamped by stamp-openapi-provenance.py (roadmap#793)