Order offers

Driver bidding and the client's accept/reject of offers. A driver either takes an order at its published price or sends a counter-offer (a custom price bid); each attempt creates an offer and the client confirms the one they want. Paths are relative to the /api/v1 prefix; all routes require Authorization: Bearer {token}.

The order payload and its role filtering are documented under Orders → the order object. See also the order lifecycle.

See Common errors.

Endpoints

Method URI Role Description
GET /orders/{orderId}/offer DRIVER The order as the driver sees it, to bid on
POST /orders/{orderId}/accept DRIVER Take the order at its published price
POST /orders/{orderId}/driver-accept DRIVER Alias of /accept
POST /orders/{orderId}/counter-offer DRIVER Bid a custom price
POST /orders/{orderId}/offers DRIVER Alias of /counter-offer
POST /orders/offers/{offerId}/accept CLIENT Accept a specific driver offer
POST /counter-offers/{offerId}/accept CLIENT Alias of /orders/offers/{offerId}/accept
POST /orders/{orderId}/accept-counter CLIENT Accept the standing counter-offer by order id
POST /counter-offers/{offerId}/reject CLIENT Reject a driver offer

The offer object

Driver bids are returned under an offer key. Money is exposed as offer_price_usd (decimal) alongside offer_price_cents.

{
  "offer": {
    "id": 17,
    "order_id": 42,
    "driver_id": 3,
    "offer_price_cents": 2000,
    "estimated_minutes": 20,
    "available_liters": 5000,
    "status": "PENDING",
    "over_capacity": false,
    "expires_at": "2026-06-26T12:05:00.000000Z",
    "driver_latitude": 10.4895,
    "driver_longitude": -66.8805,
    "created_at": "2026-06-26T12:00:00.000000Z",
    "updated_at": "2026-06-26T12:00:00.000000Z",
    "offer_price_usd": 20.0
  }
}

driver_latitude / driver_longitude snapshot the driver's position at bid time (null if no recent GPS).

When the bid exceeds the driver's remaining liters, the offer carries over_capacity: true and the response adds a warning string.


GET /orders/{orderId}/offer

The order as the bidding driver sees it (DRIVER-filtered, with the client's public contact). Only available while the order is biddable (BROADCASTING/COUNTER_OFFERED). The payload is returned at the top level, not wrapped under order.

Response 200

{
  "id": 42,
  "status": "BROADCASTING",
  "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,
  "driver_gross_usd": 16.4,
  "driver_solidary_usd": 0.16,
  "driver_receives_usd": 16.24,
  "notes": null,
  "client": { "id": 1, "name": "Ana MartĂ­nez", "phone": "+584241000001" },
  "created_at": "2026-06-26T12:00:00.000000Z"
}

Client money fields (client_solidary_*, client_pays_*), platform fields, and delivery_pin are stripped for the driver.

Errors

Status Body When
404 { "error": "OrderNotFound", "message": "La orden ya no está disponible." } Order is no longer biddable

A request for a non-existent order id resolves as a generic 404 (see Common errors).


POST /orders/{orderId}/accept

Take the order at its published price — creates a candidacy at the order's base price. The client confirms later which candidate wins. POST /orders/{orderId}/driver-accept is an alias with identical behavior.

Request body

Field Type Required Rules
estimatedMinutes integer no 1–240
force boolean no post even if the order exceeds remaining liters (assumes refill)

driverGross is ignored here; to bid a custom price use /counter-offer.

Response 201 — { "offer": { … } }, plus warning when over_capacity is set. See the offer object.

Errors

Status Body When
409 { "error": "InsufficientLiters", "message": "…", "remaining_liters": 3000, "requested_liters": 5000 } Not enough available liters and force not set
409 { "error": "Conflict", "message": "…" } Order not open for bids, already taken, or driver ineligible

/orders/{orderId}/accept is an alias of /orders/{orderId}/driver-accept (identical behavior).


POST /orders/{orderId}/counter-offer

Bid a custom price for the order. Omitting driverGross posts at the order's base price. POST /orders/{orderId}/offers is an alias with identical behavior.

Request body

Field Type Required Rules
driverGross number no > 0, ≤ 100000; omit to bid the base price
estimatedMinutes integer no 1–240
force boolean no post even if the order exceeds remaining liters

Response 201 — { "offer": { … } }, plus warning when over_capacity is set.

Errors

Status Body When
409 { "error": "InsufficientLiters", "message": "…", "remaining_liters": 3000, "requested_liters": 5000 } Not enough available liters and force not set
409 { "error": "Conflict", "message": "…" } Order not open for bids, already taken, or driver ineligible

/orders/{orderId}/counter-offer is an alias of /orders/{orderId}/offers (identical behavior).


POST /orders/offers/{offerId}/accept

The client accepts a specific driver offer; the order is confirmed to that driver. POST /counter-offers/{offerId}/accept is an alias with identical behavior.

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

Errors

Status Body When
404 { "error": "NotFound", "message": "La contraoferta no existe." } Unknown offer
409 { "error": "InsufficientLiters", "message": "…", "remaining_liters": 3000, "requested_liters": 5000 } Driver no longer has enough liters for this order
403 { "error": "Forbidden", "message": "…" } Caller may not accept this offer
409 { "error": "Conflict", "message": "…" } Offer expired or already taken

/orders/offers/{offerId}/accept is an alias of /counter-offers/{offerId}/accept (identical behavior).


POST /orders/{orderId}/accept-counter

The client accepts the standing counter-offer by order id (rather than offer id). Confirms the order to the offering driver.

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

Errors

Status Body When
409 { "error": "Conflict", "message": "…" } No acceptable counter-offer, or the order is not in a confirmable state

POST /counter-offers/{offerId}/reject

The client rejects a driver offer.

Response 200 — { "ok": true }.

Errors

Status Body When
404 { "error": "NotFound", "message": "La contraoferta no existe." } Unknown offer
403 { "error": "Forbidden", "message": "…" } Caller may not reject this offer
409 { "error": "Conflict", "message": "…" } Offer not in a rejectable state

Examples

# Driver: take the order at its published price (with an ETA)
curl -X POST /api/v1/orders/42/accept \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"estimatedMinutes": 20}'

# Driver: bid a custom price
curl -X POST /api/v1/orders/42/counter-offer \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"driverGross": 18.5, "estimatedMinutes": 25}'

# Client: accept a specific offer → order is confirmed to that driver
curl -X POST /api/v1/orders/offers/17/accept -H "Authorization: Bearer {client_token}"