REST API Endpoints
Log in or Auto-Register
Single-step authentication. If the phone number exists, generates a JWT session token. If the phone number is new, automatically registers the user account.
| Field | Type | Required | Description |
|---|---|---|---|
| phone | string | Yes | E.164 formatted or raw phone string |
| name | string | Yes | Display name for profile & messages |
Successfully authenticated. Returns JWT token and User entity.
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"_id": "6a886007e5d6aac9752288cb",
"name": "Alice Developer",
"phone": "+19991234567",
"createdAt": "2026-08-21T14:26:15.688Z"
}
}curl -X POST "https://frontend-task-chatapp.onrender.com/api/auth/login" \
-H "Content-Type: application/json" \
-d '{ "phone": "+19991234567", "name": "Alice Developer" }'Get Current User Profile
Retrieves profile metadata for the active user matching the Bearer JWT token.
Active user profile details
{
"_id": "6a886007e5d6aac9752288cb",
"name": "Alice Developer",
"phone": "+19991234567",
"createdAt": "2026-08-21T14:26:15.688Z"
}curl -X GET "https://frontend-task-chatapp.onrender.com/api/auth/me" \ -H "Authorization: Bearer <YOUR_JWT_TOKEN>"
Search User Directory
Searches registered users by name substring or phone prefix. Supports query parameter query or q.
| Name | Type | In | Description |
|---|---|---|---|
| query | string | query | Search keyword (name or phone) |
Array of matching user records
[
{
"_id": "6a886008e5d6aac9752288d2",
"name": "Alice Developer",
"phone": "+19991234567"
},
{
"_id": "6a886008e5d6aac9752288d3",
"name": "Alice Cooper",
"phone": "+1888300801"
}
]curl -X GET "https://frontend-task-chatapp.onrender.com/api/users/search?query=Alice" \ -H "Authorization: Bearer <YOUR_JWT_TOKEN>"
List All Conversations
Returns all 1:1 direct and group conversations for the authenticated user, ordered chronologically by last activity.
List of conversations wrapped in data array
{
"data": [
{
"_id": "6a886020e5d6aac9752289b2",
"type": "direct",
"participant": {
"_id": "6a88601ee5d6aac97522899c",
"name": "Bob Test",
"phone": "+1888489938"
},
"lastMessage": {
"text": "Hello Bob! How are you?",
"sender": "6a88601de5d6aac975228994",
"createdAt": "2026-08-21T14:26:41.507Z"
},
"updatedAt": "2026-08-21T14:26:41.741Z"
},
{
"_id": "6a886063e5d6aac975228b0f",
"type": "group",
"name": "Avengers Elite",
"createdBy": "6a886060e5d6aac975228afa",
"admins": [
"6a886060e5d6aac975228afa"
],
"participants": [
{
"_id": "6a886060e5d6aac975228afa",
"name": "Alice",
"phone": "+1999901945"
},
{
"_id": "6a886060e5d6aac975228aff",
"name": "Bob",
"phone": "+1888628604"
}
],
"lastMessage": {
"text": "Avengers Assemble!",
"sender": "6a886060e5d6aac975228afa",
"createdAt": "2026-08-21T14:27:48.632Z"
},
"createdAt": "2026-08-21T14:27:47.444Z",
"updatedAt": "2026-08-21T14:27:50.778Z"
}
]
}curl -X GET "https://frontend-task-chatapp.onrender.com/api/conversations" \ -H "Authorization: Bearer <YOUR_JWT_TOKEN>"
Start Direct 1:1 Conversation
Creates a new direct chat or returns the existing conversation ID if one already exists with the recipient.
| Field | Type | Required | Description |
|---|---|---|---|
| userId | string | Yes | Target recipient user ID |
Existing conversation returned
{
"_id": "6a886020e5d6aac9752289b2",
"participants": [
"6a88601de5d6aac975228994",
"6a88601ee5d6aac97522899c"
],
"createdAt": "2026-08-21T14:26:40.553Z"
}curl -X POST "https://frontend-task-chatapp.onrender.com/api/conversations" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{ "userId": "6a88601ee5d6aac97522899c" }'Create Group Conversation
Creates a multi-participant group. Requires a group name and at least 2 member IDs (3+ total members including creator).
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Group title |
| participantIds | string[] | Yes | Array of at least 2 user IDs |
Group created successfully
{
"_id": "6a886063e5d6aac975228b0f",
"type": "group",
"name": "Avengers Core",
"createdBy": "6a886060e5d6aac975228afa",
"admins": [
"6a886060e5d6aac975228afa"
],
"participants": [
{
"_id": "6a886060e5d6aac975228afa",
"name": "Alice",
"phone": "+1999901945"
},
{
"_id": "6a886060e5d6aac975228aff",
"name": "Bob",
"phone": "+1888628604"
},
{
"_id": "6a886061e5d6aac975228b05",
"name": "Charlie",
"phone": "+1777335673"
}
],
"createdAt": "2026-08-21T14:27:47.444Z",
"updatedAt": "2026-08-21T14:27:47.444Z"
}curl -X POST "https://frontend-task-chatapp.onrender.com/api/conversations/group" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{ "name": "Avengers Core", "participantIds": [ "6a886060e5d6aac975228aff", "6a886061e5d6aac975228b05" ] }'Rename Group Conversation
Updates the title of a group conversation. Caller must be an admin of the group.
| Name | Type | In | Description |
|---|---|---|---|
| id*req | string | path | Group conversation ID |
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | New group title |
Group updated successfully
{
"_id": "6a886063e5d6aac975228b0f",
"type": "group",
"name": "Avengers Elite Team",
"updatedAt": "2026-08-21T14:30:10.123Z"
}curl -X PATCH "https://frontend-task-chatapp.onrender.com/api/conversations/6a886063e5d6aac975228b0f" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{ "name": "Avengers Elite Team" }'Promote Member to Admin
Grants administrator privileges to an existing group participant. Requires admin authorization.
| Name | Type | In | Description |
|---|---|---|---|
| id*req | string | path | Group conversation ID |
| Field | Type | Required | Description |
|---|---|---|---|
| userId | string | Yes | User ID to promote |
Admin list updated
{
"_id": "6a886063e5d6aac975228b0f",
"admins": [
"6a886060e5d6aac975228afa",
"6a886060e5d6aac975228aff"
]
}curl -X POST "https://frontend-task-chatapp.onrender.com/api/conversations/6a886063e5d6aac975228b0f/admins" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{ "userId": "6a886060e5d6aac975228aff" }'Add Participants to Group
Appends one or more user IDs to the group membership list. Requires admin authorization.
| Name | Type | In | Description |
|---|---|---|---|
| id*req | string | path | Group conversation ID |
| Field | Type | Required | Description |
|---|---|---|---|
| userIds | string[] | Yes | Array of user IDs to add |
Participants added
{
"_id": "6a886063e5d6aac975228b0f",
"participants": [
{
"_id": "6a886060e5d6aac975228afa",
"name": "Alice",
"phone": "+1999901945"
},
{
"_id": "6a886061e5d6aac975228b05",
"name": "Charlie",
"phone": "+1777335673"
}
]
}curl -X POST "https://frontend-task-chatapp.onrender.com/api/conversations/6a886063e5d6aac975228b0f/participants" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{ "userIds": [ "6a886061e5d6aac975228b05" ] }'Remove Member / Leave Group
Removes a specific user from the group. If userId matches the caller, the user leaves the group; otherwise requires admin role.
| Name | Type | In | Description |
|---|---|---|---|
| id*req | string | path | Group conversation ID |
| userId*req | string | path | User ID to remove or self-leave |
Participant removed or left successfully
{
"_id": "6a886063e5d6aac975228b0f",
"participants": [
{
"_id": "6a886060e5d6aac975228afa",
"name": "Alice",
"phone": "+1999901945"
}
]
}curl -X DELETE "https://frontend-task-chatapp.onrender.com/api/conversations/6a886063e5d6aac975228b0f/participants/6a886061e5d6aac975228b05" \ -H "Authorization: Bearer <YOUR_JWT_TOKEN>"
Get Message History
Retrieves chronological messages for a conversation.
| Name | Type | In | Description |
|---|---|---|---|
| id*req | string | path | Conversation ID |
List of messages and pagination flag
{
"messages": [
{
"_id": "6a886021e5d6aac9752289be",
"conversation": "6a886020e5d6aac9752289b2",
"sender": "6a88601de5d6aac975228994",
"text": "Hello Bob! How are you?",
"createdAt": "2026-08-21T14:26:41.507Z"
}
],
"hasMore": false
}curl -X GET "https://frontend-task-chatapp.onrender.com/api/conversations/6a886020e5d6aac9752289b2/messages" \ -H "Authorization: Bearer <YOUR_JWT_TOKEN>"
Send Message
Dispatches a new text message. Reconciles with optimistic local state and triggers Socket.io broadcast to all participants.
| Field | Type | Required | Description |
|---|---|---|---|
| conversationId | string | Yes | Target conversation ID |
| text | string | Yes | Message body text |
Message persisted and broadcasted
{
"_id": "6a886021e5d6aac9752289be",
"conversation": "6a886020e5d6aac9752289b2",
"sender": "6a88601de5d6aac975228994",
"text": "Hello world!",
"createdAt": "2026-08-21T14:26:41.507Z"
}curl -X POST "https://frontend-task-chatapp.onrender.com/api/messages" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{ "conversationId": "6a886020e5d6aac9752289b2", "text": "Hello world!" }'Service Health Check
Health check endpoint hosted at server root /health.
Server is healthy
{
"status": "ok"
}curl -X GET "https://frontend-task-chatapp.onrender.com/health"
Real-Time Socket.io Protocol
Real-Time Socket.io Protocol Specification
Chatterbox pairs stateless HTTP REST mutations with a high-throughput persistent Socket.io connection at root origin. The server automatically routes messages to active participant rooms.
message:newTriggered when a peer sends a message{
"id": "6a886064e5d6aac975228b1a",
"conversation": "6a886063e5d6aac975228b0f",
"sender": "6a886060e5d6aac975228afa",
"text": "Avengers Assemble!",
"createdAt": 1787322468632
}Backend Nuance: Socket events transmit id (instead of _id) and createdAt: number (Unix ms). Chatterbox normalizes both at the store layer.
Empirically Verified Backend Quirks & Solutions
During integration testing against the Render backend, our team discovered several undocumented behaviors and payload discrepancies. Here is how Chatterbox handles them defensibly:
Socket Timestamp & Key Normalization
const normalized: Message = {
_id: rawMessage._id || rawMessage.id,
conversation: rawMessage.conversation,
sender: typeof rawMessage.sender === 'object' ? rawMessage.sender?._id : rawMessage.sender,
text: rawMessage.text,
createdAt: typeof rawMessage.createdAt === 'number'
? new Date(rawMessage.createdAt).toISOString()
: rawMessage.createdAt,
};POST /conversations Minimal Shape
// Reconcile and invalidate conversations list
queryClient.invalidateQueries({ queryKey: ['conversations', userId] });Group Creation 3+ Member Validation
Root Origin vs /api Separation
User Search Self-Inclusion
TypeScript Shared Type Definitions
These core types represent the normalized state across both the REST persistence layer and the Socket.io live stream.
export interface User {
_id: string;
name: string;
phone: string;
createdAt?: string;
}
export interface DirectParticipant {
_id: string;
name: string;
phone: string;
}
export interface Conversation {
_id: string;
type: 'direct' | 'group';
name?: string;
createdBy?: string;
admins?: string[];
participants?: DirectParticipant[] | string[];
participant?: DirectParticipant;
lastMessage?: {
text: string;
sender: string;
createdAt: string;
};
createdAt?: string;
updatedAt?: string;
}
export interface Message {
_id: string;
conversation: string;
sender: string; // user ID
text: string;
createdAt: string;
status?: 'sending' | 'sent' | 'failed';
tempId?: string;
}