Pular para conteúdo

API Backend — Rune Courier

Visao Geral

O backend expoe REST (/bff/*) para CRUD e WebSocket (/ws/chat) para tempo real.

Base URL: http://localhost:5060


Autenticacao

Todas as requisicoes REST (exceto /health) exigem:

Authorization: Bearer {jwt_token}
X-Application-Id: {application_id}
X-Tenant-Id: {tenant_id}

O WebSocket recebe esses dados como query params na conexao.


Conversas

Listar conversas

GET /bff/conversations

Retorna conversas do usuario autenticado no tenant, ordenadas por ultima atividade.

Response 200:

[
  {
    "id": "uuid",
    "type": "DIRECT",
    "name": null,
    "participants": [
      {
        "userId": "uuid",
        "email": "[email protected]",
        "name": "Joao",
        "role": "MEMBER",
        "isActive": true
      }
    ],
    "lastMessage": {
      "content": "Ola!",
      "senderName": "Maria",
      "contentType": "TEXT",
      "createdAt": "2026-02-17T10:00:00Z"
    },
    "unreadCount": 3,
    "createdAt": "2026-02-17T09:00:00Z",
    "updatedAt": "2026-02-17T10:00:00Z"
  }
]

Criar conversa

POST /bff/conversations

Request:

{
  "type": "GROUP",
  "name": "Equipe Recepcao",
  "participantIds": ["uuid-1", "uuid-2"],
  "metadata": { "channel": "INTERNAL" }
}

O criador e automaticamente adicionado como ADMIN.

Encontrar ou criar conversa direta

POST /bff/conversations/direct

Encontra conversa 1:1 existente ou cria uma nova. Previne duplicatas.

Request:

{
  "targetUserId": "uuid",
  "targetEmail": "[email protected]",
  "targetName": "Maria"
}

Obter conversa

GET /bff/conversations/{id}

Atualizar conversa

PUT /bff/conversations/{id}

Request:

{
  "name": "Novo nome do grupo"
}

Adicionar participante

POST /bff/conversations/{id}/participants

Request:

{
  "userId": "uuid",
  "email": "[email protected]",
  "name": "Carlos"
}

Marcar como lido

POST /bff/conversations/{id}/read

Marca todas as mensagens ate messageId como lidas.

Request:

{
  "messageId": "uuid"
}

Mensagens

Listar mensagens

GET /bff/conversations/{conversationId}/messages

Paginacao por cursor (mais recentes primeiro).

Query params:

Param Tipo Default Descricao
before UUID - Mensagens antes deste ID
limit int 50 Quantidade (max 100)

Response 200:

[
  {
    "id": "uuid",
    "conversationId": "uuid",
    "senderId": "uuid",
    "senderEmail": "[email protected]",
    "senderName": "Joao",
    "content": "Bom dia!",
    "contentType": "TEXT",
    "replyToId": null,
    "isEdited": false,
    "isDeleted": false,
    "metadata": {},
    "createdAt": "2026-02-17T10:00:00Z",
    "updatedAt": null
  }
]

Enviar mensagem

POST /bff/conversations/{conversationId}/messages

A mensagem e criada no banco e broadcast via WebSocket para todos os participantes online.

Request:

{
  "content": "Bom dia!",
  "contentType": "TEXT",
  "replyToId": null,
  "metadata": {}
}

Editar mensagem

PUT /bff/conversations/{conversationId}/messages/{messageId}

Apenas o remetente pode editar. Marca isEdited = true.

Request:

{
  "content": "Bom dia! (editado)"
}

Deletar mensagem

DELETE /bff/conversations/{conversationId}/messages/{messageId}

Soft-delete — marca isDeleted = true e limpa o conteudo.


Status

Contagem de nao-lidos

GET /bff/unread-counts

Response 200:

{
  "totalUnread": 7,
  "conversations": {
    "conv-uuid-1": 3,
    "conv-uuid-2": 4
  }
}

WebSocket

Conexao

ws://host/ws/chat?token={jwt}&app={appId}&tenant={tenantId}

Autenticacao: O token JWT e validado via OATH introspection no @OnOpen. Se invalido, a conexao e recusada.

Eventos recebidos (Server → Client)

Evento Payload Descricao
MESSAGE_RECEIVED MessageResponse Nova mensagem
MESSAGE_UPDATED MessageResponse Mensagem editada
MESSAGE_DELETED { conversationId, messageId } Mensagem removida
TYPING_START { conversationId, userId, userName } Digitando
TYPING_STOP { conversationId, userId } Parou de digitar
READ_RECEIPT { conversationId, userId, messageId } Marcou como lido
PRESENCE_CHANGED { userId, online } Status mudou
CONVERSATION_CREATED ConversationResponse Nova conversa

Mensagens enviadas (Client → Server)

{ "type": "TYPING", "conversationId": "uuid" }
{ "type": "TYPING_STOP", "conversationId": "uuid" }
{ "type": "MARK_READ", "conversationId": "uuid", "messageId": "uuid" }

Ping/pong nativo do protocolo WebSocket para manter a conexao.


Health

GET /health

GET /health/ready

GET /health/live


SDK — ChatClientService

Para integracao server-to-server, use o ChatClientService do haus-quarkus-sdk:

@Inject ChatClientService chatClient;

Metodos disponiveis

Metodo Descricao
listConversations(tenantId) Listar conversas
findOrCreateDirect(tenantId, userId, email, name) Conversa 1:1
createGroup(tenantId, name, participants) Criar grupo
addParticipant(tenantId, convId, userId, email, name) Adicionar participante
sendMessage(tenantId, convId, content) Enviar mensagem TEXT
sendMessage(tenantId, convId, content, type) Enviar com tipo
sendSystemMessage(tenantId, convId, content) Mensagem SYSTEM
listMessages(tenantId, convId, before, limit) Listar mensagens
markAsRead(tenantId, convId, messageId) Marcar como lido
getUnreadCounts(tenantId) Contagem nao-lidos
isHealthy() Health check
isEnabled() Verificar se chat esta habilitado

Exemplo — Notificacao de reserva

// Quando uma reserva e confirmada, envia msg de sistema no chat
chatClient.sendSystemMessage(
    hotelId.toString(),
    conversationId,
    "Reserva #" + reservation.getCode() + " confirmada!"
);

Exemplo — Criar conversa hospede-recepcao

// Criar conversa direta entre hospede e recepcao
ChatClient.ConversationResponse conv = chatClient.findOrCreateDirect(
    hotelId.toString(),
    guestUserId,
    guestEmail,
    guestName
);