Skip to main content

Módulo Message

src/app/modules/message/

Persistencia de mensajes de las conversaciones.

Modelo — Message

CampoTipoDescripción
_idObjectIdID de MongoDB
tenantIdObjectId → Tenant
ticketIdObjectId → TicketConversación a la que pertenece
sendTypestringcontact | agent | user | system
messageTypestringchat | media | system
sendUserRefModelstringUser | Contact — modelo de referencia polimórfico
sendUserIdObjectId (refPath)Emisor: Contact._id si sendType=contact, User._id si agent/user
bodystringContenido del mensaje
parentIdObjectId → MessageMensaje al que responde (threading)
MediaIdObjectId → FileArchivo adjunto (si messageType=media)
ackstringsent | delivered | read
isReadbooleanSi el agente lo marcó como leído
readAtDateFecha de lectura
createdAtDate
updatedAtDate

Endpoints

CRUD general

MétodoRutaAuthDescripción
POST/api/v1/messages/create@Auth()Crea mensaje
GET/api/v1/messages/get-all@Auth()Lista mensajes (filtrable por ticketId)
GET/api/v1/messages/get-one/:id@Auth()Obtiene un mensaje
PATCH/api/v1/messages/update/:id@Auth()Actualiza (ej: cambiar ack)
DELETE/api/v1/messages/remove/:id@Auth()Elimina mensaje
PATCH/api/v1/messages/mark-read/:id@Auth()Marca un mensaje como leído
PATCH/api/v1/messages/mark-read-ticket/:ticketId@Auth()Marca todos los mensajes de un ticket como leídos

Por contacto

Endpoints de consulta de historial centrados en el contacto. Todos filtran automáticamente por tenantId del usuario autenticado.

Orden de registro en el router: /:contactId/tickets-all se registra antes de /:contactId/:ticketId para evitar que Express capture el literal tickets-all como un parámetro :ticketId.

MétodoRutaAuthDescripción
GET/api/v1/messages/:contactId/tickets-all@Auth()Todos los tickets del contacto con su último mensaje
GET/api/v1/messages/:contactId@Auth()Todos los mensajes del contacto (todos sus tickets)
GET/api/v1/messages/:contactId/:ticketId@Auth()Mensajes de un ticket específico del contacto

Endpoints por contacto — detalle

GET /api/v1/messages/:contactId/tickets-all

Devuelve todos los tickets del contacto con el último mensaje de cada uno populado, junto con el remitente, el agente asignado y el conteo de mensajes no leídos. Ordenados por fecha del último mensaje (más reciente primero).

Delega internamente al mismo pipeline de TicketService.getTicketsList con contactId como filtro, por lo que la respuesta es idéntica a la de GET /api/v1/tickets/get-all.

Query params

ParámetroTipoPor defectoDescripción
pagenumber1Número de página (base 1)
limitnumber10Ítems por página (máx. 100)
createdFromstring (ISO 8601)Tickets creados desde esta fecha
createdTostring (ISO 8601)Tickets creados hasta esta fecha
statusstringEstado del ticket: open | closed | inProgress | pending | resolved | cancelled

Respuesta exitosa — 200 OK

{
"message": "message.findAll",
"data": {
"results": [
{
"_id": "507f1f77bcf86cd799439011",
"identifier": "uuid-1234",
"status": "open",
"lastMessage": {
"_id": "507f1f77bcf86cd799439020",
"body": "Hola, necesito ayuda",
"messageType": "chat",
"sendType": "contact",
"ack": "read",
"isRead": true,
"sentAt": "2026-05-20T10:00:00Z",
"sender": {
"_id": "507f1f77bcf86cd799439013",
"username": "juan_perez"
}
},
"contact": {
"_id": "507f1f77bcf86cd799439012",
"username": "juan_perez",
"phone": "+5491112345678",
"isActive": true,
"isBlocked": false,
"labels": []
},
"unreadCount": 3,
"assignedUser": null,
"createdAt": "2026-05-19T09:00:00Z",
"updatedAt": "2026-05-20T10:00:00Z"
}
],
"meta": {
"total": 5,
"page": 1,
"limit": 10,
"totalPages": 1
}
}
}

GET /api/v1/messages/:contactId

Devuelve todos los mensajes del contacto a través de todos sus tickets, ordenados del más reciente al más antiguo. Cada mensaje incluye el sender (Contact o User según sendType) y el parent (mensaje al que responde, si existe).

Query params

ParámetroTipoPor defectoDescripción
pagenumber1Número de página (base 1)
limitnumber10Mensajes por página (máx. 100)
createdFromstring (ISO 8601)Mensajes enviados desde esta fecha
createdTostring (ISO 8601)Mensajes enviados hasta esta fecha
messageTypestringchat | media | system
sendTypestringcontact | agent | user | system

Respuesta exitosa — 200 OK

{
"message": "message.findAll",
"data": {
"results": [
{
"_id": "507f1f77bcf86cd799439021",
"ticketId": "507f1f77bcf86cd799439011",
"body": "¡Claro! Te ayudo ahora.",
"messageType": "chat",
"sendType": "agent",
"ack": "delivered",
"isRead": false,
"readAt": null,
"MediaId": null,
"parentId": "507f1f77bcf86cd799439020",
"createdAt": "2026-05-20T10:05:00Z",
"updatedAt": "2026-05-20T10:05:00Z",
"parent": {
"_id": "507f1f77bcf86cd799439020",
"body": "Hola, necesito ayuda",
"messageType": "chat",
"sendType": "contact",
"createdAt": "2026-05-20T10:00:00Z"
},
"sender": {
"_id": "507f1f77bcf86cd799439030",
"username": "agent_laura",
"firstname": "Laura",
"lastName": "García",
"email": "laura@empresa.com"
}
}
],
"meta": {
"total": 42,
"page": 1,
"limit": 10,
"totalPages": 5
}
}
}

Notas

  • parent es null cuando el mensaje no es una respuesta directa.
  • sender expone campos de Contact (username, firstname, lastName, profilePic) o de User (username, firstname, lastName, email) según el sendType.
  • El pipeline hace un $lookup al collection tickets para filtrar por ContactId, por lo que solo se devuelven mensajes de tickets que pertenecen al contacto indicado dentro del tenant.

GET /api/v1/messages/:contactId/:ticketId

Devuelve los mensajes de un ticket específico perteneciente al contacto, ordenados del más reciente al más antiguo. Verifica que el ticket pertenezca al contacto antes de consultar; si no, devuelve 404.

Path params

ParámetroDescripción
contactIdMongoDB ObjectId del contacto
ticketIdMongoDB ObjectId del ticket

Query params

ParámetroTipoPor defectoDescripción
pagenumber1Número de página (base 1)
limitnumber10Mensajes por página (máx. 100)
createdFromstring (ISO 8601)Mensajes enviados desde esta fecha
createdTostring (ISO 8601)Mensajes enviados hasta esta fecha
messageTypestringchat | media | system
sendTypestringcontact | agent | user | system

Respuesta exitosa — 200 OK

La forma de cada mensaje es idéntica a la de GET /:contactId, incluyendo parent y sender.

Error — 404 Not Found

Devuelto cuando el ticketId no existe o no pertenece al contactId dentro del tenant del usuario autenticado.

{
"statusCode": 404,
"message": "ticket.notFound"
}

DTOs de filtro

MessageContactFilterDto

Usado por GET /:contactId y GET /:contactId/:ticketId.

class MessageContactFilterDto {
page?: number // default 1, min 1
limit?: number // default 10, max 100
createdFrom?: string // ISO 8601
createdTo?: string // ISO 8601
messageType?: 'chat' | 'media' | 'system'
sendType?: 'contact' | 'agent' | 'user' | 'system'
}

TicketContactFilterDto

Usado por GET /:contactId/tickets-all.

class TicketContactFilterDto {
page?: number // default 1, min 1
limit?: number // default 10, max 100
createdFrom?: string // ISO 8601 — fecha de creación del ticket
createdTo?: string // ISO 8601 — fecha de creación del ticket
status?: 'open' | 'closed' | 'inProgress' | 'pending' | 'resolved' | 'cancelled'
}

Tipos de mensaje

messageTypesendTypeDescripción
chatcontactMensaje de texto del cliente externo
chatagentRespuesta del agente
mediacualquieraMensaje con archivo adjunto (MediaId)
systemsystemEvento automático (ticket creado, cerrado, etc.)

Flujo con ChatGateway

El ChatGateway crea los mensajes en BD llamando MessageService tras recibir un evento WebSocket SEND_MESSAGE o SEND_FILE. Los endpoints HTTP son para consulta e historial.

Dependencias del módulo

MessageModule
├── MongooseModule (Message schema)
├── UserModule
└── TicketModule ← importado para los endpoints por contacto
├── TicketService ← getTicketsList, findByContactAndTicket
└── TicketRepository