---
title: "Price a batch of shipments"
method: POST
path: "/v1/shipper/quotes/batch"
tags: ["Shipper API"]
---

# Price a batch of shipments

`POST /v1/shipper/quotes/batch`

## Request

- Base URL: `https://api.oway.io`
- URL: `https://api.oway.io/v1/shipper/quotes/batch`
- Auth: HTTP bearer and API key in header `x-oway-api-key`

Accepts up to 1,000 quote requests and prices them after answering. The response is the batch in status `PRICING` with every item `PENDING`; read it at `GET /v1/shipper/quotes/batch/{batchId}` (the `Location` header) after `Retry-After` seconds until its status is `COMPLETED` or `FAILED`.

Each item is exactly a quote request and is priced as its own quote request would be. Results come back in the order you sent the items, so the position is how you match them; an item's `customerReference`, when you give one, is stored on its quote and finds it later. Each result is `QUOTED` with the same `QuoteResponse` a single request answers with, or `NOT_QUOTED` or `UNDER_REVIEW` with the same `ProblemDetail`. A refused or malformed item never fails the batch; only an envelope that cannot be taken does (no items, more than 1,000, or an empty item).

Idempotent twice over. With an `Idempotency-Key`, retrying the same request within 24h returns the original response; the same key with a different body returns 409 `idempotency_key_conflict`. Without one, the same items for your company within 24h return the batch that already ran, however far it has got; quotes stay valid for two days, so a replay never returns an expired price. One batch prices for your account at a time: a second, different batch while one is `PRICING` is refused with 409 `batch_in_progress` and the running batch's id.

`UNDER_REVIEW` items are the ones no automatic price can be trusted for; a person decides, and the item later becomes `QUOTED` or `NOT_QUOTED`. After a batch is `COMPLETED`, items change only that way and nothing else on the batch moves. The `requiredPickupDate` of an item is read as a day in the origin's time zone; send it with an offset, for example `2026-10-14T08:00:00-07:00`.

## Headers

- `Idempotency-Key` string

## Request body

- QuoteBatchRequest: A set of shipments to price together. The batch is accepted at once and priced afterwards; read it until its status is COMPLETED or FAILED.
  - `customerReference` string: Your own reference for the whole set, such as a plan or file name. Stored on the batch only; each item carries its own `customerReference`, or none.
  - `items` QuoteRequest[], required: The shipments to price: each one exactly a quote request. 1 to 1,000. Results come back in this order, so the position is how you match them; give an item a `customerReference` to find its quote later by your own identifier.
    - `pickupAddress` Address, required: Address information for pickup or delivery locations
      - `name` string, required: Name of the location or business
      - `address1` string, required: Primary street address
      - `address2` string: Secondary address line (suite, unit, etc.)
      - `city` string, required: City name
      - `state` string, required: Two-letter subdivision code. A request must name a US state, territory or DC; a response may name the subdivision of another country, which country names.
      - `zipCode` string, required: 5-digit ZIP code. The placeholders 00000 and 99999 are rejected: neither is an assignable USPS code, and both are what a caller sends when a numeric ZIP field defaults to zero or saturates.
      - `country` string: Two-letter ISO 3166-1 alpha-2 country code, case-insensitive and normalized to upper case. Defaults to US when omitted. Oway currently serves the US and its territories, so US, PR, GU, VI, AS and MP are accepted and any other recognized code is rejected with 400 rather than quoted. A territory may also be addressed the way a carrier would, as country US with the territory in state.
      - `phoneNumber` string, required: Contact phone number in E.164 format
      - `contactPerson` string, required: Name of the contact person at this location
      - `phoneExtension` string: Phone extension, dialled after the main number
      - `email` string, email: Contact email for this location
      - `openTime` string: Opening time for the location in 24-hour format (HH:mm). Defaults to 10:00 if not provided.
      - `closeTime` string: Closing time for the location in 24-hour format (HH:mm). Defaults to 16:00 if not provided.
      - `notes` string: Additional notes or instructions for the driver
      - `liftgateRequired` boolean: Whether a liftgate is required at this location
      - `limitedAccess` boolean: Whether this is a limited access location (residential, construction site, etc.)
      - `appointmentRequired` boolean: Whether an appointment is required for pickup/delivery
      - `callAheadRequired` boolean: Whether a call-ahead notification is required before pickup/delivery. When true, the carrier will call the contact person before arriving at the location.
      - `lumperRequired` boolean: Whether third-party lumper labor loads/unloads at this location (common at large distribution centers where drivers may not handle freight). Informational for quoting: the lumper fee is not priced into the quote and is billed after delivery as a separate charge with proof of payment.
      - `insideRequired` boolean: Whether the driver must bring freight inside the facility rather than to the dock or curb. Adds a flat inside-service fee to the quote.
    - `deliveryAddress` Address, required: Address information for pickup or delivery locations
      - `name` string, required: Name of the location or business
      - `address1` string, required: Primary street address
      - `address2` string: Secondary address line (suite, unit, etc.)
      - `city` string, required: City name
      - `state` string, required: Two-letter subdivision code. A request must name a US state, territory or DC; a response may name the subdivision of another country, which country names.
      - `zipCode` string, required: 5-digit ZIP code. The placeholders 00000 and 99999 are rejected: neither is an assignable USPS code, and both are what a caller sends when a numeric ZIP field defaults to zero or saturates.
      - `country` string: Two-letter ISO 3166-1 alpha-2 country code, case-insensitive and normalized to upper case. Defaults to US when omitted. Oway currently serves the US and its territories, so US, PR, GU, VI, AS and MP are accepted and any other recognized code is rejected with 400 rather than quoted. A territory may also be addressed the way a carrier would, as country US with the territory in state.
      - `phoneNumber` string, required: Contact phone number in E.164 format
      - `contactPerson` string, required: Name of the contact person at this location
      - `phoneExtension` string: Phone extension, dialled after the main number
      - `email` string, email: Contact email for this location
      - `openTime` string: Opening time for the location in 24-hour format (HH:mm). Defaults to 10:00 if not provided.
      - `closeTime` string: Closing time for the location in 24-hour format (HH:mm). Defaults to 16:00 if not provided.
      - `notes` string: Additional notes or instructions for the driver
      - `liftgateRequired` boolean: Whether a liftgate is required at this location
      - `limitedAccess` boolean: Whether this is a limited access location (residential, construction site, etc.)
      - `appointmentRequired` boolean: Whether an appointment is required for pickup/delivery
      - `callAheadRequired` boolean: Whether a call-ahead notification is required before pickup/delivery. When true, the carrier will call the contact person before arriving at the location.
      - `lumperRequired` boolean: Whether third-party lumper labor loads/unloads at this location (common at large distribution centers where drivers may not handle freight). Informational for quoting: the lumper fee is not priced into the quote and is billed after delivery as a separate charge with proof of payment.
      - `insideRequired` boolean: Whether the driver must bring freight inside the facility rather than to the dock or curb. Adds a flat inside-service fee to the quote.
    - `orderComponents` OrderComponent[], required: List of pallets or freight pieces.
      - `palletCount` integer, required: Number of pallets in this component
      - `poundsWeight` integer, required: Weight per pallet in pounds
      - `palletDimensions` integer[]: Pallet dimensions as [height, length, width] in inches. Deprecated: prefer the 'dimensions' object. Optional: if both 'palletDimensions' and 'dimensions' are omitted, the API defaults to 40 x 48 x 60 in. (length x width x height).
      - `dimensions` Dimensions: Pallet dimensions in inches. All fields optional: if any are omitted the entire dimensions object is treated as missing and the API default of 40 x 48 x 60 in. (length x width x height) is applied.
        - `length` integer: Length in inches
        - `width` integer: Width in inches
        - `height` integer: Height in inches
      - `description` string: Per-component freight description (e.g. NMFC item name)
      - `nmfcCode` string: NMFC commodity code for this component
      - `freightClass` string: LTL freight class declared by the shipper. When supplied it is authoritative; Oway derives a class from density only when this is absent.
      - `packagingType` string: Packaging type for this component (e.g. Pallets, Boxes, Crates)
      - `pieceCount` integer: Number of pieces (loose items) in this component, distinct from pallet count
    - `requiredPickupDate` string, date-time: Required pickup date (ISO 8601), read as a day in the pickup location's time zone. Send it with that zone's offset so the day is unambiguous.
    - `shipmentMode` 'ltl' | 'ftl': Shipment mode (LTL or FTL).
    - `customerReference` string: Your own reference for this load, such as your order or load number, at most 64 characters. Stored on the quote, so a later shipment can name it and `GET /v1/shipper/quotes/by-reference` can find the quote.

## Response `201`

Batch accepted and pricing. `Location` is where to read it.

- QuoteBatch: A batch of quote requests and what became of each. The same body comes back from creating the batch and from reading it, so read it until `status` is COMPLETED or FAILED.
  - `id` string: The batch id; read the batch by it
  - `status` 'PRICING' | 'COMPLETED' | 'FAILED', required: PRICING: accepted, still pricing; read the batch again after `Retry-After` seconds. COMPLETED: every item has an outcome; items can still move from UNDER_REVIEW to QUOTED or NOT_QUOTED, and nothing else on the batch changes. FAILED: the run could not finish; submit the items again.
  - `customerReference` string: Your reference for the whole set, as sent
  - `createdAt` string, date-time: When the batch was accepted
  - `completedAt` string, date-time: When every item had an outcome; absent while PRICING
  - `counts` QuoteBatchCounts: How many items ended each way
    - `items` integer: Items in the batch
    - `quoted` integer: Items with a price you can book
    - `notQuoted` integer: Items deliberately not priced
    - `underReview` integer: Items a person will decide
    - `pending` integer: Items not priced yet
  - `items` QuoteBatchItemResult[]: One entry per item, in the order you sent them; match by position
    - `customerReference` string: Your own reference for the load, as sent on the item and stored on the quote; absent when the item carried none. Results are in request order, so the position is the match.
    - `status` 'PENDING' | 'QUOTED' | 'NOT_QUOTED' | 'UNDER_REVIEW', required: The outcome
    - `quote` QuoteResponse: Response containing a shipping quote
      - `id` string: Unique quote identifier
      - `quotedPriceInCents` integer: Total quoted price in cents (USD)
      - `quoteExpirationTime` string, date-time: When this quote expires (ISO 8601 format). Quotes are valid for 2 days.
      - `transitDays` integer, nullable: Number of business days to deliver the shipment after pickup. Pickup day is excluded; weekends are not counted. May be null when the ETA cannot be computed.
      - `estimatedDeliveryDate` string, date-time, nullable: Estimated delivery date and time (ISO 8601 format, UTC). May be null when the ETA cannot be computed.
    - `problem` ProblemDetail: RFC 9457 Problem Details error response
      - `type` string: A URI reference that identifies the problem type
      - `title` string: A short, human-readable summary of the problem type
      - `status` integer: The HTTP status code
      - `detail` string: A human-readable explanation specific to this occurrence of the problem
      - `reason` string: A machine-readable reason code for programmatic handling
      - `violations` Violation[]: Per-field validation failures when a request is rejected for one or more invalid fields. Present only on validation-style errors.
        - `field` string: Field path that failed validation
        - `entity` string: Entity the failing field belongs to (e.g. pickupAddress, deliveryAddress, appointment). Lets clients disambiguate otherwise-identical field paths across stops. Nullable for violations that have no entity context.
        - `reason` string: Human-readable reason the field is invalid. Any dimension or weight ceiling quoted in the message is configuration-driven, so treat the figures in the example as indicative rather than as a fixed part of the contract. The example is one rejection rather than every one: it shows a shipment that requested no liftgate. A shipment whose pickup or delivery requires a liftgate is measured against a different, generally tighter, set of dimension ceilings, and its message quotes whichever figures applied.

## Other responses

- `400`: Invalid envelope (`reason: invalid_batch`): no items, more than 1,000, or an empty item. Per-item problems are never a 400; they are the item's result.
- `401`: Unauthorized: missing or invalid Bearer token (authentication failed)
- `403`: Forbidden: missing/invalid API key, or user does not belong to the authorized company
- `409`: Conflict. `reason: batch_in_progress`: a different batch is already pricing for your account; its id is in `detail`. `reason: idempotency_key_conflict`: the `Idempotency-Key` was used with a different body.
- `500`: Internal server error

---

[API](https://skmtc.dev/shipoway/apis/oway-shipper-api.md) - [All operations](https://skmtc.dev/shipoway/apis/oway-shipper-api/llms.txt) - [OpenAPI document](https://skmtc.dev/shipoway/apis/oway-shipper-api/revisions/35141ac5fb57?raw)
