🧩 Hurcle Platform API

Complete API Documentation for Frontend Integration

Base URL: https://api.hurcle.com/api
POST /auth/register

Description: Registers a new user. After successful registration, an OTP will be sent to the provided email for verification.

πŸ“€ Request Body

{
  "fullName": "John Deo",
  "username": "johnDeo",
  "email": "example@gmail.com",
  "password": "StrongPassword!!@@",
  "dob": "2005-10-09",
  "location":"islamabad,pakistan"
}
⚠️ Validation Rules:
  • Password: Must contain at least one capital letter and one special character
  • DOB: User must be at least 18 years old
  • Email: Must be a valid email format
  • Username: Must be unique
  • Location: You have to ask or get the user’s current location and pass it in this format β€” location: "Islamabad, Pakistan" (city, country)

βœ… Success Response (200)

{
  "message": "User registered successfully. Please verify your email."
}

❌ Error Responses

// 400 Bad Request
{
  "error": "Email already exists"
}

// 400 Bad Request
{
  "error": "Password must contain at least one capital letter and special character"
}

// 400 Bad Request
{
  "error": "User must be at least 18 years old"
}
POST /auth/verify-email-otp

Description: Verifies the OTP sent to the user's email during registration.

πŸ“€ Request Body

{
  "email": "example@gmail.com",
  "otp": 8967
}

βœ… Success Response (200)

{
  "message": "Email verified successfully."
}

❌ Error Responses

// 400 Bad Request
{
  "error": "Invalid or expired OTP"
}

// 404 Not Found
{
  "error": "User not found"
}
POST /auth/resend-email-otp

Description: Resends the email verification OTP to the user.

πŸ“€ Request Body

{
  "email": "demo@yopmail.com"
}

βœ… Success Response (200)

{
  "message": "Verification code resent successfully."
}
⏱️ Expiration Time: 1 minutes.
POST /auth/google/login

Description: Login or sign up via Google OAuth. Frontend handles Google login flow and sends the authorization code.

πŸ“€ Request Body

{
  "code": "12fjfjf"
}

βœ… Success Response - Additional Info Required (200)

{
  "message": "Additional information required",
  "data": {
    "_id": "690b15b6ac128979c08b9b84",
    "email": "redkhan13@gmail.com",
    "fullName": "Afridi Danial",
    "isProfileComplete": false,
    "isGoogleUser": true
  }
}

βœ… Success Response - Complete Profile (200)

{
  "message": "Login successful.",
  "token": "eyJhbGciOiJIUzI1NiIsInR5...",
  "user": {
    "_id": "690b15b6ac128979c08b9b84",
    "fullName": "Afridi Danial",
    "email": "redkhan13@gmail.com"
  }
}
⚠️ Important: If the response contains isProfileComplete: false, redirect the user to complete their profile with username and date of birth before proceeding.
PATCH /auth/complete-google-profile/:userId

Description: Completes profile information for users who registered with Google.

πŸ“€ Request Body

{
  "username": "danialafridibwp",
  "dob": "2002-10-12",
    "location":"city,country"

}

βœ… Success Response (200)

{
  "message": "Profile completed successfully.",
  "data": {
    "_id": "690b15b6ac128979c08b9b84",
    "email": "redkhan13@gmail.com",
    "username": "danialafridibwp",
    "dob": "2002-10-12",
    "token": "eyJhbGciOiJIUzI1NiIsInR5..."
  }
}
πŸ’‘ Note: After successful profile completion, store the token and use it for authenticated requests. Location: You have to ask or get the user’s current location and pass it in this format β€” location: "Islamabad, Pakistan" (city, country)
POST /auth/login

Description: Authenticates a user with email and password.

πŸ“€ Request Body

{
  "email": "example@gmail.com",
  "password": "StrongPassword!!@@"
}

βœ… Success Response (200)

{
  "message": "Login successful.",
  "token": "eyJhbGciOiJIUzI1NiIsInR5...",
  "user": {
    "_id": "690b15b6ac128979c08b9b84",
    "fullName": "John Deo",
    "email": "example@gmail.com"
  }
}

❌ Error Responses

// 401 Unauthorized
{
  "error": "Invalid email or password"
}

// 403 Forbidden
{
  "error": "Please verify your email before logging in"
}
GET /user/me

Description: Fetches the profile details of the currently logged-in user.

πŸ”‘ Headers

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5...

βœ… Success Response (200)

{
  "_id": "690b15b6ac128979c08b9b84",
  "fullName": "John Deo",
  "username": "johnDeo",
  "email": "example@gmail.com",
  "dob": "2005-10-09",
  "isEmailVerified": true
}

❌ Error Responses

// 401 Unauthorized
{
  "error": "Invalid or expired token"
}

// 401 Unauthorized
{
  "error": "No token provided"
}
POST /auth/forget-password

Description: Initiates the password reset process by sending an OTP to the user's email.

πŸ“€ Request Body

{
  "email": "example@gmail.com"
}

βœ… Success Response (200)

{
  "message": "OTP sent to your email address."
}
POST /auth/verify-password-otp

Description: Verifies the OTP sent for password reset.

πŸ“€ Request Body

{
  "email": "example@gmail.com",
  "otp": 4495
}

βœ… Success Response (200)

{
  "message": "OTP verified successfully."
}
⚠️ Important: After successful OTP verification, the user can proceed to reset their password. OTPs expire after 1 minutes.
POST /auth/resend-forgot-password-otp

Description: Resends the password reset OTP to the user's email.

πŸ“€ Request Body

{
  "email": "example@gmail.com"
}

βœ… Success Response (200)

{
  "message": "Forgot password OTP resent successfully."
}
POST /auth/reset-password

Description: Resets the user's password after OTP verification.

πŸ“€ Request Body

{
  "email": "example@gmail.com",
  "password": "Demo@123"
}

βœ… Success Response (200)

{
  "message": "Password reset successfully."
}
⚠️ Security Note: Users cannot access the reset password screen directly. OTP verification is required first as a security measure.

πŸ“‹ Common Error Codes Reference

Status Code Error Type Description
400 Bad Request Invalid input data or validation error
401 Unauthorized Missing or invalid authentication token
403 Forbidden Email not verified or insufficient permissions
404 Not Found Resource (user, endpoint) not found
409 Conflict Email or username already exists
429 Too Many Requests Rate limit exceeded (OTP requests)
500 Internal Server Error Unexpected server error

πŸ”„ Authentication Flow Diagram

Standard Registration Flow:

1. POST /auth/register β†’ 2. POST /auth/verify-email-otp β†’ 3. POST /auth/login

Google OAuth Flow (New User):

1. POST /auth/google/login β†’ 2. PATCH /auth/complete-google-profile/:userId β†’ 3. Access granted

Google OAuth Flow (Existing User):

1. POST /auth/google/login β†’ 2. Access granted (token received)

Password Reset Flow:

1. POST /auth/forget-password β†’ 2. POST /auth/verify-password-otp β†’ 3. POST /auth/reset-password

User Profile Management

GET {{LocalUrl}}/user/me

Description: Retrieve the current authenticated user’s profile details.

πŸ”‘ Headers

Authorization: Bearer <token>

βœ… Success Response

{
  "_id": "680b15b6ac128979c08b9b84",
  "fullName": "Demo User",
  "username": "demo",
  "bio": "This is the demo user of Hurcle Platform",
  "image": "https://i.pravatar.cc/150?img=32",
  "language": "English",
  "privacy": "PUBLIC",
  "notificationsEnabled": true
}
PATCH {{LocalUrl}}/user/me

Description: Update user profile information such as fullName, username, bio, or image. To update the profile image, follow the steps below before calling this endpoint.

πŸ–ΌοΈ Image Upload Flow for Frontend:
  • 1️⃣ Let the user select one or more image files from their device.
  • 2️⃣ Send the selected file(s) to {{LocalUrl}}/media/upload-multiple using FormData with the key name files.
  • 3️⃣ The response will return one or more uploaded image URLs.
  • 4️⃣ Use the returned image URL to update the user profile by passing it as image: "uploadedImageUrl" in the PATCH payload below.

πŸ“€ Request Body

{
  "fullName": "Demo User",
  "username": "demo",
  "bio": "This is the demo user of Hurcle Platform",
  "image": "https://cdn.hurcle.com/uploads/unique-image-url.jpg"
}

βœ… Image Upload Example (Frontend)

// Example JavaScript (FormData upload)
const formData = new FormData();
formData.append("files", selectedFile);

const uploadRes = await fetch("{{LocalUrl}}/media/upload-multiple", {
  method: "POST",
  body: formData
});
const data = await uploadRes.json();
const imageUrl = data[0]; // use this in your profile update

// Then call PATCH /user/me
await fetch("{{LocalUrl}}/user/me", {
  method: "PATCH",
  headers: {
    "Authorization": "Bearer ",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    fullName: "Demo User",
    username: "demo",
    bio: "This is the demo user of Hurcle Platform",
    image: imageUrl
  })
});
PATCH {{LocalUrl}}/user/me/password

Description: Change the user’s password securely from the profile page.

πŸ“€ Request Body

{
  "currentPassword": "Afridi4you!@@",
  "newPassword": "Demo@123"
}
PATCH {{LocalUrl}}/user/notifications

Description: Enable or disable user notifications.

πŸ“€ Request Body

{
  "notificationsEnabled": true
}
PATCH {{LocalUrl}}/user/privacy

Description: Update the user’s account privacy to PRIVATE or PUBLIC.

πŸ“€ Request Body

{
  "privacy": "PRIVATE"
}
PATCH {{LocalUrl}}/user/me/language

Description: Change user language preference.

πŸ“€ Request Body

{
  "language": "French"
}
DELETE {{LocalUrl}}/user/me/delete

Description: Delete the current user account (token authentication required).

πŸ”‘ Headers

Authorization: Bearer <token>
⚠️ Caution: This action is irreversible and permanently deletes the user’s data.

Post Management

POST {{LocalUrl}}/post/create

Description: Create a new post with text content, optional media, visibility settings, and an optional location.

πŸ–ΌοΈ Image/Video Upload Flow:
  • 1️⃣ Allow users to select one or more files (images/videos).
  • 2️⃣ Upload them to {{LocalUrl}}/media/upload-multiple using FormData with key files.
  • 3️⃣ Get the uploaded media URLs from the response.
  • 4️⃣ Include them in the media array when creating a post.

πŸ“€ Request Body

{
  "content": "Enjoying the sunset at the beach!",
  "media": [
    {
      "url": "https://cdn.hurcle.com/uploads/beach-photo.jpg",
      "type": "image"
    }
  ],
  "visibility": "public",
  "location": {
    "type": "Point",
    "coordinates": [73.0479, 33.6844]
  }
}
GET {{LocalUrl}}/post

Description: Retrieve all public posts available to the authenticated user. Supports pagination.

πŸ” Query Params

?page=1&limit=10
GET {{LocalUrl}}/post/all

Description: Admin-only endpoint for viewing all posts in the system.

GET {{LocalUrl}}/post/map-area

Description: Get posts within a specific map area, defined by latitude and longitude bounds.

πŸ” Query Params

?north=33.70&south=33.60&east=73.10&west=73.00&limit=20&offset=0
GET {{LocalUrl}}/post/:id

Description: Get a specific post by its ID for the authenticated user.

GET {{LocalUrl}}/post/user/:id

Description: Retrieve all posts created by a specific user ID.

PATCH {{LocalUrl}}/post/:id

Description: Update an existing post’s content, visibility, or location. ⚠️ Note: Once a post is created, its attached media (images/videos) cannot be modified or replaced. To change media, the post must be deleted and recreated.

πŸ“€ Request Body (Editable Fields Only)

{
  "content": "Enjoying  a  day at the ground πŸ–οΈ!",
  "visibility": "public",
  "location": {
    "type": "Point",
    "coordinates": [72.11, 72.22]
  }
}
🚫 Media Update Restriction: Uploading or replacing media via /media/upload-multiple is not permitted in this endpoint. The media field is read-only after post creation.
DELETE {{LocalUrl}}/post/:id

Description: Delete a specific post created by the authenticated user.

⚠️ Caution: This will permanently remove the post and all associated media links.

Chat Management

POST /chat/direct

Description: Creates a one-to-one encrypted chat between the current user and another contact.

πŸ“€ Request Body

{
  "participantId": "6911aabbccddeeff11223344"
}
πŸ’‘ Note: A direct chat will only be created if both users have added each other as contacts.

βœ… Example Response

{
  "_id": "6912f8f7d2c3a2b4f1a2d3e4",
  "type": "direct",
  "members": ["690304822443a24bb8d62188", "6911aabbccddeeff11223344"],
  "createdBy": "690304822443a24bb8d62188",
  "createdAt": "2025-11-11T07:20:00Z"
}
POST /chat/group

Description: Creates a new group chat with multiple members and an optional group public key for encryption. The description field may include an explanation for image usage, such as:

πŸ“€ Request Body

{
  "name": "Team Alpha",
  "members": ["690304822443a24bb8d62188", "6911aabbccddeeff11223344"],
  "description": "Project discussion group with image upload",
  "image": "https://your-uploaded-image-url.jpg"
}

βœ… Example Response

{
  "_id": "6912f8f7d2c3a2b4f1a2d3e4",
  "type": "group",
  "name": "Team Alpha",
  "members": [
    "690304822443a24bb8d62188",
    "6911aabbccddeeff11223344"
  ],
  "createdBy": "690304822443a24bb8d62188",
  
  "createdAt": "2025-11-11T07:25:00Z"
}
GET /chat

Description: Fetches all chats (direct and group) the authenticated user is a member of.

βœ… Example Response

[
  {
    "_id": "6911bebbab947721a2b45027",
    "type": "direct",
    "members": [
      { "_id": "690304822443a24bb8d62188", "username": "danial" },
      { "_id": "6911aabbccddeeff11223344", "username": "ahmad" }
    ]
  },
  {
    "_id": "6911bebbab947721a2b45029",
    "type": "group",
    "name": "Team Alpha",
    "members": [
      "690304822443a24bb8d62188",
      "6911aabbccddeeff11223344"
    ]
  }
]
GET /chat/:chatId/messages

Description: Fetches all encrypted messages belonging to a specific chat.

βœ… Example Response

[
  {
    "_id": "6912f12345a7efb100abc111",
    "senderId": "690304822443a24bb8d62188",
    "ciphertext": "U2FsdGVkX1+encryptedText==",
    "encryptedKey": "RSA_KEY_STRING",
    "iv": "randomIVstring",
    "coordinates": { "lat": 31.5204, "lng": 74.3587 },
    "createdAt": "2025-11-11T07:30:00Z"
  }
]
WS WebSocket Events

Description: Real-time encrypted chat communication over WebSockets. All events require authentication via token (WsAuthGuard).

πŸ“‘ Client β†’ Server Events

1️⃣ joinChat
{
  "event": "joinChat",
  "data": "chatId"
}

β†’ Joins a chat room if the authenticated user is a member. Membership is validated server-side.

2️⃣ sendMessage

Direct Chat

{
  "event": "sendMessage",
  "data": {
    "chatId": "6912f8f7d2c3a2b4f1a2d3e4",
    "ciphertext": "BASE64_ENCRYPTED_TEXT",
    "nonce": "BASE64_NONCE",
    "coordinates": { "lat": 31.5204, "lng": 74.3587 },
    "media": []
  }
}

β†’ Requires either ciphertext + nonce or media only.

Group Chat

{
  "event": "sendMessage",
  "data": {
    "chatId": "6912f8f7d2c3a2b4f1a2d3e4",
    "groupCiphertext": {
      "keyId": "group-key-id",
      "ciphertexts": {
        "USER_ID_1": "ENCRYPTED_TEXT_1",
        "USER_ID_2": "ENCRYPTED_TEXT_2"
      }
    },
    "groupNonce": {
      "nonces": {
        "USER_ID_1": "NONCE_1",
        "USER_ID_2": "NONCE_2"
      }
    },
    "coordinates": { "lat": 31.5204, "lng": 74.3587 },
    "media": []
  }
}

β†’ Requires valid group encryption payload or media. Encryption is per-recipient.

3️⃣ markMessagesRead
{
  "event": "markMessagesRead",
  "data": {
    "chatId": "6912f8f7d2c3a2b4f1a2d3e4"
  }
}

β†’ Marks all unread messages as read for the current user.

πŸ“€ Server β†’ Client Events

βœ… newMessage

Emitted to all chat members when a message is sent successfully.

{
  "message": "Message sent successfully",
  "data": {
    "_id": "6912f12345a7efb100abc111",
    "chatId": "6912f8f7d2c3a2b4f1a2d3e4",

    "ciphertext": "BASE64_ENCRYPTED_TEXT",
    "nonce": "BASE64_NONCE",

    "groupCiphertext": {
      "keyId": "group-key-id",
      "ciphertexts": {
        "USER_ID_1": "ENCRYPTED_TEXT_1"
      }
    },
    "groupNonce": {
      "nonces": {
        "USER_ID_1": "NONCE_1"
      }
    },

    "media": [],
    "coordinates": { "lat": 31.5204, "lng": 74.3587 },

    "sender": {
      "_id": "690304822443a24bb8d62188",
      "username": "danial",
      "fullName": "Danial Ahmed",
      "image": "profile.jpg",
      "userPublicKey": "RSA_PUBLIC_KEY"
    },

    "chat": {
      "_id": "6912f8f7d2c3a2b4f1a2d3e4",
      "type": "group",
      "members": [
        {
          "_id": "690304822443a24bb8d62188",
          "username": "danial",
          "fullName": "Danial Ahmed",
          "image": "profile.jpg",
          "userPublicKey": "RSA_PUBLIC_KEY"
        }
      ]
    },

    "createdAt": "2025-11-11T07:35:00Z"
  }
}
πŸ“– messagesRead
{
  "chatId": "6912f8f7d2c3a2b4f1a2d3e4",
  "readerId": "690304822443a24bb8d62188",
  "modifiedCount": 5,
  "remainingUnread": 0
}
❌ newMessageError
{
  "message": "Message must include either encrypted text or media"
}