Surplus

End-of-route "surplus" listings: a driver publishes leftover water at a fixed price, and a customer reserves the whole lot β€” which auto-creates an order. Paths are relative to the /api/v1 prefix; all routes require Authorization: Bearer {token}.

See Common errors. A reservation creates an order, so Orders and the order lifecycle apply from there on.

Endpoints

Method URI Role Description
POST /surplus DRIVER Publish a surplus listing
GET /surplus/me DRIVER My active listings
DELETE /surplus/{surplusId} DRIVER Cancel a listing
POST /surplus/{surplusId}/cancel DRIVER Cancel a listing (alias of DELETE)
GET /surplus/nearby any Nearby active listings
GET /surplus any Nearby active listings (alias of /surplus/nearby)
POST /surplus/{surplusId}/reserve CLIENT Reserve a listing (creates an order)

A driver may not publish a surplus listing while they have an active order. Money fields end in _usd (decimal), derived from integer _cents.


POST /surplus

Publishes a surplus listing. The driver enters totalAmount β€” the total gross they want to receive for the whole listing. The listing expires after durationMinutes (default 6 h if omitted).

Request body

Field Type Required Rules
availableLiters integer yes 500–50000
totalAmount number yes > 0, ≀ 100000 β€” total gross the driver wants to receive
description string no ≀ 280 chars
latitude number yes between βˆ’90 and 90
longitude number yes between βˆ’180 and 180
address string yes 3–280 chars
durationMinutes integer no 15–720 (max 12 h); defaults to 360 (6 h)

Response 201

{
  "listing": {
    "id": 5,
    "driver_id": 3,
    "city_id": 1,
    "available_liters": 5000,
    "price_per_liter_cents": 2,
    "total_price_cents": 10000,
    "price_per_liter_usd": 0.02,
    "total_price_usd": 100.0,
    "description": "Excedente fin de ruta",
    "latitude": 10.487,
    "longitude": -66.88,
    "address": "Av. Principal, Las Mercedes",
    "status": "ACTIVA",
    "sold_liters": 0,
    "remaining_liters": 5000,
    "expires_at": "2026-06-26T14:00:00+00:00",
    "created_at": "2026-06-26T13:00:00+00:00"
  }
}

Each listing carries a status: ACTIVA, RESERVADA (reserved β†’ order created), EXPIRADA (past expires_at), or CANCELADA.

Errors

Status Body When
409 { "error": "Conflict", "message": "No podΓ©s publicar remates con una orden activa." } The driver already has an active order

GET /surplus/me

The caller's currently active listings (not expired, not cancelled, with liters left).

Response 200 β€” { "listings": [ { … } ] } (each entry is a listing, same shape as POST /surplus).


DELETE /surplus/{surplusId}

Cancels a listing (sets its status to CANCELADA). Only the listing's owner may cancel. POST /surplus/{surplusId}/cancel is an exact alias.

Response 200 β€” { "ok": true }.

Errors

Status Body When
409 { "error": "Conflict", "message": "No es tu listing." } The caller does not own the listing

POST /surplus/{surplusId}/cancel

Alias of DELETE /surplus/{surplusId} β€” identical behavior, response, and errors.


GET /surplus/nearby

Active listings within a radius of a point, enriched with client-facing pricing (base_price_* and client_pays_*). GET /surplus is an exact alias.

Pricing is previewed for paymentMethod (defaults to CASH_USD).

Query

Field Type Required Rules
lat number yes between βˆ’90 and 90
lng number yes between βˆ’180 and 180
radiusKm number no > 0, ≀ 100; defaults to 15
paymentMethod string no an active integration code; defaults to CASH_USD (PAYMENT_FORM is not supported here)

Response 200

{
  "listings": [
    {
      "id": 5,
      "driver_id": 3,
      "available_liters": 5000,
      "remaining_liters": 5000,
      "total_price_cents": 10000,
      "total_price_usd": 100.0,
      "price_per_liter_usd": 0.02,
      "base_price_cents": 12195,
      "base_price_usd": 121.95,
      "client_pays_cents": 12317,
      "client_pays_usd": 123.17,
      "address": "Av. Principal, Las Mercedes",
      "latitude": 10.487,
      "longitude": -66.88,
      "expires_at": "2026-06-26T14:00:00+00:00"
    }
  ]
}

GET /surplus

Alias of GET /surplus/nearby β€” identical query and response.


POST /surplus/{surplusId}/reserve

A customer reserves the whole listing. This creates an order (CONFIRMED for cash, WAITING_FOR_PAYMENT for online methods) and returns it in the CLIENT-sanitized shape (see Orders β†’ the order object).

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
paymentMethod string no an active integration code; defaults to CASH_USD (PAYMENT_FORM is not supported here)

Response 200 β€” { "order": { … } } (CLIENT view). The order includes surplus_listing_id (the listing it came from); a non-null value marks the order as a surplus reservation. Normal orders have surplus_listing_id: null.

Errors

Status Body When
409 { "error": "Conflict", "message": "El remate ya no estΓ‘ disponible." } The listing was sold, cancelled, or expired

Examples

# Driver: publish surplus
curl -X POST /api/v1/surplus \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"availableLiters": 5000, "totalAmount": 100, "latitude": 10.487, "longitude": -66.88, "address": "Av. Principal", "durationMinutes": 60}'

# Client: reserve a listing (cash)
curl -X POST /api/v1/surplus/5/reserve \
  -H "Authorization: Bearer {client_token}" \
  -H "Content-Type: application/json" \
  -d '{"latitude": 10.49, "longitude": -66.85, "address": "Calle 2, Casa 4"}'