Orders

The customer-facing order lifecycle: create an order, track the driver, confirm delivery, and read receipts. Paths are relative to the /api/v1 prefix; all routes require Authorization: Bearer {token}.

Driver bidding (accept / counter-offer) and the client accept/reject of offers live under Order offers (/orders/{id}/offer, /orders/{id}/accept, /counter-offers/{id}/accept). Driver-side listings live under Drivers (/driver/orders, /driver/online). Both are documented separately.

See Common errors and the order lifecycle.

Endpoints

Method URI Role Description
POST /orders CLIENT Create an order (starts broadcasting)
GET /orders CLIENT Paginated order history (cursor)
GET /orders/active CLIENT The caller's current active order
GET /orders/history CLIENT Recent orders (flat list)
POST /orders/{orderId}/start DRIVER Start the assigned trip
POST /orders/{orderId}/location DRIVER Push the driver's live location
POST /orders/{orderId}/arrived DRIVER Mark arrival at the destination
GET /orders/{orderId}/tracking CLIENT Live driver position, distance & ETA
POST /orders/{orderId}/deliver DRIVER Confirm delivery with the PIN
PATCH /orders/{orderId}/payment-method CLIENT Change the order's payment method
POST /orders/{orderId}/cancel any Cancel the order
GET /orders/{orderId}/receipt CLIENT Accounting receipt (delivered orders)
GET /orders/{orderId} any Order detail (owner or assigned driver)

The order object

Single-order responses wrap the order under an order key. The payload is the order record filtered by role:

delivery_pin is additionally only present once the order is paid (CONFIRMED/IN_PROGRESS/ARRIVED).

Representative CLIENT view:

{
  "order": {
    "id": 42,
    "status": "CONFIRMED",
    "latitude": 10.487,
    "longitude": -66.88,
    "address": "Av. Principal, Las Mercedes",
    "liters_requested": 5000,
    "water_type": "POTABLE",
    "payment_method": "CASH_USD",
    "base_price_usd": 20.0,
    "client_solidary_usd": 0.2,
    "client_pays_usd": 20.2,
    "delivery_pin": "1234",
    "notes": null,
    "created_at": "2026-06-26T12:00:00+00:00"
  }
}

POST /orders

Creates an order and starts broadcasting to nearby online drivers. A customer may only have one active order at a time.

Request body

Field Type Required Rules
latitude number yes between −90 and 90
longitude number yes between −180 and 180
address string yes 3–280 chars
litersRequested integer yes 1–50000
basePrice number yes > 0, ≤ 100000
waterType string no POTABLE or INDUSTRIAL
paymentMethod string no an active method code or PAYMENT_FORM; defaults to CASH_USD
paymentFormId integer conditional required when paymentMethod=PAYMENT_FORM; must be an active payment form
notes string no ≤ 500 chars
accessInstructions string no ≤ 500 chars

Response 201{ "order": { … } } (CLIENT view). Cash orders start at CONFIRMED; online methods start at WAITING_FOR_PAYMENT.

Errors

Status Body When
409 { "error": "ActiveOrderExists", "message": "…" } The customer already has an active order

GET /orders

Cursor-paginated history for the authenticated customer. See Pagination → cursor-based.

Query: status, date_from, date_to, limit (1–100, default 25), cursor.

Response 200

{
  "items": [
    {
      "id": 42,
      "status": "DELIVERED",
      "liters_requested": 5000,
      "water_type": "POTABLE",
      "address": "Av. Principal, Las Mercedes",
      "client_pays_usd": 20.2,
      "payment_method": "CASH_USD",
      "driver": { "id": 3, "name": "Carlos Rodríguez" },
      "expires_at": "2026-06-26T12:05:00+00:00",
      "created_at": "2026-06-26T12:00:00+00:00",
      "delivered_at": "2026-06-26T12:40:00+00:00"
    }
  ],
  "next_cursor": "eyJpZCI6NDF9",
  "has_more": true
}

GET /orders/active

Returns the caller's current active order, or null.

Response 200{ "order": { … } } or { "order": null }. The delivery_pin is stripped unless the order is paid.


GET /orders/history

Recent orders as a flat list.

Query: limit (1–100, default 20).

Response 200{ "orders": [ { … } ] } (each entry is a CLIENT-view order).


POST /orders/{orderId}/start

Driver starts the assigned trip (CONFIRMEDIN_PROGRESS).

Response 200{ "order": { … } } (DRIVER view).

Status Body When
409 { "error": "Conflict", "message": "…" } Order not in a startable state

POST /orders/{orderId}/location

Push the driver's live GPS while serving an order (IN_PROGRESS/ARRIVED).

Request body

Field Type Required Rules
latitude number yes between −90 and 90
longitude number yes between −180 and 180

Response 204 No Content.

Status Body When
404 { "error": "OrderNotFound" } Unknown order
403 { "error": "Forbidden" } Caller is not the assigned driver
409 { "error": "WrongStatus", "message": "…" } Order not IN_PROGRESS/ARRIVED

POST /orders/{orderId}/arrived

Marks arrival. The driver must be within 500 m of the destination; passing latitude/longitude updates the live location first. Idempotent once ARRIVED.

Request body

Field Type Required Rules
latitude number no between −90 and 90
longitude number no between −180 and 180

Response 200

{ "ok": true, "status": "ARRIVED", "arrivedAt": "2026-06-26T12:35:00+00:00", "distanceMeters": 120.4 }

Errors

Status Body When
404 { "error": "OrderNotFound" } Unknown order
403 { "error": "Forbidden" } Caller is not the assigned driver
409 { "error": "WrongStatus", "message": "…" } Order not IN_PROGRESS
409 { "error": "NoLocation", "message": "…" } No recent driver location
409 { "error": "TooFarFromDestination", "message": "…", "distanceKm": 1.8 } Over 500 m away

GET /orders/{orderId}/tracking

Live driver position, straight-line distance, and a rough ETA (25 km/h). Only the order owner, only while CONFIRMED/IN_PROGRESS/ARRIVED.

Response 200

{
  "order": { "id": 42, "status": "IN_PROGRESS", "latitude": 10.487, "longitude": -66.88, "address": "Av. Principal", "expires_at": "2026-06-26T12:05:00+00:00" },
  "driver": {
    "id": 3,
    "name": "Carlos Rodríguez",
    "phone": "+584241001001",
    "currentLat": 10.49,
    "currentLng": -66.85,
    "lastLocationAt": "2026-06-26T12:34:00+00:00"
  },
  "distanceKm": 1.2,
  "estimatedMinutes": 3,
  "avgSpeedKmh": 25
}

driver is null (with distanceKm/estimatedMinutes null) until the driver has a known location.

Status Body When
403 { "error": "Forbidden" } Caller is not the order owner
409 { "error": "NoTracking", "message": "…" } Order not in a trackable state

POST /orders/{orderId}/deliver

Driver confirms delivery using the customer's 4-digit PIN.

Request body

Field Type Required Rules
actualLiters integer yes 1–50000
deliveryPin string yes exactly 4 digits

Response 200{ "ok": true }.

Status Body When
400 { "error": "DeliveryError", "message": "…PIN…" } Wrong PIN
409 { "error": "DeliveryError", "message": "…" } Order not deliverable

PATCH /orders/{orderId}/payment-method

Changes the order's payment method. Owner only.

Request body

Field Type Required Rules
paymentMethod string yes an active method code or PAYMENT_FORM
paymentFormId integer conditional required when paymentMethod=PAYMENT_FORM; must be an active payment form; ignored otherwise

Response 200{ "order": { … } } (CLIENT view) with the updated payment_method; client_pays_usd may change.

Errors

Status Body When
404 { "error": "OrderNotFound", "message": "…" } Unknown order or not owned by the caller
409 { "error": "PaymentMethodLocked", "message": "…" } Order past WAITING_FOR_PAYMENT, the client already reported a payment, or switching to cash after acceptance
422 { "error": "…" } Method not available, or missing/invalid paymentFormId

POST /orders/{orderId}/cancel

Cancels the order. Allowed for the order's customer or assigned driver (and admins).

Request body

Field Type Required Rules
reason string no ≤ 280 chars

Response 200{ "order": { … } } (filtered for the caller's role).

Status Body When
403 { "error": "CancelError", "message": "…" } Caller may not cancel this order
404 { "error": "CancelError", "message": "…" } Order not found
409 { "error": "CancelError", "message": "…" } Order not cancellable

GET /orders/{orderId}/receipt

Full accounting receipt for a delivered order, owner only.

Response 200

{
  "receipt": {
    "order_id": 42,
    "status": "DELIVERED",
    "delivered_at": "2026-06-26T12:40:00+00:00",
    "liters_requested": 5000,
    "actual_liters": 5000,
    "water_type": "POTABLE",
    "payment_method": "CASH_USD",
    "breakdown": {
      "base_price_usd": 20.0,
      "client_solidary_usd": 0.2,
      "client_pays_usd": 20.2,
      "aguita_commission_usd": 3.6,
      "driver_gross_usd": 16.4,
      "driver_solidary_usd": 0.16,
      "driver_receives_usd": 16.24,
      "solidary_fund_usd": 0.36
    },
    "driver": {
      "id": 3,
      "name": "Carlos Rodríguez",
      "phone": "+584241001001",
      "vehicle": { "plate": "ABC-123", "capacity_liters": 5000 }
    },
    "rating": { "score": 5, "comment": "Puntual", "tags": ["fast"], "created_at": "2026-06-26T12:45:00+00:00" }
  }
}

rating is null until the customer rates the order. See Business → money breakdown.

Status Body When
403 { "error": "Forbidden" } Caller is not the order owner
409 { "error": "OrderNotDelivered", "message": "…" } Order not delivered yet

GET /orders/{orderId}

Order detail for the owner or the assigned driver. The payload is role-filtered, and delivery_pin is present only when the order is paid.

For the client, each pending order.offers[] adds distance_km (number|null), eta_minutes (number|null), and driver_busy (boolean). offers[].driver exposes only public fields — raw coordinates are not included. Same shape on GET /orders/active.

Response 200{ "order": { … } }.

Status Body When
404 { "error": "OrderNotFound" } Unknown order
403 { "error": "Forbidden" } Caller is neither owner nor assigned driver

Examples

# Create (cash) → broadcasting/confirmed
curl -X POST /api/v1/orders \
  -H "Authorization: Bearer {client_token}" \
  -H "Content-Type: application/json" \
  -d '{"latitude": 10.487, "longitude": -66.88, "address": "Av. Principal", "litersRequested": 5000, "basePrice": 20}'

# Driver: live location, then mark arrived
curl -X POST /api/v1/orders/42/location \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"latitude": 10.4895, "longitude": -66.8805}'

# Driver: confirm delivery with the client's PIN
curl -X POST /api/v1/orders/42/deliver \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"actualLiters": 5000, "deliveryPin": "1234"}'