BIO.RE
Messages

Get Unread Count

Lightweight badge counter — total unread messages where you're the receiver. Counts messages in PENDING / ESCROWED / DELIVERED states across all sessions.

GET /api/v1/messages/unread-count — 🔑 Bearer · Rate limit: 60 req / minute · Kill-switched

Returns the total count of unread messages for the calling user — across all sessions. "Unread" = you're the receiverId AND status IN (PENDING, ESCROWED, DELIVERED). Use this to drive the navigation badge / app icon counter.

Read state isn't user-driven. There is no POST /messages/:id/read — status flips to READ server-side when the chat-service Socket.IO layer marks the message read in real-time (or via internal background job). This endpoint counts messages still in PENDING / ESCROWED / DELIVERED — anything beyond READ (REPLIED / COMPLETED / EXPIRED / REFUNDED / REJECTED / QUARANTINED) is excluded.

Request

No body, no params.

HeaderRequiredNotes
Authorization: Bearer <accessToken>JWT from POST /auth/login

Response

200 OKApiResponseOf<UnreadCountResponseDto>

{
  "success": true,
  "data": {
    "total": 5
  }
}
FieldTypeNotes
totalnumberTotal unread messages across all sessions where you're the receiver

Errors

HTTPcode / i18nKeyReason
401(guard)Missing / invalid bearer token
429(throttle)Rate limit exceeded (60 req/min)
503features.messaging_disabledAdmin kill switch MESSAGING is active

Side effects

  1. Single prisma.message.count({ where: { receiverId: userId, status: { in: [PENDING, ESCROWED, DELIVERED] } } }).
  2. Return { total }. No mutations.

Polling guidance

  • Refresh on app focus + after returning from a chat screen (Socket.IO event flips status → count drops).
  • Don't tight-loop poll — the count flips via Socket.IO events delivered to the chat-service WebSocket. Subscribe there for real-time updates instead of polling.
  • A 30s background refresh covers the case where the user backgrounded the WebSocket layer.

Code samples

curl https://api.bio.re/api/v1/messages/unread-count \
  -H "Authorization: Bearer $ACCESS_TOKEN"
async function getUnreadCount(accessToken: string): Promise<number> {
  const res = await fetch('https://api.bio.re/api/v1/messages/unread-count', {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Unread count fetch failed'), {
      code: json?.error?.code,
    });
  }
  return json.data.total as number;
}
import { useQuery } from '@tanstack/react-query';

export const messageKeys = {
  unreadCount: () => ['messages', 'unread-count'] as const,
};

export function useUnreadCount() {
  return useQuery({
    queryKey: messageKeys.unreadCount(),
    queryFn: async () => {
      const res = await fetch('/api/v1/messages/unread-count');
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Unread count fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data.total as number;
    },
    // Refresh on focus is the primary trigger; tight polling is wasteful (use Socket.IO instead)
    staleTime: 30_000,
    refetchOnWindowFocus: true,
  });
}

Try it

GET
/api/v1/messages/unread-count
AuthorizationBearer <token>

In: header

Response Body

application/json

application/json

curl -X GET "https://loading/api/v1/messages/unread-count"
{
  "success": true,
  "data": {
    "total": 5
  }
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/message/message.controller.ts125–131 (getUnreadCount)
DTO (response)apps/api-core/src/modules/message/dto/message-client-response.dto.ts155–158 (UnreadCountResponseDto)
Serviceapps/api-core/src/modules/message/message.service.ts915–923 (getUnreadCount)
Real-time updatesapps/chat-service/Socket.IO events flip read state on the server side
Prisma modelpackages/prisma/prisma/schema.prismaMessage.status (enum MessageStatus), Message.receiverId

On this page