Skip to main content
The orders API lets you place new orders and move each order through its lifecycle: from pending to brewing, then ready, and finally picked-up. Orders can also be cancelled from pending or brewing states.

Order status lifecycle

pending → brewing → ready → picked-up
   ↓          ↓
cancelled  cancelled
Valid transitions are:
FromTo
pendingbrewing, cancelled
brewingready, cancelled
readypicked-up
picked-up— (terminal)
cancelled— (terminal)

CoffeeOrder schema

All order endpoints return a CoffeeOrder object. Its fields are:
id
string
required
Unique order identifier. Format: order-N (e.g., order-1, order-42).
customerName
string
required
Name provided by the customer when the order was placed.
drinkId
string
required
The drink ordered. One of: espresso, americano, latte, cappuccino, cold-brew, tea.
drinkName
string
required
Human-readable name of the drink (e.g., "Latte").
size
string
required
Size of the drink. One of: small, medium, large.
milk
string
required
Milk choice. One of: whole, oat, almond, none.
temperature
string
required
Temperature preference. One of: hot, iced, extra-hot.
shots
integer
required
Number of espresso shots.
notes
string
Optional free-text notes from the customer (e.g., allergy information).
status
string
required
Current order status. One of: pending, brewing, ready, picked-up, cancelled.
priceCents
integer
required
Final calculated price in US cents, including size multiplier and any extra shot surcharges.
createdAt
string
required
ISO 8601 UTC timestamp of when the order was placed (e.g., "2026-04-10T14:30:00Z").

POST /orders

Place a new coffee order. The server validates the drink ID, size, milk, and temperature against the menu and returns a CoffeeOrder with status pending.

Request body

customerName
string
required
Name of the customer placing the order.
drinkId
string
required
ID of the drink to order. Must be one of the IDs returned by GET /menu: espresso, americano, latte, cappuccino, cold-brew, tea.
size
string
required
Desired size. One of: small, medium, large.
milk
string
Milk preference. One of: whole, oat, almond, none. Defaults to the drink’s default milk when omitted.
temperature
string
Temperature preference. One of: hot, iced, extra-hot. Defaults to the drink’s first available temperature when omitted.
shots
integer
Number of espresso shots. Must not exceed the drink’s maxShots. Defaults to 1 for espresso-based drinks and 0 for tea.
notes
string
Free-text notes to attach to the order (e.g., "extra foam please").

Response

Returns a CoffeeOrder object with status: "pending".

Errors

_tagHTTP statusDescription
DrinkNotFoundError404The drinkId does not match any menu item
InvalidOrderInputError400The request body failed validation (e.g., invalid size or milk for that drink)
InternalAppError500An unexpected server error occurred

Example

curl -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -d '{
    "customerName": "Alice",
    "drinkId": "latte",
    "size": "medium",
    "milk": "oat",
    "temperature": "hot",
    "shots": 2,
    "notes": "extra foam please"
  }'
{
  "id": "order-1",
  "customerName": "Alice",
  "drinkId": "latte",
  "drinkName": "Latte",
  "size": "medium",
  "milk": "oat",
  "temperature": "hot",
  "shots": 2,
  "notes": "extra foam please",
  "status": "pending",
  "priceCents": 593,
  "createdAt": "2026-04-10T14:30:00.000Z"
}

GET /orders

Returns a list of all orders, optionally filtered by status.

Query parameters

status
string
Filter results to orders with this status. One of: pending, brewing, ready, picked-up, cancelled. Omit to return all orders.

Response

An array of CoffeeOrder objects.

Errors

_tagHTTP statusDescription
InvalidOrderInputError400The status query parameter has an unrecognised value
InternalAppError500An unexpected server error occurred

Examples

List all orders:
curl http://localhost:3000/orders
List only orders that are ready for pick-up:
curl "http://localhost:3000/orders?status=ready"
[
  {
    "id": "order-3",
    "customerName": "Bob",
    "drinkId": "cappuccino",
    "drinkName": "Cappuccino",
    "size": "small",
    "milk": "whole",
    "temperature": "hot",
    "shots": 1,
    "status": "ready",
    "priceCents": 425,
    "createdAt": "2026-04-10T14:15:00.000Z"
  }
]

GET /orders/:orderId

Retrieve a single order by its ID.

Path parameters

orderId
string
required
The order ID to retrieve. Must match the pattern order-N (e.g., order-1).

Response

A single CoffeeOrder object.

Errors

_tagHTTP statusDescription
OrderNotFoundError404No order with the given orderId exists
InternalAppError500An unexpected server error occurred

Example

curl http://localhost:3000/orders/order-1
{
  "id": "order-1",
  "customerName": "Alice",
  "drinkId": "latte",
  "drinkName": "Latte",
  "size": "medium",
  "milk": "oat",
  "temperature": "hot",
  "shots": 2,
  "notes": "extra foam please",
  "status": "pending",
  "priceCents": 593,
  "createdAt": "2026-04-10T14:30:00.000Z"
}

POST /orders/:orderId/start-brewing

Transitions the order from pending to brewing. Call this when a barista picks up the order and begins preparing it.

Path parameters

orderId
string
required
The ID of the order to start brewing. Must match the pattern order-N.

Response

The updated CoffeeOrder object with status: "brewing".

Errors

_tagHTTP statusDescription
OrderNotFoundError404No order with the given orderId exists
InvalidOrderStatusTransitionError409The order is not in pending status and cannot be moved to brewing
InternalAppError500An unexpected server error occurred

Example

curl -X POST http://localhost:3000/orders/order-1/start-brewing
{
  "id": "order-1",
  "customerName": "Alice",
  "drinkId": "latte",
  "drinkName": "Latte",
  "size": "medium",
  "milk": "oat",
  "temperature": "hot",
  "shots": 2,
  "notes": "extra foam please",
  "status": "brewing",
  "priceCents": 593,
  "createdAt": "2026-04-10T14:30:00.000Z"
}

POST /orders/:orderId/mark-ready

Transitions the order from brewing to ready. Call this when the drink is prepared and waiting for the customer.

Path parameters

orderId
string
required
The ID of the order to mark as ready. Must match the pattern order-N.

Response

The updated CoffeeOrder object with status: "ready".

Errors

_tagHTTP statusDescription
OrderNotFoundError404No order with the given orderId exists
InvalidOrderStatusTransitionError409The order is not in brewing status and cannot be moved to ready
InternalAppError500An unexpected server error occurred

Example

curl -X POST http://localhost:3000/orders/order-1/mark-ready
{
  "id": "order-1",
  "customerName": "Alice",
  "drinkId": "latte",
  "drinkName": "Latte",
  "size": "medium",
  "milk": "oat",
  "temperature": "hot",
  "shots": 2,
  "notes": "extra foam please",
  "status": "ready",
  "priceCents": 593,
  "createdAt": "2026-04-10T14:30:00.000Z"
}

POST /orders/:orderId/pick-up

Transitions the order from ready to picked-up. Call this when the customer collects their drink. This is a terminal state — no further transitions are possible.

Path parameters

orderId
string
required
The ID of the order being picked up. Must match the pattern order-N.

Response

The updated CoffeeOrder object with status: "picked-up".

Errors

_tagHTTP statusDescription
OrderNotFoundError404No order with the given orderId exists
InvalidOrderStatusTransitionError409The order is not in ready status and cannot be moved to picked-up
InternalAppError500An unexpected server error occurred

Example

curl -X POST http://localhost:3000/orders/order-1/pick-up
{
  "id": "order-1",
  "customerName": "Alice",
  "drinkId": "latte",
  "drinkName": "Latte",
  "size": "medium",
  "milk": "oat",
  "temperature": "hot",
  "shots": 2,
  "notes": "extra foam please",
  "status": "picked-up",
  "priceCents": 593,
  "createdAt": "2026-04-10T14:30:00.000Z"
}

POST /orders/:orderId/cancel

Cancels an order. This is allowed from pending or brewing states. Once an order reaches ready or picked-up, it can no longer be cancelled.

Path parameters

orderId
string
required
The ID of the order to cancel. Must match the pattern order-N.

Response

The updated CoffeeOrder object with status: "cancelled".

Errors

_tagHTTP statusDescription
OrderNotFoundError404No order with the given orderId exists
InvalidOrderStatusTransitionError409The order is in a state that does not allow cancellation (e.g., ready or picked-up)
InternalAppError500An unexpected server error occurred

Example

curl -X POST http://localhost:3000/orders/order-1/cancel
{
  "id": "order-1",
  "customerName": "Alice",
  "drinkId": "latte",
  "drinkName": "Latte",
  "size": "medium",
  "milk": "oat",
  "temperature": "hot",
  "shots": 2,
  "notes": "extra foam please",
  "status": "cancelled",
  "priceCents": 593,
  "createdAt": "2026-04-10T14:30:00.000Z"
}
To test the full lifecycle, place an order with POST /orders, then call start-brewing, mark-ready, and pick-up in sequence using the id from each response.

Build docs developers (and LLMs) love