Admin order support

Back-office side of the per-order support chat. Two support kinds share this panel: the driver side and the client side, both defined in Chat. Paths are relative to the /api/v1 prefix; all routes require an admin bearer token. Support is global — there is no city scoping, so any active admin sees the whole queue.

Kinds. The inbox is filterable by kind (driver or client); omit it to get both. The room-addressed endpoints (show/claim/messages/release/read) work the same for either kind — a room already carries its kind, so no kind param is needed there.

Claim model. A support room starts unassigned (the pool). An admin claims it explicitly (POST …/claim) or by sending the first message; from then on only that admin can read, write, or release it. Releasing returns the room to the pool.

Addressing. Admins reach a room by its room_id, taken from the inbox.

See Common errors.

Endpoints

Method URI Description
GET /admin/order-support Support inbox (pool or mine)
GET /admin/chat-rooms/{roomId} Read a support room
POST /admin/chat-rooms/{roomId}/claim Claim a pooled room
POST /admin/chat-rooms/{roomId}/messages Send a message (claims a pooled room)
POST /admin/chat-rooms/{roomId}/release Release a claimed room to the pool
POST /admin/chat-rooms/{roomId}/read Mark the room read

Failures share the envelope { "error": "SupportError", "message": "…" }.


GET /admin/order-support

Paginated support inbox, newest activity first. Each item carries the room_id used to address the room in the other endpoints.

Query parameters

Param Type Default Rules
bucket string pool pool (unassigned) or mine (claimed by you)
kind string — (both) driver or client; omit to include both kinds
limit integer 25 1–100
cursor string Opaque cursor from next_cursor

Response 200

{
  "items": [
    {
      "order_id": 42,
      "room_id": 12,
      "kind": "driver",
      "channel": "chat.rooms.12",
      "state": "pool",
      "assigned_admin": null,
      "last_message": {
        "sender_role": "DRIVER",
        "sender_name": "Pedro",
        "content": "Se me dañó la bomba, no puedo cargar.",
        "created_at": "2026-06-26T12:40:00+00:00"
      }
    }
  ],
  "next_cursor": "eyJjaGF0X3Jvb21zLmxhc3RfbWVzc2FnZV9hdCI6...",
  "has_more": false
}

Each item's kind is driver or client. In the mine bucket, state is claimed and assigned_admin is { "id", "name" }.


GET /admin/chat-rooms/{roomId}

Reads a support room: channel, the last 50 messages (oldest first) and status. A pooled room can be read by any admin (for triage); a claimed room only by its assigned admin. Read-only.

Response 200

{
  "channel": "chat.rooms.12",
  "messages": [
    {
      "id": 44,
      "order_id": 42,
      "sender_id": 3,
      "sender_role": "DRIVER",
      "sender_name": "Pedro",
      "content": "Se me dañó la bomba, no puedo cargar.",
      "type": "TEXT",
      "created_at": "2026-06-26T12:40:00+00:00"
    }
  ],
  "status": { "state": "pool", "assigned_admin": null }
}

Errors

Status Body When
404 { "error": "SupportError", "message": "Chat de soporte no encontrado." } Unknown room (or not a support room)
403 { "error": "SupportError", "message": "Este chat está asignado a otro administrador." } Room claimed by another admin

POST /admin/chat-rooms/{roomId}/claim

Claims a pooled room for you. Idempotent if you already own it. Only while the order is active.

Response 200

{ "status": { "state": "claimed", "assigned_admin": { "id": 7, "name": "Ana" } } }

Errors

Status Body When
404 { "error": "SupportError", "message": "Chat de soporte no encontrado." } Unknown room
400 { "error": "SupportError", "message": "Soporte no disponible para esta orden." } Order is not active
403 { "error": "SupportError", "message": "Este chat está asignado a otro administrador." } Claimed by another admin
409 { "error": "SupportError", "message": "Otro administrador tomó este chat." } Lost the claim race

POST /admin/chat-rooms/{roomId}/messages

Sends a message. If the room is in the pool, sending claims it for you; if you already own it, it appends. Only available while the order is active.

Request body

Field Type Required Rules
content string yes 1–500 chars

Response 201

{
  "message": {
    "id": 46,
    "order_id": 42,
    "sender_id": 7,
    "sender_role": "ADMIN",
    "sender_name": "Ana",
    "content": "Te contacto con la estación más cercana.",
    "type": "TEXT",
    "created_at": "2026-06-26T12:42:00+00:00"
  },
  "channel": "chat.rooms.12",
  "status": { "state": "claimed", "assigned_admin": { "id": 7, "name": "Ana" } }
}

Errors

Status Body When
404 { "error": "SupportError", "message": "Chat de soporte no encontrado." } Unknown room
400 { "error": "SupportError", "message": "Soporte no disponible para esta orden." } Order is not active
403 { "error": "SupportError", "message": "Este chat está asignado a otro administrador." } Claimed by another admin
409 { "error": "SupportError", "message": "Otro administrador tomó este chat." } Lost the claim race

POST /admin/chat-rooms/{roomId}/release

Releases a room you own back to the pool. Only the assigned admin may release.

Response 200

{ "status": { "state": "pool", "assigned_admin": null } }

Errors

Status Body When
404 { "error": "SupportError", "message": "Chat de soporte no encontrado." } Unknown room
403 { "error": "SupportError", "message": "No sos el administrador asignado de este chat." } You are not the assigned admin
409 { "error": "SupportError", "message": "Este chat no tiene soporte asignado." } Room is not claimed

POST /admin/chat-rooms/{roomId}/read

Marks the room read up to now. Only the assigned admin may call it.

Response 200

{ "read_at": "2026-06-26T12:43:00+00:00" }

Examples

# Pool inbox (both kinds)
curl "/api/v1/admin/order-support?bucket=pool" -H "Authorization: Bearer {admin_token}"

# Pool inbox, client support only
curl "/api/v1/admin/order-support?bucket=pool&kind=client" -H "Authorization: Bearer {admin_token}"

# Claim a room (room_id from the inbox)
curl -X POST /api/v1/admin/chat-rooms/12/claim -H "Authorization: Bearer {admin_token}"

# Reply
curl -X POST /api/v1/admin/chat-rooms/12/messages \
  -H "Authorization: Bearer {admin_token}" \
  -H "Content-Type: application/json" \
  -d '{"content": "Te contacto con la estación más cercana."}'

# Release back to the pool
curl -X POST /api/v1/admin/chat-rooms/12/release -H "Authorization: Bearer {admin_token}"