Browse customers and drill into a single customer's order history. Paths are relative
to the /api/v1 prefix; these are back-office endpoints requiring an admin token.
Requires an admin bearer token (see Admin auth). Admin only.
See Common errors.
An admin only sees customers who have ordered in their assigned cities.
| Method | URI | Access | Description |
|---|---|---|---|
GET |
/admin/customers |
Admin | Paginated customer list with spend totals |
GET |
/admin/customers/{customerId} |
Admin | Customer profile, totals & recent orders |
POST |
/admin/customers/{customerId}/credit/adjust |
Admin | Manually credit or debit promotional credit |
GET |
/admin/customers/{customerId}/credit/transactions |
Admin | Paginated promotional credit history |
GET |
/admin/customers/{customerId}/bank-account |
Admin | The customer's saved Pago MĂłvil account |
PUT |
/admin/customers/{customerId}/bank-account |
Admin | Create or replace the saved account |
DELETE |
/admin/customers/{customerId}/bank-account |
Admin | Remove the saved account |
Page-paginated customer list (newest first). See Pagination → page-based.
Query
| Field | Type | Required | Rules |
|---|---|---|---|
is_active |
boolean | no | filter by the user's active flag |
min_spent |
number | no | ≥ 0; approximate minimum spend in USD |
max_spent |
number | no | ≥ 0; approximate maximum spend in USD |
q |
string | no | ≤ 80 chars; matches name or phone |
limit |
integer | no | 1–100, default 25 |
page |
integer | no | ≥ 1, default 1 |
min_spent/max_spentare approximate, not an exact order total.
Response 200
{
"items": [
{
"id": 1,
"name": "Ana MartĂnez",
"phone": "+584241000001",
"email": "ana@example.com",
"is_active": true,
"total_orders": 12,
"total_liters_ordered": 36000,
"total_solidary_usd": 4.2,
"created_at": "2026-06-01T09:00:00+00:00"
}
],
"meta": { "total": 137, "per_page": 25, "current_page": 1, "last_page": 6 }
}
Customer profile, lifetime totals, and a page of their orders (newest first).
Query
| Field | Type | Required | Rules |
|---|---|---|---|
limit |
integer | no | 1–100, default 10 |
page |
integer | no | ≥ 1, default 1 |
Response 200
{
"customer": {
"id": 1,
"name": "Ana MartĂnez",
"phone": "+584241000001",
"email": "ana@example.com",
"is_active": true,
"created_at": "2026-06-01T09:00:00+00:00",
"updated_at": "2026-06-28T18:00:00+00:00"
},
"totals": {
"total_orders": 12,
"total_liters_ordered": 36000,
"total_spent_usd": 4.2
},
"credit": {
"anyUSD": 10,
"ordersUSD": 0,
"surplusUSD": 5,
"usableOnOrdersUSD": 10,
"usableOnSurplusUSD": 15,
"earnedUSD": 25,
"redeemedUSD": 10
},
"orders": [
{
"id": 42,
"status": "DELIVERED",
"location_from": { "lat": 10.487, "lng": -66.88, "text": "Av. Principal" },
"location_to": { "lat": null, "lng": null, "text": null },
"liters": 5000,
"price_usd": 20.2,
"driver": { "id": 3, "name": "Carlos RodrĂguez", "phone": "+584241001001" },
"created_at": "2026-06-26T12:00:00+00:00",
"completed_at": "2026-06-26T12:40:00+00:00"
}
],
"meta": { "total": 12, "per_page": 10, "current_page": 1, "last_page": 2 }
}
driver is null for orders that were never assigned. location_to is currently
always null-valued. credit has the same shape as
GET /credit/me.
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "CustomerNotFound" } |
No customer (client profile) with that id |
Manually moves one promotional credit bucket.
Request body
| Field | Type | Required | Rules |
|---|---|---|---|
type |
string | yes | credit or debit |
scope |
string | no | ANY, ORDERS or SURPLUS (defaults to ANY) |
amount |
number | yes | min:0.01, in USD |
reason |
string | yes | 10–500 chars |
Response 200
{
"transaction": {
"id": 31,
"type": "MANUAL_ADJUSTMENT",
"scope": "ANY",
"direction": "CREDIT",
"amount_usd": 5,
"balance_after_usd": 15,
"order_id": null,
"promotion_id": null,
"description": "credit manual: CortesĂa por demora",
"created_at": "2026-08-27T12:00:00+00:00"
}
}
| Field | Type | Description |
|---|---|---|
transaction.scope |
string | The bucket that moved |
transaction.direction |
string | CREDIT or DEBIT |
transaction.amount_usd |
number | Always positive; the sign lives in direction |
transaction.balance_after_usd |
number | Balance of that bucket after the movement; a debit may leave it negative |
Errors
| Status | When |
|---|---|
401 |
Missing or invalid admin bearer token |
404 |
CustomerNotFound — no customer with that id |
422 |
Validation failed |
Cursor-paginated promotional credit history, newest first.
Query parameters
| Field | Type | Required | Rules |
|---|---|---|---|
type |
string | No | PROMO_GRANT, ORDER_REDEMPTION, ORDER_FORFEIT, ORDER_REFUND or MANUAL_ADJUSTMENT |
scope |
string | No | ANY, ORDERS or SURPLUS |
limit |
integer | No | min:1, max:100 (default 25) |
cursor |
string | No | Cursor from a previous response |
Response 200 — { "items": [ {transaction}, … ], "next_cursor": …, "has_more": … },
each item shaped like the transaction above.
Errors
| Status | When |
|---|---|
401 |
Missing or invalid admin bearer token |
404 |
CustomerNotFound — no customer with that id |
422 |
Validation failed |
The customer's one saved Pago MĂłvil account, used to pre-fill the refund form.
Response 200
{
"account": {
"id": 7,
"id_number": "V12345678",
"phone": "04125551234",
"bank_code": "0102",
"updated_at": "2026-09-15T14:00:00+00:00"
}
}
account is null when the customer has no saved account.
Errors
| Status | When |
|---|---|
401 |
Missing or invalid admin bearer token |
404 |
CustomerNotFound — no customer with that id |
Creates the saved account, or replaces it if one exists (one account per customer).
Request body
| Field | Type | Required | Rules |
|---|---|---|---|
idNumber |
string | yes | Cédula, regex ^[VEJGP]?\d{6,10}$; normalized to uppercase without separators |
phone |
string | yes | Venezuelan mobile, regex `^(0 |
bankCode |
string | yes | Exactly 4 digits |
Response 200 — { "account": {…} }, shaped like the GET above.
Errors
| Status | When |
|---|---|
401 |
Missing or invalid admin bearer token |
404 |
CustomerNotFound — no customer with that id |
422 |
Validation failed |
Removes the saved account, if any.
Response 204 — no content.
Errors
| Status | When |
|---|---|
401 |
Missing or invalid admin bearer token |