Chat

In-order messaging built on chat rooms. Every room belongs to an order and has a type:

All share the same room/message endpoints. Paths are relative to the /api/v1 prefix; all routes require Authorization: Bearer {token}.

Flow. First resolve the room for the order (POST /orders/{orderId}/chat-rooms) to get its id and real-time channel, then read and post messages by roomId. Reads never create or mutate anything.

Availability. The client↔driver room opens once the order is paid (CONFIRMED onward, through DELIVERED). Both support rooms (ORDER_ADMIN_SUPPORT and ORDER_ADMIN_CLIENT_SUPPORT) are available while the order is active (WAITING_FOR_PAYMENT/CONFIRMED through ARRIVED). See the order lifecycle.

Real-time. Every response that resolves or lists a room returns its channel field — subscribe to whatever the server returns rather than building the name yourself.

See Common errors.

Endpoints

Method URI Description
POST /orders/{orderId}/chat-rooms Resolve (create if needed) the order's room of a given type
GET /chat-rooms/{roomId}/messages List the room's last 50 messages
POST /chat-rooms/{roomId}/messages Send a message
POST /chat-rooms/{roomId}/read Mark the room read for the caller

All chat failures share one envelope: { "error": "ChatError", "message": "…" }.


POST /orders/{orderId}/chat-rooms

Resolves the order's chat room of the requested type. Idempotent. Returns the room id, its channel, and the last 50 messages so the caller can subscribe and render immediately.

Who may open which type:

type Allowed caller
ORDER_CLIENT_DRIVER The order's client or assigned driver
ORDER_ADMIN_SUPPORT The order's assigned driver only
ORDER_ADMIN_CLIENT_SUPPORT The order's client only

Request body

Field Type Required Rules
type string yes ORDER_CLIENT_DRIVER, ORDER_ADMIN_SUPPORT, or ORDER_ADMIN_CLIENT_SUPPORT

Response 200 (client↔driver room)

{
  "id": 7,
  "type": "ORDER_CLIENT_DRIVER",
  "channel": "chat.rooms.7",
  "messages": [
    {
      "id": 31,
      "order_id": 42,
      "sender_id": 1,
      "sender_role": "CLIENT",
      "sender_name": "Juan",
      "content": "Estoy en el portĂłn.",
      "type": "TEXT",
      "created_at": "2026-06-26T12:31:00+00:00"
    }
  ]
}

A support room (ORDER_ADMIN_SUPPORT or ORDER_ADMIN_CLIENT_SUPPORT) additionally returns the assignment status:

{
  "id": 12,
  "type": "ORDER_ADMIN_SUPPORT",
  "channel": "chat.rooms.12",
  "status": { "state": "pool", "assigned_admin": null },
  "messages": []
}

status.state is pool (waiting for an admin) or claimed (an admin is handling it — then assigned_admin is { "id", "name" }).

Errors

Status Body When
404 { "error": "ChatError", "message": "Pedido no encontrado." } Unknown order
403 { "error": "ChatError", "message": "No perteneces a este chat." } Caller may not open this room type for the order
400 { "error": "ChatError", "message": "Chat no disponible para esta orden." } Client↔driver room requested before the order is paid
400 { "error": "ChatError", "message": "Soporte no disponible para esta orden." } Support room requested while the order is not active (and none exists yet)
422 ValidationError Missing/invalid type

GET /chat-rooms/{roomId}/messages

Returns the room's channel plus the last 50 messages, oldest first. Read-only; requires the caller to be an active member of the room. sender_role is the emitter's role in the room (CLIENT, DRIVER, or ADMIN).

Response 200

{
  "channel": "chat.rooms.7",
  "messages": [
    {
      "id": 31,
      "order_id": 42,
      "sender_id": 1,
      "sender_role": "CLIENT",
      "sender_name": "Juan",
      "content": "Estoy en el portĂłn.",
      "type": "TEXT",
      "created_at": "2026-06-26T12:31:00+00:00"
    }
  ]
}

Errors

Status Body When
404 { "error": "ChatError", "message": "Chat no encontrado." } Unknown room
403 { "error": "ChatError", "message": "No perteneces a este chat." } Caller is not an active member

POST /chat-rooms/{roomId}/messages

Sends a message to the room. Requires active membership and that the order is still in the room's messaging window (paid for a client↔driver room, active for a support room).

Request body

Field Type Required Rules
content string yes 1–500 chars

Response 201

{
  "message": {
    "id": 32,
    "order_id": 42,
    "sender_id": 3,
    "sender_role": "DRIVER",
    "sender_name": "Pedro",
    "content": "Voy llegando.",
    "type": "TEXT",
    "created_at": "2026-06-26T12:32:00+00:00"
  },
  "channel": "chat.rooms.7"
}

Errors

Status Body When
404 { "error": "ChatError", "message": "Chat no encontrado." } Unknown room
403 { "error": "ChatError", "message": "No perteneces a este chat." } Caller is not an active member
400 { "error": "ChatError", "message": "Chat no disponible para esta orden." } Client↔driver order not paid
400 { "error": "ChatError", "message": "Soporte no disponible para esta orden." } Support order no longer active

POST /chat-rooms/{roomId}/read

Marks the room read up to now for the calling member.

Response 200

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

Errors — same 404 / 403 as the message endpoints.


Examples

# Resolve the client↔driver room for order 42
curl -X POST /api/v1/orders/42/chat-rooms \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"type": "ORDER_CLIENT_DRIVER"}'

# Read the conversation (roomId from the resolve response)
curl /api/v1/chat-rooms/7/messages -H "Authorization: Bearer {token}"

# Send a message
curl -X POST /api/v1/chat-rooms/7/messages \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"content": "Voy llegando."}'

# Open the support room for order 42 (driver token)
curl -X POST /api/v1/orders/42/chat-rooms \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"type": "ORDER_ADMIN_SUPPORT"}'