# Posts Management API

**Author**: Josh, Lead Backend Team  
**Last Updated**: 2026-06-18  
**Version**: v1.0

**Base URL**: `https://api.nexgate.com/api/v1`

**Short Description**: The Posts Management API provides comprehensive social media posting functionality including creating posts, polls, quote posts, drafts management, media attachments, commerce integrations, collaboration features, and engagement interactions (likes, bookmarks, reposts, comments). This API enables users to create rich content with privacy controls and scheduled publishing.

**Hints**:
- All endpoints require authentication via Bearer token unless specified
- Draft posts follow a "one draft at a time" rule - publish or discard before creating new
- Posts support text content, media (images/videos), polls, and commerce attachments
- Privacy settings control visibility, comment permissions, and repost permissions
- Collaborative posts require accepted invitations from collaborators
- Pagination is 1-indexed (page=1 for first page), default size is 20 items
- Poll votes can be changed if poll allows it (allowVoteChange setting)

---

## Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

### Success Response Structure
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-09-23T10:30:45",
  "data": {
    // Actual response data goes here
  }
}
```

### Error Response Structure
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-09-23T10:30:45",
  "data": "Error description"
}
```

### Standard Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` for successful operations, `false` for errors |
| `httpStatus` | string | HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.) |
| `message` | string | Human-readable message describing the operation result |
| `action_time` | string | ISO 8601 timestamp of when the response was generated |
| `data` | object/string | Response payload for success, error details for failures |

---

## HTTP Method Badge Standards

For better visual clarity, all endpoints use colored badges for HTTP methods with the following standard colors:

- **GET** - <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> - Green (Safe, read-only operations)
- **POST** - <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> - Blue (Create new resources)
- **PUT** - <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> - Yellow (Update/replace entire resource)
- **DELETE** - <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> - Red (Remove resources)

---

## Common Response Structure for POST/PUT Operations

Most POST and PUT operations return a PostResponse object. For brevity, this documentation references the detailed PostResponse structure defined below instead of repeating it for each endpoint.

**Standard Success Response for Post Creation/Update:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "[Operation-specific message]",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    // PostResponse object - see "PostResponse Structure" section below
  }
}
```

---

## PostResponse Structure

This is the standard response structure returned by most post-related endpoints:

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "author": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "userName": "john_doe",
    "firstName": "John",
    "lastName": "Doe",
    "profilePictureUrl": "https://cdn.nexgate.com/profiles/john_doe.jpg",
    "isVerified": true
  },
  "content": "Check out this amazing product! @jane_doe #shopping",
  "contentParsed": {
    "text": "Check out this amazing product! @jane_doe #shopping",
    "entities": [
      {
        "type": "MENTION",
        "text": "@jane_doe",
        "startIndex": 33,
        "endIndex": 42,
        "user": {
          "id": "987e6543-e21b-12d3-a456-426614174999",
          "userName": "jane_doe",
          "firstName": "Jane",
          "lastName": "Doe",
          "profilePictureUrl": "https://cdn.nexgate.com/profiles/jane_doe.jpg"
        }
      },
      {
        "type": "HASHTAG",
        "text": "#shopping",
        "startIndex": 43,
        "endIndex": 52,
        "hashtag": "shopping"
      }
    ]
  },
  "postType": "REGULAR",
  "postKind": "ORIGINAL",
  "status": "PUBLISHED",
  "media": [
    {
      "id": "media-123",
      "mediaType": "IMAGE",
      "originalUrl": "https://cdn.nexgate.com/posts/image1.jpg",
      "thumbnailUrl": "https://cdn.nexgate.com/posts/image1_thumb.jpg",
      "placeholderBase64": "base64-string-here",
      "width": 1920,
      "height": 1080,
      "order": 1,
      "imageTags": []
    }
  ],
  "attachments": {
    "products": [],
    "shops": [],
    "events": [],
    "buyTogetherGroups": [],
    "installmentPlans": [],
    "externalLink": null
  },
 "collaboration": {
  "isCollaborative": true,
  "collaborators": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "status": "ACCEPTED",
      "invitedAt": "2024-01-15T10:00:00",
      "respondedAt": "2024-01-15T10:30:00",
      "user": {
        "id": "u1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "userName": "john_doe",
        "firstName": "John",
        "lastName": "Doe",
        "profilePictureUrl": "https://cdn.example.com/photos/john.jpg"
      }
    },
    {
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "status": "PENDING",
      "invitedAt": "2024-01-15T11:00:00",
      "respondedAt": null,
      "user": {
        "id": "u2c3d4e5-f6a7-8901-bcde-f12345678901",
        "userName": "current_user",
        "firstName": "Current",
        "lastName": "User",
        "profilePictureUrl": "https://cdn.example.com/photos/current.jpg"
      }
    }
  ],
  "byline": "By Jane Smith and 1 others",
  "currentUserStatus": "PENDING",
  "currentUserIsPendingCollaborator": true,
  "currentUserIsAcceptedCollaborator": false
},
  "privacySettings": {
    "visibility": "PUBLIC",
    "whoCanComment": "EVERYONE",
    "whoCanRepost": "EVERYONE",
    "hideLikesCount": false,
    "hideCommentsCount": false
  },
  "engagement": {
    "likesCount": 0,
    "commentsCount": 0,
    "repostsCount": 0,
    "quotesCount": 0,
    "bookmarksCount": 0,
    "sharesCount": 0,
    "viewsCount": 0,
    "canLike": true,
    "canComment": true,
    "canRepost": true,
    "canShare": true
  },
   "userInteraction": {
      "hasLiked": false,
      "hasBookmarked": true,
      "hasReposted": false,
      "myRepostId": null,
      "hasQuoted": true,
      "hasCommented": false,
      "hasViewed": true,
      "hasShared": false
    },
  "topComments": [
    {
      "id": "comment-001",
      "userId": "user-abc-123",
      "content": "This is amazing! 🔥",
      "userName": "sarah_smith",
      "displayName": "Sarah Smith",
      "profilePictureUrl": "https://cdn.nexgate.com/profiles/sarah_smith.jpg"
    },
    {
      "id": "comment-002",
      "userId": "user-xyz-789",
      "content": "Where can I get this?",
      "userName": "mike_wilson",
      "displayName": "Mike Wilson",
      "profilePictureUrl": "https://cdn.nexgate.com/profiles/mike_wilson.jpg"
    }
  ],
  "topLikers": [
    {
      "userId": "user-abc-123",
      "userName": "john_doe",
      "displayName": "John Doe",
      "profilePictureUrl": "https://cdn.nexgate.com/profiles/john_doe.jpg"
    },
    {
      "userId": "user-xyz-789",
      "userName": "sarah_smith",
      "displayName": "Sarah Smith",
      "profilePictureUrl": "https://cdn.nexgate.com/profiles/sarah_smith.jpg"
    }
  ],
  "topReposters": [
    {
      "userId": "user-lmn-456",
      "userName": "mike_wilson",
      "displayName": "Mike Wilson",
      "profilePictureUrl": "https://cdn.nexgate.com/profiles/mike_wilson.jpg"
    }
  ],
  "createdAt": "2025-12-11T10:30:45",
  "publishedAt": "2025-12-11T10:30:45",
  "scheduledAt": null
}
```

---

## Endpoints

## 1. Create Post
**Purpose**: Create a new post (draft or scheduled) with optional content, media, poll, attachments, and collaboration

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Creates a post with status DRAFT (unless scheduledAt is provided, then SCHEDULED)
- Enforces "one draft at a time" rule - user must publish or discard existing draft first
- Automatically parses @mentions, $shop tags, and #hashtags from content
- Validates all attached products, shops, events exist and are active
- Sends collaboration invitations to specified users via notifications
- For scheduled posts, validates scheduledAt is at least 5 minutes in the future
- Media files must already be uploaded to file service before creating post
- Collaborative posts remain in PENDING status until all invitees accept/reject

**Business Rules**:
1. **Draft System**: Only one draft allowed per user at a time
2. **Content Requirements**: Either content text OR media OR poll must be provided (not all optional)
3. **Poll Requirements**: If postType is POLL, poll object with 2-10 options is required
4. **Media Limits**: Maximum 10 media items per post
5. **Commerce Limits**: Max 10 products, 5 shops, 3 events per post
6. **Collaboration Limits**: Maximum 5 collaborators per post
7. **Scheduling Rules**: Scheduled time must be at least 5 minutes in future, max 6 months ahead
8. **Mention Extraction**: System automatically detects and links @username and $shopname mentions
9. **Hashtag Indexing**: All #hashtags automatically indexed for search and trending
10. **Privacy Inheritance**: MENTIONED visibility requires at least one @mention in content

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Request JSON Sample**:
```json
{
  "content": "Excited to share my latest project! @john_doe check this out #coding",
  "postType": "REGULAR",
  "media": [
    {
      "mediaType": "IMAGE",
      "mediaUrl": "https://cdn.nexgate.com/uploads/image1.jpg",
      "placeholderBase64": "base64-string",
      "width": 1920,
      "height": 1080
    }
  ],
  "privacySettings": {
    "visibility": "PUBLIC",
    "whoCanComment": "EVERYONE",
    "whoCanRepost": "EVERYONE",
    "hideLikesCount": false,
    "hideCommentsCount": false
  },
  "scheduledAt": null
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| content | string | No* | Post text content | Max: 5000 characters. *Required if no media/poll |
| postType | string | Yes | Type of post | enum: REGULAR, POLL |
| media | array | No | Array of media objects (images/videos) | Max: 10 items |
| media[].mediaType | string | Yes (if media provided) | Type of media | enum: IMAGE, VIDEO |
| media[].mediaUrl | string | Yes (if media provided) | URL of the media file | Valid URL format, must be from approved CDN |
| media[].placeholderBase64 | string | No | BlurHash or base64 placeholder | Used for progressive image loading |
| media[].width | integer | No | Media width in pixels | Min: 1, Max: 8000 |
| media[].height | integer | No | Media height in pixels | Min: 1, Max: 8000 |
| media[].duration | integer | No | Video duration in seconds | Required for videos, Max: 600 (10 minutes) |
| poll | object | No | Poll configuration | Required if postType is POLL |
| poll.title | string | Yes (if poll provided) | Poll question/title | Min: 5, Max: 500 characters |
| poll.description | string | No | Additional poll description | Max: 1000 characters |
| poll.options | array | Yes (if poll provided) | Array of poll options | Min: 2, Max: 10 |
| poll.options[].optionText | string | Yes | Text for the poll option | Min: 1, Max: 200 characters |
| poll.options[].optionImageUrl | string | No | Optional image for the option | Valid URL format, must be from approved CDN |
| poll.allowMultipleVotes | boolean | No | Allow selecting multiple options | Default: false |
| poll.isAnonymous | boolean | No | Hide voter identities | Default: true. If false, voters visible to post author |
| poll.allowVoteChange | boolean | No | Allow users to change their vote | Default: true. If false, votes are final |
| poll.expiresAt | string | No | Poll expiration datetime | ISO 8601 format, must be future, max 30 days from now |
| poll.hideResultsUntilExpiry | boolean | No | Hide vote counts and results until the poll expires | Default: false. When `true`, results are only revealed after `expiresAt` passes — even voters cannot see counts before then. Requires `expiresAt` to be meaningful; if no expiry is set, results remain hidden indefinitely |
| attachments | object | No | Commerce attachments | |
| attachments.productIds | array | No | Array of product UUIDs to attach | Max: 10. Products must be ACTIVE status |
| attachments.shopIds | array | No | Array of shop UUIDs to attach | Max: 5. Shops must be VERIFIED |
| attachments.eventIds | array | No | Array of event UUIDs to attach | Max: 3. Events must be PUBLISHED |
| attachments.buyTogetherGroupIds | array | No | Array of group purchase UUIDs | Groups must be ACTIVE, not COMPLETED |
| attachments.installmentPlanIds | array | No | Array of installment plan UUIDs | Plans must be ACTIVE, not EXPIRED |
| attachments.externalLink | object | No | External link object | Only one external link allowed per post |
| attachments.externalLink.url | string | Yes (if externalLink provided) | URL to attach | Valid URL format, supports http/https only |
| collaboration | object | No | Collaboration settings | |
| collaboration.collaboratorIds | array | No | Array of user UUIDs to invite | Max: 5. Users must not be blocked by author |
| privacySettings | object | No | Privacy configuration | |
| privacySettings.visibility | string | No | Post visibility level | enum: PUBLIC, FOLLOWERS, MENTIONED, PRIVATE. Default: PUBLIC |
| privacySettings.whoCanComment | string | No | Comment permission level | enum: EVERYONE, FOLLOWERS, MENTIONED, DISABLED. Default: EVERYONE |
| privacySettings.whoCanRepost | string | No | Repost permission level | enum: EVERYONE, FOLLOWERS, DISABLED. Default: EVERYONE |
| privacySettings.hideLikesCount | boolean | No | Hide like count from others | Default: false. Author always sees true count |
| privacySettings.hideCommentsCount | boolean | No | Hide comment count from others | Default: false. Author always sees true count |
| scheduledAt | string | No | Schedule post for future publication | ISO 8601 format, must be 5+ minutes future, max 6 months ahead |

**Validation Notes**:
- At least one of: content, media, or poll must be provided
- If postType is POLL, content is optional but poll object is required
- `hideResultsUntilExpiry: true` without an `expiresAt` means results stay hidden indefinitely — set an expiry when using this flag
- Media files must be pre-uploaded to file service and URLs must be from approved CDN domains
- All UUIDs (products, shops, events, users) are validated for existence and status
- Mentions (@username, $shopname) and hashtags (#tag) are automatically extracted from content
- MENTIONED visibility requires at least one @mention in content
- Scheduled posts cannot be in the past; minimum 5 minutes buffer required
- Collaborative posts send real-time notifications to all invited users
- External links are validated for protocol (http/https only) and reachability

**Success Response**: Returns standard PostResponse structure (see "PostResponse Structure" section above)

**Success Response Message**: "Post created successfully"

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "You already have a draft post",
  "action_time": "2025-12-11T10:30:45",
  "data": "You already have a draft post. Please update or publish your existing draft before creating a new one."
}
```

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `400 BAD_REQUEST`: Already have a draft post, invalid post data, or validation errors
- `401 UNAUTHORIZED`: Authentication issues (empty, invalid, expired, or malformed tokens)
- `422 UNPROCESSABLE_ENTITY`: Validation errors with detailed field information
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

**Error Response Examples**:

*Bad Request - Existing Draft (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "You already have a draft post",
  "action_time": "2025-12-11T10:30:45",
  "data": "You already have a draft post. Please update or publish your existing draft before creating a new one."
}
```

*Validation Error - Content Too Long (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    "content": "Content exceeds maximum length of 5000 characters"
  }
}
```

*Validation Error - Invalid Poll (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    "poll.options": "Poll must have at least 2 options"
  }
}
```

*Validation Error - Empty Post (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Post must have content, media, or poll",
  "action_time": "2025-12-11T10:30:45",
  "data": "At least one of content, media, or poll must be provided"
}
```

*Validation Error - Invalid Product Attachment (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found",
  "action_time": "2025-12-11T10:30:45",
  "data": "Product with ID 550e8400-e29b-41d4-a716-446655440000 not found or inactive"
}
```

*Validation Error - Scheduled Time in Past (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid scheduled time",
  "action_time": "2025-12-11T10:30:45",
  "data": "Scheduled time must be at least 5 minutes in the future"
}
```

*Validation Error - Blocked Collaborator (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot collaborate with blocked user",
  "action_time": "2025-12-11T10:30:45",
  "data": "User 770e8400-e29b-41d4-a716-446655440001 has blocked you or you have blocked them"
}
```

**Use Case Examples**:

*Example 1: Simple Text Post*
```json
{
  "content": "Just launched my new product! Check it out at nexgate.com/myshop",
  "postType": "REGULAR"
}
```
Result: Creates a PUBLIC draft post with text only, no attachments.

*Example 2: Product Showcase Post*
```json
{
  "content": "New arrivals! 🎉 Amazing deals on electronics. $TechWorld #shopping #deals",
  "postType": "REGULAR",
  "media": [
    {
      "mediaType": "IMAGE",
      "mediaUrl": "https://cdn.nexgate.com/uploads/product1.jpg",
      "width": 1920,
      "height": 1080
    }
  ],
  "attachments": {
    "productIds": ["550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002"],
    "shopIds": ["660e8400-e29b-41d4-a716-446655440003"]
  }
}
```
Result: Creates draft with image, attaches 2 products and 1 shop, extracts $TechWorld mention and #shopping/#deals hashtags.

*Example 3: Poll Post*
```json
{
  "content": "What feature should we build next? Vote below! 👇",
  "postType": "POLL",
  "poll": {
    "title": "Next Feature Priority",
    "description": "Help us decide what to build for Q1 2026",
    "options": [
      {"optionText": "Dark Mode"},
      {"optionText": "Video Upload"},
      {"optionText": "Live Streaming"},
      {"optionText": "Advanced Analytics"}
    ],
    "allowMultipleVotes": true,
    "isAnonymous": false,
    "allowVoteChange": true,
    "expiresAt": "2025-12-25T23:59:59Z",
    "hideResultsUntilExpiry": true
  }
}
```
Result: Creates poll allowing multiple votes, non-anonymous (voters visible), votes can be changed, expires Dec 25. Results (vote counts) are hidden from everyone until Dec 25 — after expiry they become visible automatically.

*Example 4: Scheduled Collaborative Event Post*
```json
{
  "content": "Excited to announce our tech conference! @john_doe and I are hosting. Join us! 🚀 #TechConf2026",
  "postType": "REGULAR",
  "attachments": {
    "eventIds": ["880e8400-e29b-41d4-a716-446655440004"]
  },
  "collaboration": {
    "collaboratorIds": ["770e8400-e29b-41d4-a716-446655440001"]
  },
  "scheduledAt": "2025-12-20T08:00:00Z"
}
```
Result: Creates scheduled post for Dec 20, attaches event, sends collaboration invite to john_doe, extracts @john_doe mention and #TechConf2026 hashtag.

---

## 2. Publish Draft Post

**Purpose**: Publish the current user's draft post

**Endpoint**: <span style="background-color: #49cc90; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/api/v1/e-social/posts/publish`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Content Validation Rules**:

The post must pass content validation before publishing. At minimum, a post must have meaningful content.

| Scenario | Allowed? | Notes |
|----------|----------|-------|
| Text only | ✅ Yes | Valid standalone post |
| Text + Media | ✅ Yes | Valid post with images/videos |
| Media only (no text) | ✅ Yes  | Image/video post without caption |
| Poll only | ✅ Yes | Poll title serves as content |
| Poll + Text | ✅ Yes | Enhanced poll post |
| Quote only | ✅ Yes | Quoted post provides context |
| Quote + Text | ✅ Yes | Quote with commentary |
| Attachment only (product/shop/event/link) | ✅ Yes | Commerce/social attachment |
| Attachment + Text | ✅ Yes | Enhanced attachment post |
| Attachment + Media (no text) | ✅ Yes | Rich commerce post |
| Attachment + Media + Text | ✅ Yes | Full featured post |
| Empty post | ❌ No | Must have at least one content type |

**Validation Error Messages**:
| Condition | Error Message |
|-----------|---------------|
| Media without text | "Media posts must include text content" |
| Empty post | "Post must have at least one of: text content, poll, quote, or attachments" |
| No draft found | "No draft found" |
| Pending collaborators | "Cannot publish collaborative post - waiting for collaborator acceptance" |

**Business Rules**:
1. **Draft Requirement**: User must have exactly one draft post
2. **Content Validation**: Draft must pass all content validation rules (see table above)
3. **Media Rule**: Posts with media (images/videos) must include text content
4. **Collaboration Status**: All invited collaborators must have accepted (status = ACCEPTED)
5. **Attachment Validation**: All attached products/shops/events must still be active/valid
6. **Scheduled Posts**: Cannot publish a scheduled post using this endpoint
7. **Feed Distribution**: Published posts immediately appear in followers' feeds
8. **Notification Triggers**: Sends notifications to @mentioned users and collaborators

**Validation Checks on Publish**:
- Draft exists and belongs to current user
- Content validation passes (text, media, poll, quote, or attachments)
- Media posts have accompanying text content
- All collaborators have accepted (if collaborative post)
- All attached products are still ACTIVE
- All attached shops are still VERIFIED
- All attached events are still PUBLISHED
- Content meets length requirements (max 5000 chars)
- Media URLs are valid and accessible
- Poll configuration is valid (if poll post)

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post published successfully",
  "action_time": "2025-12-29T10:30:45",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "author": {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "userName": "john_doe",
      "firstName": "John",
      "lastName": "Doe",
      "profilePictureUrl": "https://storage.example.com/profiles/john.jpg",
      "isVerified": true
    },
    "content": "Check out this amazing product! 🛍️",
    "postType": "REGULAR",
    "status": "PUBLISHED",
    "media": [
      {
        "id": "media-001",
        "mediaType": "IMAGE",
        "originalUrl": "https://storage.example.com/posts/image1.jpg",
        "thumbnailUrl": "https://storage.example.com/posts/image1_thumb.jpg",
        "width": 1080,
        "height": 1080,
        "order": 0
      }
    ],
    "attachments": {
      "products": [
        {
          "id": "770e8400-e29b-41d4-a716-446655440002",
          "name": "Wireless Headphones",
          "price": 150000.00,
          "imageUrl": "https://storage.example.com/products/headphones.jpg",
          "shopName": "TechStore TZ",
          "inStock": true
        }
      ],
      "shops": [],
      "events": [],
      "buyTogetherGroups": [],
      "installmentPlans": [],
      "externalLink": null
    },
    "privacySettings": {
      "visibility": "PUBLIC",
      "whoCanComment": "EVERYONE",
      "whoCanRepost": "EVERYONE",
      "hideLikesCount": false,
      "hideCommentsCount": false
    },
    "engagement": {
      "likesCount": 0,
      "commentsCount": 0,
      "repostsCount": 0,
      "quotesCount": 0,
      "bookmarksCount": 0,
      "sharesCount": 0,
      "viewsCount": 0,
      "canLike": true,
      "canComment": true,
      "canRepost": true,
      "canShare": true
    },
    "userInteraction": {
      "hasLiked": false,
      "hasBookmarked": false,
      "hasReposted": false,
      "myRepostId": null,
      "hasQuoted": false,
      "hasCommented": false,
      "hasViewed": false,
      "hasShared": false
    },
    "createdAt": "2025-12-29T10:00:00",
    "publishedAt": "2025-12-29T10:30:45",
    "scheduledAt": null
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| id | Unique identifier of the published post |
| author | Information about the post author |
| content | Text content of the post |
| postType | Content type: REGULAR, POLL |
| postKind | Sharing kind: ORIGINAL, REPOST, QUOTE (derived, not stored in DB) |
| status | Post status (now PUBLISHED) |
| media | Array of media attachments (images/videos) |
| attachments | Commerce and social attachments |
| privacySettings | Privacy and interaction settings |
| engagement | Engagement counters (all start at 0) |
| userInteraction | Current user's interaction status |
| publishedAt | Timestamp when post was published |

**Error Response JSON Samples**:

*Media Without Text Content:*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Media posts must include text content",
  "action_time": "2025-12-29T10:30:45",
  "data": null
}
```

*Empty Post:*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Post must have at least one of: text content, poll, quote, or attachments",
  "action_time": "2025-12-29T10:30:45",
  "data": null
}
```

*No Draft Found:*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "No draft found",
  "action_time": "2025-12-29T10:30:45",
  "data": null
}
```

*Pending Collaborator Approval:*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot publish collaborative post - waiting for collaborator acceptance",
  "action_time": "2025-12-29T10:30:45",
  "data": "2 of 3 collaborators have accepted"
}
```

*Invalid Attached Product:*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Invalid product attachment",
  "action_time": "2025-12-29T10:30:45",
  "data": "Product 550e8400-e29b-41d4-a716-446655440001 is no longer active"
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: No draft found, content validation failed, or media without text
- `401 UNAUTHORIZED`: Missing or invalid authentication token
- `403 FORBIDDEN`: Collaborative post pending approval from collaborators
- `404 NOT_FOUND`: No draft post found for user
- `422 UNPROCESSABLE_ENTITY`: Draft validation failed (invalid attachments, expired poll, etc.)

**Use Case Examples**:

*Example 1: Text Only Post*
```
Draft: "Hello world! This is my first post"
Result: ✅ Published successfully
```

*Example 2: Text + Media Post*
```
Draft: "Check out this sunset! 🌅" + [image.jpg]
Result: ✅ Published successfully
```

*Example 3: Media Only (No Text)*
```
Draft: [image.jpg] (no text content)
Result: ❌ Error - "Media posts must include text content"
```

*Example 4: Poll Only*
```
Draft: Poll "What's your favorite color?" with options [Red, Blue, Green]
Result: ✅ Published successfully (poll title serves as content)
```

*Example 5: Product Attachment Only*
```
Draft: Attached product "Wireless Headphones" (no text)
Result: ✅ Published successfully (attachment provides context)
```

*Example 6: Product + Media (No Text)*
```
Draft: Attached product + [product_photo.jpg] (no text)
Result: ❌ Error - "Media posts must include text content"
```

*Example 7: Quote Post Only*
```
Draft: Quoting another post (no additional text)
Result: ✅ Published successfully (quoted post provides context)
```

*Example 8: Empty Post*
```
Draft: No content, no media, no attachments
Result: ❌ Error - "Post must have at least one of: text content, poll, quote, or attachments"
```

## 3. Delete Post
**Purpose**: Soft delete a post (marks as deleted, doesn't remove from database)

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/{postId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Performs soft delete: Sets isDeleted flag to true, doesn't remove from database
- Removes post from all users' feeds immediately
- Preserves post data for analytics and audit purposes
- All comments, likes, bookmarks remain in database but are hidden
- Decrements engagement counters for attached products/shops/events
- Sends notification to collaborators (if collaborative post)
- Post no longer appears in searches, hashtag feeds, or explore pages
- Only post author can delete their own posts (not collaborators)
- **Cascade repost deletion**: If this is an original post, all pure reposts of it are automatically soft-deleted asynchronously in the background. Quote posts (reposts with added text) are NOT deleted — they remain but show a tombstone where the original was embedded.

**Business Rules**:
1. **Author Only**: Only the original post author can delete (collaborators cannot)
2. **Soft Delete**: Data retained in database with isDeleted = true
3. **Irreversible**: Deleted posts cannot be undeleted (would need backend intervention)
4. **Cascade Hiding**: Comments/likes/bookmarks hidden but not deleted
5. **Analytics Preservation**: Engagement data preserved for historical analytics
6. **Feed Removal**: Immediately removed from all feeds (home, explore, profile)
7. **Notification**: Collaborators notified of deletion
8. **Attachment Impact**: Products/shops/events lose post engagement stats
9. **Repost Cascade**: All pure reposts (no added text) are automatically soft-deleted asynchronously — response is instant, cascade runs in background
10. **Quote Posts Survive**: Quote posts (which have their own content) remain alive but show `quotedPost.unavailable = true` with message "This post is no longer available."
11. **Interactions on dead reposts**: Once the original is deleted, likes/comments/views on any repost of it return "Post not found" — interactions are blocked

**What Gets Hidden**:
- Post itself (no longer visible to anyone)
- All comments and replies on the post
- All likes, bookmarks, reposts
- Post no longer appears in:
    - User's profile
    - Followers' feeds
    - Hashtag pages
    - Search results
    - Product/shop/event pages (if attached)

**What Gets Preserved** (in database for analytics):
- Original post content and metadata
- All engagement metrics (historical counts)
- All comments and their content
- All likes, bookmarks, and reposts
- Author and timestamp information
- Attachment relationships

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to delete | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post deleted successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| data | Always null for this endpoint |

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `400 BAD_REQUEST`: Can only delete your own posts
- `401 UNAUTHORIZED`: Authentication issues
- `403 FORBIDDEN`: Not the post author (collaborators cannot delete)
- `404 NOT_FOUND`: Post not found or already deleted
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

**Error Response Examples**:

*Not Post Author (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot delete post",
  "action_time": "2025-12-11T10:30:45",
  "data": "Only the post author can delete this post. Collaborators cannot delete."
}
```

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-12-11T10:30:45",
  "data": "Post with ID 550e8400-e29b-41d4-a716-446655440000 not found or already deleted"
}
```

**Use Case Examples**:

*Example 1: Delete Regular Post*
```
DELETE /e-social/posts/550e8400-e29b-41d4-a716-446655440000

Post has: 50 likes, 10 comments, 5 reposts
Result: Post soft deleted, all engagement hidden, removed from feeds, data preserved in DB
```

*Example 2: Delete Post with Product Attachments*
```
DELETE /e-social/posts/660e8400-e29b-41d4-a716-446655440001

Post attached to 2 products
Result: Post deleted, product engagement counters decremented, post removed from product pages
```

*Example 3: Collaborator Tries to Delete (Fails)*
```
DELETE /e-social/posts/770e8400-e29b-41d4-a716-446655440002

User is collaborator, not author
Result: Error 403 - Only original author can delete collaborative posts
```

---

## 4. Get Post by ID
**Purpose**: Retrieve a single post by its ID with full details

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns full post details including author info, content, media, attachments, engagement
- Respects post visibility settings (PUBLIC, FOLLOWERS, MENTIONED, PRIVATE)
- Checks if requesting user has permission to view based on privacy settings
- Includes user interaction state (hasLiked, hasBookmarked, etc.)
- For poll posts, includes user's voting status and results if visible
- Increments view counter if this is first view by this user
- Returns parsed content with @mentions, $shops, and #hashtags highlighted
- Includes collaboration info if collaborative post

**Visibility Logic**:
- **PUBLIC**: Anyone can view
- **FOLLOWERS**: Only followers of author can view
- **MENTIONED**: Only users mentioned in post (@username) can view
- **PRIVATE**: Only author can view

**Additional Access Rules**:
- Blocked users cannot view each other's posts (even if PUBLIC)
- Deleted posts (isDeleted = true) return 404 NOT_FOUND
- Scheduled posts (status = SCHEDULED) only visible to author before scheduled time
- Draft posts (status = DRAFT) only visible to author
- Collaborative posts visible to all collaborators regardless of visibility setting

**Response Includes**:
- Full post content with parsed entities (mentions, hashtags, shops)
- Author information with profile picture and verification status
- All media with URLs, dimensions, placeholders (BlurHash)
- All commerce attachments (products, shops, events, groups, installments)
- Collaboration details (if collaborative post)
- Privacy settings
- Engagement metrics (likes, comments, reposts counts)
- User's interaction state (liked?, bookmarked?, reposted?, etc.)
- Poll results (if poll post and user can see results)
- Creation and publication timestamps

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to retrieve | Must be valid UUID format |

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Post retrieved successfully"

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `401 UNAUTHORIZED`: Authentication issues
- `403 FORBIDDEN`: User doesn't have permission to view this post (visibility restrictions)
- `404 NOT_FOUND`: Post not found, deleted, or not yet published (scheduled/draft)
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

**Error Response Examples**:

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-12-11T10:30:45",
  "data": "Post with ID 550e8400-e29b-41d4-a716-446655440000 not found"
}
```

*Insufficient Permissions - Followers Only (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot view this post",
  "action_time": "2025-12-11T10:30:45",
  "data": "This post is visible to followers only. Follow the author to view."
}
```

*Blocked User (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot view this post",
  "action_time": "2025-12-11T10:30:45",
  "data": "You cannot view posts from this user"
}
```

**Use Case Examples**:

*Example 1: View Public Post*
```
GET /e-social/posts/550e8400-e29b-41d4-a716-446655440000

Post visibility: PUBLIC
Result: Full post details returned, view counter incremented, engagement visible
```

*Example 2: View Followers-Only Post (Not Following)*
```
GET /e-social/posts/660e8400-e29b-41d4-a716-446655440001

Post visibility: FOLLOWERS
User is not following author
Result: Error 403 - Must follow author to view
```

*Example 3: View Mentioned Post (Not Mentioned)*
```
GET /e-social/posts/770e8400-e29b-41d4-a716-446655440002

Post visibility: MENTIONED
User not mentioned in post
Result: Error 403 - Only mentioned users can view
```

*Example 4: View Own Draft Post*
```
GET /e-social/posts/880e8400-e29b-41d4-a716-446655440003

Post status: DRAFT
User is post author
Result: Full post details returned with status = DRAFT
```

*Example 5: View Collaborative Post (As Collaborator)*
```
GET /e-social/posts/990e8400-e29b-41d4-a716-446655440004

Post visibility: PRIVATE
User is accepted collaborator
Result: Full post details returned (collaborators can always view)
```

---

## 5. Get Published Posts
**Purpose**: Retrieve all published posts with pagination (for discovery/explore feed)

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Number of items per page | Min: 1, Max: 100 | 20 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Posts retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": [
    {
      // PostResponse object
    },
    {
      // PostResponse object
    }
  ]
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| data | Array of PostResponse objects |

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `401 UNAUTHORIZED`: Authentication issues
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

---

## 6. Get Posts by Author
**Purpose**: Retrieve all posts from a specific author with pagination

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/author/{authorId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| authorId | string | Yes | UUID of the author | Must be valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Number of items per page | Min: 1, Max: 100 | 20 |

**Success Response**: Returns array of PostResponse objects

**Success Response Message**: "Posts retrieved successfully"

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `401 UNAUTHORIZED`: Authentication issues
- `404 NOT_FOUND`: Author not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

---

## 7. Get My Scheduled Posts
**Purpose**: Retrieve all scheduled posts for the authenticated user

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/scheduled`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Success Response**: Returns array of PostResponse objects with status SCHEDULED

**Success Response Message**: "Scheduled posts retrieved successfully"

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `401 UNAUTHORIZED`: Authentication issues
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

---

## 8. Get My Current Draft
**Purpose**: Retrieve the authenticated user's current draft post

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/draft`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Success Response**: Returns standard PostResponse structure with status DRAFT, or null if no draft exists

**Success Response Message**: "Draft retrieved successfully"

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `401 UNAUTHORIZED`: Authentication issues
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

---

## 9. Discard Draft
**Purpose**: Delete the current draft post permanently

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/draft`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Draft discarded successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `400 BAD_REQUEST`: No draft found or can only discard your own draft
- `401 UNAUTHORIZED`: Authentication issues
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

---

## 10. Update Draft
**Purpose**: Update content, media, or privacy settings of the current draft

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication (format: `Bearer <token>`) |
| Content-Type | string | Yes | Must be `application/json` |

**Request JSON Sample**:
```json
{
  "content": "Updated post content",
  "media": [
    {
      "mediaType": "IMAGE",
      "mediaUrl": "https://cdn.nexgate.com/uploads/new_image.jpg",
      "width": 1920,
      "height": 1080
    }
  ],
  "privacySettings": {
    "visibility": "FOLLOWERS",
    "whoCanComment": "FOLLOWERS"
  }
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| content | string | No | Updated post text content | Max: 5000 characters |
| media | array | No | Updated media array | Max: 10 items |
| privacySettings | object | No | Updated privacy settings | |

**Success Response**: Returns standard PostResponse structure with updated values

**Success Response Message**: "Draft updated successfully"

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `400 BAD_REQUEST`: No draft found, can only update your own posts, or only draft posts can be updated
- `401 UNAUTHORIZED`: Authentication issues
- `422 UNPROCESSABLE_ENTITY`: Validation errors
- `500 INTERNAL_SERVER_ERROR`: Unexpected server errors

---

## 11-20. Draft Commerce Attachments (Attach/Remove Products, Shops, Events, Groups, Plans)

For brevity, these endpoints follow the same pattern:

**Attach Endpoints**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/draft/attach-{type}/{id}`
- `/draft/attach-product/{productId}`
- `/draft/attach-shop/{shopId}`
- `/draft/attach-event/{eventId}`
- `/draft/attach-group/{groupId}`
- `/draft/attach-plan/{planId}`

**Remove Endpoints**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/draft/remove-{type}/{id}`
- `/draft/remove-product/{productId}`
- `/draft/remove-shop/{shopId}`
- `/draft/remove-event/{eventId}`
- `/draft/remove-group/{groupId}`
- `/draft/remove-plan/{planId}`

All return standard PostResponse structure. Success messages follow pattern: "{Type} attached/removed to/from draft successfully"

---

## 21. Update Draft Content Only
**Purpose**: Update only the text content of the current draft

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts/content`

**Request Body**: Plain text string (not JSON object)

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Draft content updated successfully"

---

## 22. Update Draft Media Only
**Purpose**: Update only the media attachments of the current draft

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts/media`

**Request Body**: Array of MediaRequest objects

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Media added to draft successfully"

---

## 23. Update Draft Privacy Settings Only
**Purpose**: Update only the privacy settings of the current draft

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts/privacy`

**Request Body**: PrivacySettingsRequest object

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Privacy settings updated successfully"

---

## 24. Update Draft Collaboration
**Purpose**: Update collaborator invitations for the current draft

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts/collaboration`

**Request Body**: CollaborationRequest object with collaboratorIds array

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Collaboration settings updated successfully"

---

## 25. Accept Collaboration
**Purpose**: Accept a collaboration invitation on a published post

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/collaboration/accept`

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Collaboration accepted successfully"

---

## 26. Decline Collaboration
**Purpose**: Decline a collaboration invitation on a published post

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/collaboration/decline`

**Success Response**: Returns standard PostResponse structure

**Success Response Message**: "Collaboration declined successfully"

---

## 27. Remove Collaborator
**Purpose**: Remove a collaborator from a published post (author or self only)

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/{postId}/collaborators/{collaboratorId}`

**Success Response Message**: "Collaborator removed successfully"

---

## 28. Create Repost or Quote Post

**Purpose**: Repost or quote an existing post. The same endpoint handles both — the presence or absence of `content` determines which one is created.

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/quote/{quotedPostId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

---

### Repost vs Quote — The Difference

| | Repost | Quote Post |
|---|---|---|
| `content` in body | empty / omitted | your text |
| `postKind` in response | `REPOST` | `QUOTE` |
| Counter on original | `repostsCount` + 1 | `quotesCount` + 1 |
| Has its own engagement | No (shows original's counts) | Yes (its own likes/comments) |
| Duplicate check | Can't repost same post twice | Can quote same post multiple times |
| Daily limit | Yes (configurable) | No separate limit |

---

### Repost (silent forward — no added text)

**Request**:
```
POST /e-social/posts/quote/aaa-111
Content-Type: application/json

{}
```
Body is empty — no `content` field means it's a pure repost.

**What happens**:
- A new post is created with `quotedPostId = aaa-111`, `content = null`
- Original post's `repostsCount` incremented by 1
- Response engagement and interaction data comes from the **original post**, not this new repost post

**Response** (`postKind: REPOST`):
```json
{
  "id": "bbb-222",
  "postKind": "REPOST",
  "author": { "userName": "mbappe" },
  "content": null,
  "quotedPost": {
    "id": "aaa-111",
    "author": { "userName": "ronaldo" },
    "content": "Siuuu! 🔥",
    "status": "PUBLISHED"
  },
  "engagement": {
    "likesCount": 9001,
    "repostsCount": 1,
    "quotesCount": 1
  },
  "userInteraction": {
    "hasLiked": false,
    "hasReposted": true,
    "myRepostId": "bbb-222"
  }
}
```

> **Note**: `engagement` and `userInteraction` on a repost always reflect the **original post's** data — not the repost itself. Likes, comments, views on the repost all flow through to the original.

---

### Quote Post (repost with your own added comment)

**Request**:
```
POST /e-social/posts/quote/aaa-111
Content-Type: application/json

{
  "content": "Respect to the GOAT 🐐"
}
```

**What happens**:
- A new post is created with `quotedPostId = aaa-111`, `content = "Respect to the GOAT 🐐"`
- Original post's `quotesCount` incremented by 1
- This new post has **its own independent engagement** — its own likes, comments, views

**Response** (`postKind: QUOTE`):
```json
{
  "id": "ccc-333",
  "postKind": "QUOTE",
  "author": { "userName": "messi" },
  "content": "Respect to the GOAT 🐐",
  "quotedPost": {
    "id": "aaa-111",
    "author": { "userName": "ronaldo" },
    "content": "Siuuu! 🔥",
    "status": "PUBLISHED"
  },
  "engagement": {
    "likesCount": 0,
    "repostsCount": 0,
    "quotesCount": 0
  },
  "userInteraction": {
    "hasLiked": false,
    "hasReposted": false,
    "myRepostId": null,
    "hasQuoted": true
  }
}
```

---

### Undo a Repost

There is no dedicated "unrepost" endpoint. To undo a repost, the client reads `userInteraction.myRepostId` from the original post's response, then calls:

```
DELETE /e-social/posts/{myRepostId}
```

This soft-deletes the repost and decrements the original's `repostsCount`.

**How the client knows the user has reposted:**

When fetching any post, `userInteraction` includes:
```json
"userInteraction": {
  "hasReposted": true,
  "myRepostId": "bbb-222"
}
```

If `hasReposted = false`, `myRepostId` is `null`.

---

### Reposting a Repost (Chain Resolution)

If you try to repost a post that is itself a pure repost, the system automatically resolves to the **original post** and creates the new repost pointing directly to it.

**Example**:
- Ronaldo posts `POST_A`
- Mbappe reposts → `POST_B` (points to `POST_A`)
- You repost `POST_B` → your new repost points to `POST_A`, **not** `POST_B`

This prevents empty chains and ensures:
- The display always shows the original content
- Counts always go to the original author
- Duplicate detection correctly blocks you if you already reposted `POST_A`

---

### When the Original Post is Deleted

- **Your repost**: automatically soft-deleted in the background when original is deleted. No action needed.
- **Your quote post**: stays alive, but `quotedPost.unavailable = true` with message `"This post is no longer available."`. The quote retains its own content and engagement.
- **Interactions on your repost** (after original deleted): like/comment/view calls return "Post not found" — all blocked.

---

**Business Rules**:
1. Original post must be PUBLISHED
2. Cannot repost the same post twice (duplicate check by author + quotedPostId)
3. Daily limit applies to pure reposts (no-content) — configurable server-side
4. Reposting a repost always resolves to the original (chain flattening)
5. Privacy: original post's `whoCanRepost` setting is enforced
6. Repost engagement (likes, comments, views) always routes to the original post
7. Quote post engagement is independent — stays on the quote post

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| quotedPostId | UUID | Yes | ID of the post to repost or quote |

**Request Body Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| content | string | No | Your added comment. Omit or leave empty for a pure repost. |
| privacySettings | object | No | Privacy for the new repost/quote post |

**Success Response**: Returns standard PostResponse with `quotedPost` populated and `postKind` set to `REPOST` or `QUOTE`.

**Success Response Message**: "Quote post created successfully"

**Standard Error Types**:
- `400 BAD_REQUEST`: Already reposted this post, daily repost limit reached, or original not published
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Original post's `whoCanRepost` setting blocks you
- `404 NOT_FOUND`: Original post not found

---

## Interaction Endpoints (29-38)

## 29. Like Post
**Purpose**: Add a like to a post

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/like`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Adds current user's like to the specified post
- Increments post's likesCount by 1
- Creates like record with timestamp
- Sends notification to post author (unless liking own post)
- Updates user's userInteraction.hasLiked to true
- Idempotent: If already liked, returns success without changes
- Respects post visibility (must be able to view post to like it)
- For collaborative posts, all collaborators receive notification

**Business Rules**:
1. **Visibility Check**: User must have permission to view post based on privacy settings
2. **Blocking**: Cannot like posts from blocked users or if author blocked you
3. **Deleted Posts**: Cannot like deleted posts (isDeleted = true)
4. **Draft/Scheduled**: Cannot like unpublished posts (must be PUBLISHED status)
5. **Idempotent**: Multiple like attempts don't create duplicate likes
6. **Notification**: Author receives notification (unless self-like)
7. **Counter Update**: Post's engagement counter incremented atomically
8. **User State**: User's interaction state updated for this post

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to like | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post liked successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot like this post (blocked, visibility, etc.)
- `404 NOT_FOUND`: Post not found or deleted
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Cannot Like - Blocked User (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot like this post",
  "action_time": "2025-12-11T10:30:45",
  "data": "You cannot interact with posts from this user"
}
```

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-12-11T10:30:45",
  "data": "Post with ID 550e8400-e29b-41d4-a716-446655440000 not found"
}
```

---

## 30. Unlike Post
**Purpose**: Remove a like from a post

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/{postId}/like`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Removes current user's like from the specified post
- Decrements post's likesCount by 1
- Deletes like record from database
- No notification sent (unlike is silent)
- Updates user's userInteraction.hasLiked to false
- Idempotent: If not liked, returns success without changes
- Can unlike even if post is now deleted or user is blocked

**Business Rules**:
1. **Silent Operation**: No notification sent to post author
2. **Always Allowed**: Can unlike even if blocked or post deleted (to clean up state)
3. **Idempotent**: Multiple unlike attempts don't cause errors
4. **Counter Decrement**: Post's engagement counter decremented atomically
5. **State Cleanup**: User's interaction state updated

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to unlike | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post unliked successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `404 NOT_FOUND`: Post not found (permanent deletion from DB)
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 31. Bookmark Post
**Purpose**: Save a post to bookmarks for later viewing

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/bookmark`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Adds post to user's bookmark collection
- Creates bookmark record with timestamp
- Post appears in user's bookmarks list (GET /posts/bookmarks)
- Increments post's bookmarksCount by 1
- No notification sent (bookmarking is private)
- Updates user's userInteraction.hasBookmarked to true
- Idempotent: If already bookmarked, returns success without changes
- Respects visibility (must be able to view post to bookmark it)

**Business Rules**:
1. **Private Action**: Bookmarks are private, author doesn't get notified
2. **Visibility Required**: Must have permission to view post based on privacy settings
3. **Persistent**: Bookmarks persist even if post is deleted (for user's reference)
4. **Organization**: Can be organized into collections (future feature)
5. **No Limit**: Unlimited bookmarks allowed per user
6. **Idempotent**: Multiple bookmark attempts don't create duplicates
7. **Counter Update**: Post's engagement counter incremented
8. **Access Anytime**: User can always access their bookmarked posts

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to bookmark | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post bookmarked successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot bookmark this post (visibility restrictions)
- `404 NOT_FOUND`: Post not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Use Case Examples**:

*Example 1: Bookmark for Later Reading*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/bookmark

User sees interesting post with products attached
Bookmarks to check products later
Result: Post saved to bookmarks, appears in GET /posts/bookmarks
```

*Example 2: Bookmark Collaborative Post*
```
POST /e-social/posts/660e8400-e29b-41d4-a716-446655440001/bookmark

Post has 3 collaborators
User bookmarks for reference
Result: Post bookmarked, visible in bookmarks even if one collaborator leaves
```

---

## 32. Unbookmark Post
**Purpose**: Remove a post from bookmarks

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/{postId}/bookmark`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Removes post from user's bookmark collection
- Deletes bookmark record from database
- Post no longer appears in user's bookmarks list
- Decrements post's bookmarksCount by 1
- No notification sent (unbookmarking is private)
- Updates user's userInteraction.hasBookmarked to false
- Idempotent: If not bookmarked, returns success without changes

**Business Rules**:
1. **Silent Operation**: No notification sent
2. **Always Allowed**: Can unbookmark anytime, even if post deleted
3. **Idempotent**: Multiple unbookmark attempts don't cause errors
4. **Counter Decrement**: Post's engagement counter decremented

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to unbookmark | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post unbookmarked successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `404 NOT_FOUND`: Post not found (permanent deletion)
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

**Use Case Examples**:

*Example 1: Simple Repost (No Comment)*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/repost

Body: {} or no body

Result: Original post appears on user's profile with "reposted" badge
Appears in followers' feeds
Original author gets notification
```

*Example 2: Quote Post (With Comment)*
```
POST /e-social/posts/660e8400-e29b-41d4-a716-446655440001/repost

Body: {
  "comment": "This is exactly what I've been saying! Great insight 🎯"
}

Result: Creates new quote post with comment
Original post embedded in quote
New post has own likes/comments
Both posts' repost counters incremented
```

---

## 34. Undo Repost (Unrepost)

**Purpose**: Remove your repost. There is no dedicated unrepost endpoint — unreposting is done by deleting the repost post itself using `myRepostId` from the post's `userInteraction`.

**How to undo a repost**:

1. Fetch the original post. The response contains:
```json
"userInteraction": {
  "hasReposted": true,
  "myRepostId": "bbb-222"
}
```

2. Call the standard delete endpoint with the repost's ID:

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/{myRepostId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**What happens**:
- The repost post (`bbb-222`) is soft-deleted
- Original post's `repostsCount` decremented by 1
- `userInteraction.hasReposted` returns `false`, `myRepostId` returns `null` on next fetch
- No notification sent (silent)

**Business Rules**:
1. Only the repost author can delete their own repost
2. To remove a quote post (which has content), use the same `DELETE /posts/{quotePostId}` — this decrements `quotesCount` instead
3. If original post is already deleted, the repost will have been auto-deleted by the cascade — nothing to undo

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post deleted successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Not your repost
- `401 UNAUTHORIZED`: Authentication required
- `404 NOT_FOUND`: Repost not found or already deleted

---

## 35. Record View
**Purpose**: Track post views for analytics

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/view`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Records that current user viewed the post
- Increments post's viewsCount by 1 (first view only)
- Creates view record with timestamp
- Idempotent: Multiple views from same user only count once
- Updates user's userInteraction.hasViewed to true
- No notification sent (views are private analytics)
- View counts visible to post author in analytics dashboard
- Automatic: Usually called by frontend when post enters viewport

**Business Rules**:
1. **One View Per User**: Each user counted only once regardless of repeat views
2. **Private Metric**: View counts not shown publicly (author sees in analytics)
3. **No Notification**: Viewing doesn't notify author
4. **Automatic Tracking**: Frontend typically calls this when post visible for 2+ seconds
5. **Respects Visibility**: Must have permission to view post
6. **Author Excluded**: Author's own views don't count
7. **Idempotent**: Multiple calls from same user don't inflate count

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post being viewed | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "View recorded successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot view this post (visibility restrictions)
- `404 NOT_FOUND`: Post not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 36. Get My Bookmarks
**Purpose**: Retrieve all posts bookmarked by current user

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/bookmarks`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns all posts bookmarked by current user
- Sorted by bookmark date (newest first)
- Includes posts even if deleted or author blocked (user retains access)
- Paginated results for performance
- Each post includes full PostResponse structure
- Shows deleted posts with special indicator (for user reference)
- Includes posts from private/followers-only accounts if bookmarked when accessible

**Business Rules**:
1. **Personal Collection**: Only user's own bookmarks visible
2. **Persists Deletions**: Bookmarked posts remain accessible even if deleted
3. **Persists Blocking**: Can view bookmarked posts even if author blocked
4. **Chronological**: Sorted by bookmark time (newest first)
5. **Full Details**: Each post includes complete information
6. **Pagination**: Default 20 per page, max 100

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1, Max: 100 | 20 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Bookmarks retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": [
    {
      // Full PostResponse object
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "content": "Bookmarked post content...",
      // ... rest of PostResponse
    },
    {
      // Another PostResponse object
    }
  ]
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 37. Get My Reposts
**Purpose**: Retrieve all posts reposted by current user

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/my-reposts`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns all posts reposted by current user (simple reposts only)
- Does NOT include quote posts (those are separate posts in user's profile)
- Sorted by repost date (newest first)
- Paginated results for performance
- Each post shows original author and content
- Respects current visibility settings of original posts

**Business Rules**:
1. **Simple Reposts Only**: Only shows direct reposts, not quote posts
2. **Quote Posts**: Appear in regular "Get User Posts" endpoint as authored posts
3. **Current Visibility**: If original post now private/deleted, it's excluded
4. **Chronological**: Sorted by repost time (newest first)
5. **Full Details**: Each post includes complete original information

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1, Max: 100 | 20 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Reposts retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": [
    {
      // Full PostResponse object of reposted post
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "author": {
        // Original author info
      },
      "content": "Original post content...",
      // ... rest of PostResponse
    }
  ]
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 38. Get User Reposts
**Purpose**: Retrieve all posts reposted by a specific user

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/users/{userId}/reposts`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns all posts reposted by specified user (simple reposts only)
- Does NOT include quote posts (those are in user's authored posts)
- Respects privacy settings of original posts
- Only shows reposts visible to requesting user based on:
    - Original post visibility settings
    - Blocking status
    - Follow relationships
- Sorted by repost date (newest first)
- Paginated results for performance

**Business Rules**:
1. **Simple Reposts Only**: Only shows direct reposts, not quote posts
2. **Visibility Filtering**: Only shows original posts requesting user can see
3. **Privacy Respected**: Follows same visibility rules as original posts
4. **Blocking**: Hidden if original author blocked requesting user
5. **Chronological**: Sorted by repost time (newest first)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| userId | string | Yes | UUID of user whose reposts to retrieve | Must be valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1, Max: 100 | 20 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "User reposts retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": [
    {
      // Full PostResponse object of reposted post
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "author": {
        // Original author info
      },
      "content": "Original post content...",
      // ... rest of PostResponse
    }
  ]
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `404 NOT_FOUND`: User not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

**Summary**: All interaction endpoints return `data: null` except GET endpoints (36-38) which return paginated PostResponse arrays.

---

# Poll Endpoints (39-42)

## 39. Vote on Poll
**Purpose**: Cast a vote on a poll post

**Endpoint**: `POST {base_url}/e-social/posts/{postId}/vote`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Records user's vote on poll options
- Supports single or multiple option selection based on poll settings
- If allowVoteChange is true, replaces existing vote
- If allowVoteChange is false, rejects vote if user already voted
- Increments vote counts for selected options
- Updates poll's totalVotes count
- If non-anonymous, records voter identity
- If anonymous, records vote without identity
- Validates poll hasn't expired before accepting vote
- Respects post visibility (must be able to view post to vote)

**Business Rules**:
1. **Single vs Multiple**: Respects poll's allowMultipleVotes setting
2. **Vote Changes**: Allowed only if allowVoteChange is true
3. **Expiration**: Cannot vote on expired polls
4. **Visibility**: Must have access to view post
5. **Blocking**: Cannot vote on polls from blocked users
6. **Option Validation**: All optionIds must belong to this poll
7. **Single Vote Mode**: If allowMultipleVotes false, only 1 optionId allowed
8. **Multiple Vote Mode**: If allowMultipleVotes true, can select multiple options
9. **Anonymous**: If isAnonymous true, voter identity hidden
10. **Non-Anonymous**: If isAnonymous false, voter identity visible to post author

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the poll post | Must be valid UUID format |

**Request JSON Sample** (Single Vote):
```json
{
  "optionIds": ["option-uuid-1"]
}
```

**Request JSON Sample** (Multiple Votes):
```json
{
  "optionIds": ["option-uuid-1", "option-uuid-2", "option-uuid-4"]
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| optionIds | array | Yes | Array of poll option UUIDs | Min: 1 option. If allowMultipleVotes false, max: 1. All IDs must belong to poll |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Vote recorded successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Poll expired, vote change not allowed, or invalid options
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot vote (blocked, visibility restrictions)
- `404 NOT_FOUND`: Post or poll not found
- `422 UNPROCESSABLE_ENTITY`: Validation errors (too many options, invalid IDs)
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Poll Expired (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot vote on poll",
  "action_time": "2025-12-11T10:30:45",
  "data": "This poll has expired and is no longer accepting votes"
}
```

*Vote Change Not Allowed (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot change vote",
  "action_time": "2025-12-11T10:30:45",
  "data": "You have already voted on this poll and vote changes are not allowed"
}
```

*Too Many Options - Single Vote Mode (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    "optionIds": "This poll allows only one vote. You selected 3 options."
  }
}
```

*Invalid Option ID (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Invalid poll option",
  "action_time": "2025-12-11T10:30:45",
  "data": "Option option-uuid-5 does not belong to this poll"
}
```

**Use Case Examples**:

*Example 1: Single Vote Poll*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/vote

Poll settings: allowMultipleVotes = false

Body: {
  "optionIds": ["option-uuid-2"]
}

Result: Vote recorded for option 2, totalVotes++, option vote count++
```

*Example 2: Multiple Vote Poll*
```
POST /e-social/posts/660e8400-e29b-41d4-a716-446655440001/vote

Poll settings: allowMultipleVotes = true

Body: {
  "optionIds": ["option-uuid-1", "option-uuid-3", "option-uuid-5"]
}

Result: Votes recorded for 3 options, totalVotes++, each option count++
```

*Example 3: Change Vote (Allowed)*
```
POST /e-social/posts/770e8400-e29b-41d4-a716-446655440002/vote

Poll settings: allowVoteChange = true
User already voted for option 1

Body: {
  "optionIds": ["option-uuid-4"]
}

Result: Previous vote removed, new vote recorded, counts updated
```

*Example 4: Change Vote (Not Allowed)*
```
POST /e-social/posts/880e8400-e29b-41d4-a716-446655440003/vote

Poll settings: allowVoteChange = false
User already voted

Body: {
  "optionIds": ["option-uuid-2"]
}

Result: Error 400 - Vote changes not allowed, original vote preserved
```

---

## 40. Remove Vote
**Purpose**: Remove your vote from a poll

**Endpoint**: `DELETE {base_url}/e-social/posts/{postId}/vote`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Removes all of user's votes from the poll
- Decrements vote counts for previously selected options
- Decrements poll's totalVotes count
- Only allowed if allowVoteChange is true
- Idempotent: If not voted, returns success without changes
- Can remove votes even if poll expired (cleanup)

**Business Rules**:
1. **Vote Change Required**: Only works if allowVoteChange is true
2. **Complete Removal**: Removes ALL votes (if multiple vote poll)
3. **Idempotent**: Safe to call if not voted
4. **Expired Polls**: Can remove votes even after expiration
5. **Counter Updates**: All affected option counts decremented
6. **Silent Operation**: No notification sent

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the poll post | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Vote removed successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Vote removal not allowed (allowVoteChange is false)
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot access poll
- `404 NOT_FOUND`: Post or poll not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Vote Removal Not Allowed (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot remove vote",
  "action_time": "2025-12-11T10:30:45",
  "data": "This poll does not allow vote changes or removal"
}
```

**Use Case Examples**:

*Example 1: Remove Single Vote*
```
DELETE /e-social/posts/550e8400-e29b-41d4-a716-446655440000/vote

Poll settings: allowVoteChange = true
User voted for option 2

Result: Vote removed, option 2 count--, totalVotes--
```

*Example 2: Remove Multiple Votes*
```
DELETE /e-social/posts/660e8400-e29b-41d4-a716-446655440001/vote

Poll settings: allowMultipleVotes = true, allowVoteChange = true
User voted for options 1, 3, 5

Result: All 3 votes removed, all option counts--, totalVotes--
```

*Example 3: Remove Vote - Not Allowed*
```
DELETE /e-social/posts/770e8400-e29b-41d4-a716-446655440002/vote

Poll settings: allowVoteChange = false

Result: Error 400 - Vote removal not allowed, original vote preserved
```

---

## 41. Get Poll Results
**Purpose**: Get detailed poll results including vote counts and percentages

**Endpoint**: `GET {base_url}/e-social/posts/{postId}/poll-results`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns complete poll details and voting statistics
- Calculates percentages for each option
- Shows user's voting status (hasVoted, which options voted)
- Displays poll configuration (anonymous, multiple votes, change allowed)
- Shows expiration status
- Respects post visibility (must be able to view post)
- Real-time results update as votes come in

**Business Rules**:
1. **Visibility**: Must have access to view parent post
2. **Real-Time**: Results update immediately after each vote
3. **Percentages**: Calculated as (optionVotes / totalVotes) × 100
4. **User State**: Shows which options current user voted for
5. **Expiration Check**: Indicates if poll has expired
6. **Zero Votes**: Shows 0.0% for options with no votes
7. **Anonymous Respect**: Voter identities hidden if isAnonymous true
8. **Public Results**: Anyone who can view post can see results

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the poll post | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Poll results retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    "pollId": "poll-uuid",
    "title": "What's your favorite programming language?",
    "description": "Choose your top pick for backend development",
    "totalVotes": 150,
    "allowMultipleVotes": false,
    "isAnonymous": true,
    "allowVoteChange": true,
    "expiresAt": "2025-12-15T10:30:45",
    "hasExpired": false,
    "userHasVoted": true,
    "options": [
      {
        "optionId": "option-uuid-1",
        "optionText": "Python",
        "optionImageUrl": null,
        "optionOrder": 1,
        "votesCount": 75,
        "percentage": 50.0,
        "userVoted": true
      },
      {
        "optionId": "option-uuid-2",
        "optionText": "JavaScript",
        "optionImageUrl": null,
        "optionOrder": 2,
        "votesCount": 50,
        "percentage": 33.3,
        "userVoted": false
      },
      {
        "optionId": "option-uuid-3",
        "optionText": "Java",
        "optionImageUrl": null,
        "optionOrder": 3,
        "votesCount": 25,
        "percentage": 16.7,
        "userVoted": false
      }
    ]
  }
}
```

**Response Fields Explained**:
| Field | Type | Description |
|-------|------|-------------|
| pollId | string | UUID of the poll |
| title | string | Poll question/title |
| description | string | Additional poll description (optional) |
| totalVotes | integer | Total number of votes cast |
| allowMultipleVotes | boolean | Can users select multiple options |
| isAnonymous | boolean | Are voter identities hidden |
| allowVoteChange | boolean | Can users change their vote |
| expiresAt | string | Poll expiration datetime (ISO 8601, null if no expiration) |
| hasExpired | boolean | Whether poll has passed expiration time |
| userHasVoted | boolean | Has current user voted on this poll |
| options | array | Array of poll options with results |
| options[].optionId | string | UUID of this option |
| options[].optionText | string | Text for this option |
| options[].optionImageUrl | string | Optional image for this option (null if none) |
| options[].optionOrder | integer | Display order (1, 2, 3...) |
| options[].votesCount | integer | Number of votes for this option |
| options[].percentage | number | Percentage of total votes (0.0–100.0) |
| options[].userVoted | boolean | Did current user vote for this option |

**Success Response Message**: `"Poll results retrieved successfully"`

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot view poll (post visibility restrictions)
- `404 NOT_FOUND`: Post not found or not a poll post
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Use Case Examples**:

*Example 1: View Active Poll Results*
```
GET /e-social/posts/550e8400-e29b-41d4-a716-446655440000/poll-results

Poll has 150 total votes, user voted for Python
Result: Returns all options with vote counts, percentages, user's vote highlighted
```

*Example 2: View Expired Poll*
```
GET /e-social/posts/660e8400-e29b-41d4-a716-446655440001/poll-results

Poll expired yesterday, hasExpired = true
Result: Results displayed, voting disabled, final counts shown
```

*Example 3: View Poll Without Voting*
```
GET /e-social/posts/770e8400-e29b-41d4-a716-446655440002/poll-results

User hasn't voted yet, userHasVoted = false
Result: All options shown with current results, all userVoted = false
```

---

## 42. Get Poll Voters (All Options)
**Purpose**: Get a paginated list of all unique users who voted on a poll, across all options

**Endpoint**: `GET {base_url}/e-social/posts/{postId}/poll-voters`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns a deduplicated, paginated list of users who voted on this poll
- Only available for non-anonymous polls (`isAnonymous = false`)
- Accessible by **any authenticated user** (no author/collaborator restriction)
- Requires secondary onboarding completion (username + profile picture)
- Each voter entry includes follow relationship context (`youFollow`, `followsYou`)
- Deduplicates voters who voted on multiple options — one entry per unique voter
- Default page size is 5

**Business Rules**:
1. **Non-Anonymous Only**: Returns error if `isAnonymous = true`
2. **Any Authenticated User**: No author/collaborator restriction
3. **Secondary Onboarding Required**: Caller must have username + profilePic set
4. **Deduplication**: Users who voted on multiple options appear only once
5. **Follow Status**: Response reflects the calling user's follow relationship with each voter
6. **Pagination**: Default 5 voters per page, configurable via query params

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the poll post | Must be valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1 | 5 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Poll voters retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    "voters": [
      {
        "userId": "123e4567-e89b-12d3-a456-426614174000",
        "displayName": "John Doe",
        "userName": "johndoe",
        "profileImageUrl": "https://cdn.nexgate.com/profiles/johndoe.jpg",
        "youFollow": true,
        "followsYou": false
      },
      {
        "userId": "987e6543-e21b-12d3-a456-426614174999",
        "displayName": "Jane Smith",
        "userName": "janesmith",
        "profileImageUrl": "https://cdn.nexgate.com/profiles/janesmith.jpg",
        "youFollow": false,
        "followsYou": true
      }
    ],
    "totalElements": 42,
    "totalPages": 9,
    "hasNext": true,
    "hasPrevious": false,
    "isFirst": true,
    "isLast": false,
    "currentPage": 1
  }
}
```

**Response Fields Explained**:
| Field | Type | Description |
|-------|------|-------------|
| voters | array | Paginated list of unique voters across all poll options |
| voters[].userId | string | UUID of the voter |
| voters[].displayName | string | Voter's full display name |
| voters[].userName | string | Voter's unique username |
| voters[].profileImageUrl | string | URL to voter's profile picture |
| voters[].youFollow | boolean | Whether the calling user follows this voter |
| voters[].followsYou | boolean | Whether this voter follows the calling user |
| totalElements | integer | Total number of unique voters across all pages |
| totalPages | integer | Total number of pages |
| hasNext | boolean | Whether a next page exists |
| hasPrevious | boolean | Whether a previous page exists |
| isFirst | boolean | Whether this is the first page |
| isLast | boolean | Whether this is the last page |
| currentPage | integer | Current page number (1-indexed) |

**Success Response Message**: `"Poll voters retrieved successfully"`

**Standard Error Types**:
- `400 BAD_REQUEST`: Poll is anonymous — voter list not available
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Secondary onboarding not complete
- `404 NOT_FOUND`: Post not found or not a poll post
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Anonymous Poll (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Voters not available",
  "action_time": "2025-12-11T10:30:45",
  "data": "This is an anonymous poll. Voter identities are hidden."
}
```

*Secondary Onboarding Incomplete (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Profile incomplete",
  "action_time": "2025-12-11T10:30:45",
  "data": "Please complete your profile setup (username and profile picture) to access this feature."
}
```

**Use Case Examples**:

*Example 1: View All Voters — First Page*
```
GET /e-social/posts/550e8400-e29b-41d4-a716-446655440000/poll-voters

Poll is non-anonymous, 42 unique voters across all options
Result: Returns first 5 voters with follow relationship context
```

*Example 2: Custom Page Size*
```
GET /e-social/posts/550e8400-e29b-41d4-a716-446655440000/poll-voters?page=2&size=10

Result: Returns voters 11–20
```

*Example 3: Anonymous Poll*
```
GET /e-social/posts/770e8400-e29b-41d4-a716-446655440002/poll-voters

Poll has isAnonymous = true
Result: Error 400 — Voter identities hidden for anonymous polls
```

*Example 4: User Voted on Multiple Options (Deduplication)*
```
GET /e-social/posts/880e8400-e29b-41d4-a716-446655440003/poll-voters

User voted on 3 options in a multiple-vote poll
Result: User appears only once in the deduplicated voter list
```

---

## 43. Get Poll Voters by Option
**Purpose**: Get list of users who voted for a specific poll option (post author/collaborators only, non-anonymous polls only)

**Endpoint**: `GET {base_url}/e-social/posts/{postId}/poll-voters/{optionId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication + Post Author/Collaborator)

**Authentication**: Bearer Token

**Behavior**:
- Returns list of users who voted for a specific poll option
- Only available for non-anonymous polls (`isAnonymous = false`)
- Only accessible by post author and accepted collaborators
- Sorted by vote time (newest first)
- Includes user profile information
- Paginated for large polls

**Business Rules**:
1. **Non-Anonymous Only**: Only works if `isAnonymous = false`
2. **Author/Collaborator Access**: Only post author and collaborators can view
3. **Privacy Protection**: Regular users cannot see who voted per option
4. **Vote Time**: Includes when each user voted
5. **Profile Info**: Returns basic user profile (name, photo, verified status)
6. **Pagination**: Default 50 voters per page
7. **Current Votes**: Shows current votes (if user changed vote, shows new choice)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the poll post | Must be valid UUID format |
| optionId | string | Yes | UUID of the poll option | Must belong to this poll |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1, Max: 100 | 50 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Voters retrieved successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": {
    "pollId": "poll-uuid",
    "pollTitle": "What's your favorite programming language?",
    "optionId": "option-uuid-1",
    "optionText": "Python",
    "totalVoters": 75,
    "voters": [
      {
        "userId": "123e4567-e89b-12d3-a456-426614174000",
        "userName": "john_doe",
        "firstName": "John",
        "lastName": "Doe",
        "profilePictureUrl": "https://cdn.nexgate.com/profiles/john_doe.jpg",
        "isVerified": true,
        "votedAt": "2025-12-11T10:25:30"
      },
      {
        "userId": "987e6543-e21b-12d3-a456-426614174999",
        "userName": "jane_smith",
        "firstName": "Jane",
        "lastName": "Smith",
        "profilePictureUrl": "https://cdn.nexgate.com/profiles/jane_smith.jpg",
        "isVerified": false,
        "votedAt": "2025-12-11T10:20:15"
      }
    ],
    "pagination": {
      "currentPage": 1,
      "pageSize": 50,
      "totalPages": 2,
      "totalElements": 75,
      "hasNext": true,
      "hasPrevious": false
    }
  }
}
```

**Response Fields Explained**:
| Field | Type | Description |
|-------|------|-------------|
| pollId | string | UUID of the poll |
| pollTitle | string | Poll question/title |
| optionId | string | UUID of the queried option |
| optionText | string | Text of the queried option |
| totalVoters | integer | Total voters for this specific option |
| voters | array | Paginated list of users who voted for this option |
| voters[].userId | string | UUID of the voter |
| voters[].userName | string | Voter's unique username |
| voters[].firstName | string | Voter's first name |
| voters[].lastName | string | Voter's last name |
| voters[].profilePictureUrl | string | URL to voter's profile picture |
| voters[].isVerified | boolean | Whether voter's account is verified |
| voters[].votedAt | string | ISO 8601 datetime when this user voted |
| pagination.currentPage | integer | Current page number (1-indexed) |
| pagination.pageSize | integer | Number of items per page |
| pagination.totalPages | integer | Total number of pages |
| pagination.totalElements | integer | Total voters for this option |
| pagination.hasNext | boolean | Whether a next page exists |
| pagination.hasPrevious | boolean | Whether a previous page exists |

**Success Response Message**: `"Voters retrieved successfully"`

**Standard Error Types**:
- `400 BAD_REQUEST`: Anonymous poll — voter list not available
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Not post author/collaborator
- `404 NOT_FOUND`: Post or option not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Anonymous Poll (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Voters not available",
  "action_time": "2025-12-11T10:30:45",
  "data": "This is an anonymous poll. Voter identities are hidden."
}
```

*Not Post Author or Collaborator (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Access denied",
  "action_time": "2025-12-11T10:30:45",
  "data": "Only the post author and collaborators can view voter lists"
}
```

**Use Case Examples**:

*Example 1: View Voters for a Specific Option as Post Author*
```
GET /e-social/posts/550e8400-e29b-41d4-a716-446655440000/poll-voters/option-uuid-1

User is post author
Poll is non-anonymous, option has 75 voters

Result: Returns paginated list of users who voted for this specific option
```

*Example 2: View Voters as Collaborator*
```
GET /e-social/posts/660e8400-e29b-41d4-a716-446655440001/poll-voters/option-uuid-2

User is accepted collaborator
Poll is non-anonymous

Result: Returns voter list (collaborators have same access as author)
```

*Example 3: Anonymous Poll — Access Denied*
```
GET /e-social/posts/770e8400-e29b-41d4-a716-446655440002/poll-voters/option-uuid-3

User is post author
Poll is anonymous (isAnonymous = true)

Result: Error 400 — Voter identities hidden for anonymous polls
```

*Example 4: Regular User — Access Denied*
```
GET /e-social/posts/880e8400-e29b-41d4-a716-446655440003/poll-voters/option-uuid-1

User is neither author nor collaborator
Poll is non-anonymous

Result: Error 403 — Only author/collaborators can view voters per option
```

---

## Comment Endpoints (43-52)

## 43. Create Comment
**Purpose**: Create a comment or reply on a post

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/comments`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Creates top-level comment if parentCommentId is null
- Creates nested reply if parentCommentId provided
- Increments post's commentsCount by 1
- Sends notification to post author (unless commenting on own post)
- For replies, notifies parent comment author
- Automatically parses @mentions in comment content
- All mentioned users receive notifications
- Respects post's whoCanComment privacy setting
- Supports nested replies (unlimited depth)
- Updates post's engagement metrics

**Business Rules**:
1. **Permission Check**: Post must allow comments (whoCanComment setting)
2. **Visibility Check**: Must have permission to view post to comment
3. **Blocking**: Cannot comment on posts from blocked users
4. **Nesting**: Supports unlimited reply depth (replies to replies to replies...)
5. **Mentions**: All @mentioned users notified
6. **Notification**: Post author and parent comment author notified
7. **Content Length**: Min 1, Max 2000 characters
8. **Spam Prevention**: Rate limited (max 10 comments per minute per user)
9. **Edit Window**: Can edit within 15 minutes of posting
10. **Deleted Posts**: Cannot comment on deleted posts

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post to comment on | Must be valid UUID format |

**Request JSON Sample** (Top-level Comment):
```json
{
  "content": "Great post! @john_doe you should check this out",
  "parentCommentId": null
}
```

**Request JSON Sample** (Nested Reply):
```json
{
  "content": "I completely agree with you @jane_doe! 💯",
  "parentCommentId": "660e8400-e29b-41d4-a716-446655440001"
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| content | string | Yes | Comment text | Min: 1, Max: 2000 characters |
| parentCommentId | string | No | UUID of parent comment for replies | Must be valid UUID if provided, must belong to same post |

**Success Response**: Returns CommentResponse object

**CommentResponse Structure**:
```json
{
  "id": "770e8400-e29b-41d4-a716-446655440001",
  "postId": "550e8400-e29b-41d4-a716-446655440000",
  "author": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "userName": "john_doe",
    "firstName": "John",
    "lastName": "Doe",
    "profilePictureUrl": "https://cdn.nexgate.com/profiles/john_doe.jpg",
    "isVerified": true
  },
  "content": "Great post! @john_doe you should check this out",
  "contentParsed": {
    "text": "Great post! @john_doe you should check this out",
    "entities": [
      {
        "type": "MENTION",
        "text": "@john_doe",
        "startIndex": 12,
        "endIndex": 21,
        "user": {
          "id": "987e6543-e21b-12d3-a456-426614174999",
          "userName": "john_doe"
        }
      }
    ]
  },
  "parentCommentId": null,
  "replyCount": 5,
  "likesCount": 12,
  "isPinned": false,
  "isEdited": false,
  "userInteraction": {
    "hasLiked": false
  },
  "createdAt": "2025-12-11T10:30:45",
  "updatedAt": null
}
```

**Success Response Message**: "Comment created successfully"

**Standard Error Types**:
- `400 BAD_REQUEST`: Invalid content, parent comment not found, or spam detected
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot comment (permissions, blocked, disabled comments)
- `404 NOT_FOUND`: Post not found
- `422 UNPROCESSABLE_ENTITY`: Validation errors (content too long/short)
- `429 TOO_MANY_REQUESTS`: Rate limit exceeded
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Comments Disabled (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot comment on this post",
  "action_time": "2025-12-11T10:30:45",
  "data": "The author has disabled comments for this post"
}
```

*Comments Followers Only (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot comment on this post",
  "action_time": "2025-12-11T10:30:45",
  "data": "Only followers can comment on this post. Follow the author to comment."
}
```

*Invalid Parent Comment (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid parent comment",
  "action_time": "2025-12-11T10:30:45",
  "data": "Parent comment does not belong to this post"
}
```

*Rate Limit Exceeded (429):*
```json
{
  "success": false,
  "httpStatus": "TOO_MANY_REQUESTS",
  "message": "Too many comments",
  "action_time": "2025-12-11T10:30:45",
  "data": "Maximum 10 comments per minute. Please wait 30 seconds."
}
```

**Use Case Examples**:

*Example 1: Top-Level Comment*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/comments

Body: {
  "content": "This is exactly what I needed! Thank you 🙏",
  "parentCommentId": null
}

Result: Top-level comment created, post author notified, commentsCount++
```

*Example 2: Reply to Comment*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/comments

Body: {
  "content": "Glad you found it helpful! @jane_doe",
  "parentCommentId": "660e8400-e29b-41d4-a716-446655440001"
}

Result: Nested reply created, parent comment author + @jane_doe notified
```

*Example 3: Nested Reply (Reply to Reply)*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/comments

Body: {
  "content": "Same here! Following this thread 👀",
  "parentCommentId": "770e8400-e29b-41d4-a716-446655440002"
}

Result: 2nd-level nested reply created, forms conversation thread
```

---

## 44. Get Comments
**Purpose**: Retrieve all top-level comments for a post (paginated)

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/comments`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns top-level comments only (parentCommentId = null)
- Sorted by: Pinned first, then by createdAt (newest first)
- Includes replyCount for each comment
- Respects post visibility (must be able to view post)
- Paginated for performance
- Each comment includes author info and engagement metrics
- Does NOT include replies (use Get Replies endpoint)

**Business Rules**:
1. **Top-Level Only**: Returns only direct comments on post, not replies
2. **Pinned Priority**: Pinned comments always appear first
3. **Sorting**: After pinned, sorted by newest first
4. **Reply Count**: Each comment shows number of replies
5. **Pagination**: Default 20 per page, max 100
6. **Visibility**: Same visibility rules as viewing the post

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post | Must be valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1, Max: 100 | 20 |

**Success Response**: Returns array of CommentResponse objects

**Success Response Message**: "Comments retrieved successfully"

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot view post
- `404 NOT_FOUND`: Post not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 45. Get Comment
**Purpose**: Retrieve a single comment by ID with full details

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/comments/{commentId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns full comment details including author, content, engagement
- Includes parsed content with @mentions
- Shows user's interaction state (hasLiked)
- Respects post visibility rules
- Returns parent comment info if it's a reply

**Business Rules**:
1. **Visibility**: Must be able to view parent post
2. **Deleted**: Returns 404 if comment deleted
3. **Full Details**: Includes all engagement metrics
4. **Context**: Can get comment without knowing postId

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of the comment | Must be valid UUID format |

**Success Response**: Returns CommentResponse object

**Success Response Message**: "Comment retrieved successfully"

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot view post
- `404 NOT_FOUND`: Comment not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 46. Get Replies
**Purpose**: Retrieve all replies to a specific comment (nested comments)

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/comments/{commentId}/replies`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Returns all direct replies to specified comment
- Sorted by createdAt (oldest first for conversation flow)
- Each reply can have its own replies (nested)
- Paginated for performance
- Includes reply count for each reply (supports infinite nesting)

**Business Rules**:
1. **Direct Replies Only**: Returns immediate children, not grandchildren
2. **Conversation Order**: Sorted oldest first (chronological conversation)
3. **Nested Support**: Each reply shows its own replyCount
4. **Recursive Loading**: Frontend loads nested replies on-demand
5. **Pagination**: Default 50 per page (comments usually have fewer replies)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of parent comment | Must be valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number (1-indexed) | Min: 1 | 1 |
| size | integer | No | Items per page | Min: 1, Max: 100 | 50 |

**Success Response**: Returns array of CommentResponse objects

**Success Response Message**: "Replies retrieved successfully"

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot view post
- `404 NOT_FOUND`: Comment not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 47. Update Comment
**Purpose**: Edit an existing comment's content

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts/comments/{commentId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Updates comment content
- Sets isEdited flag to true
- Updates updatedAt timestamp
- Preserves original createdAt timestamp
- Re-parses @mentions in updated content
- Sends notifications to newly mentioned users
- Only comment author can edit
- Edit window: 15 minutes from creation (configurable)

**Business Rules**:
1. **Author Only**: Only comment author can edit
2. **Time Window**: Can only edit within 15 minutes of creation
3. **Edit Flag**: isEdited set to true (visible to users)
4. **Content Validation**: Same rules as creation (1-2000 chars)
5. **Mention Updates**: New mentions trigger notifications
6. **No History**: Only current version stored (no edit history)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of comment to edit | Must be valid UUID format |

**Request JSON Sample**:
```json
{
  "content": "Updated comment with correction! @new_user check this"
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| content | string | Yes | Updated comment text | Min: 1, Max: 2000 characters |

**Success Response**: Returns updated CommentResponse object with isEdited = true

**Success Response Message**: "Comment updated successfully"

**Standard Error Types**:
- `400 BAD_REQUEST`: Edit window expired (>15 minutes)
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Not comment author
- `404 NOT_FOUND`: Comment not found
- `422 UNPROCESSABLE_ENTITY`: Validation errors
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Edit Window Expired (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot edit comment",
  "action_time": "2025-12-11T10:30:45",
  "data": "Comments can only be edited within 15 minutes of posting"
}
```

*Not Comment Author (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Cannot edit comment",
  "action_time": "2025-12-11T10:30:45",
  "data": "Only the comment author can edit this comment"
}
```

---

## 48. Delete Comment
**Purpose**: Soft delete a comment (marks as deleted, preserves data)

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/comments/{commentId}`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Performs soft delete: Sets isDeleted flag, doesn't remove from DB
- Decrements post's commentsCount by 1
- Replies remain visible but show "[deleted]" as parent
- Comment author and post author can delete
- Preserves comment data for audit/analytics
- Updates comment content to "[deleted]" in responses
- No notification sent

**Business Rules**:
1. **Who Can Delete**: Comment author OR post author
2. **Soft Delete**: Data retained in database
3. **Replies Preserved**: Child replies remain visible
4. **Parent Reference**: Replies show "[deleted]" as parent content
5. **Counter Update**: Post's comment count decremented
6. **Cascade Option**: Post author can cascade delete all replies (optional)
7. **Silent Operation**: No notifications sent

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of comment to delete | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Comment deleted successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Not comment author or post author
- `404 NOT_FOUND`: Comment not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Use Case Examples**:

*Example 1: Author Deletes Own Comment*
```
DELETE /e-social/posts/comments/770e8400-e29b-41d4-a716-446655440001

User is comment author
Comment has 3 replies
Result: Comment deleted, replies preserved, show "[deleted]" as parent
```

*Example 2: Post Author Deletes Inappropriate Comment*
```
DELETE /e-social/posts/comments/880e8400-e29b-41d4-a716-446655440002

User is post author (not comment author)
Comment violates community guidelines
Result: Comment deleted by post author, content moderation applied
```

---

## 49. Pin Comment
**Purpose**: Pin a comment to the top of post's comment section (post author only)

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/{postId}/comments/{commentId}/pin`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Pins comment to top of comment list
- Sets isPinned flag to true
- Only one comment can be pinned per post
- If another comment already pinned, it gets unpinned automatically
- Post author or collaborators can pin
- Pinned comment always appears first in GET /comments
- Sends notification to comment author

**Business Rules**:
1. **Post Author Only**: Only post author/collaborators can pin
2. **Single Pin**: Only 1 comment pinned at a time
3. **Auto-Unpin**: Pinning new comment unpins previous
4. **Top-Level Only**: Can only pin top-level comments (not replies)
5. **Notification**: Comment author notified of pin
6. **Visibility**: Pinned indicator shown to all users

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | string | Yes | UUID of the post | Must be valid UUID format |
| commentId | string | Yes | UUID of comment to pin | Must be valid UUID, must be top-level comment |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Comment pinned successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Cannot pin replies, only top-level comments
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Not post author/collaborator
- `404 NOT_FOUND`: Post or comment not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

**Error Response Examples**:

*Cannot Pin Reply (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot pin reply",
  "action_time": "2025-12-11T10:30:45",
  "data": "Only top-level comments can be pinned. Replies cannot be pinned."
}
```

**Use Case Examples**:

*Example 1: Pin Important Comment*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/comments/770e8400-e29b-41d4-a716-446655440001/pin

Post author pins helpful comment
Result: Comment appears at top, comment author notified, isPinned = true
```

*Example 2: Pin New Comment (Auto-Unpins Previous)*
```
POST /e-social/posts/550e8400-e29b-41d4-a716-446655440000/comments/880e8400-e29b-41d4-a716-446655440002/pin

Already have pinned comment
Result: Previous comment unpinned automatically, new comment pinned
```

---

## 50. Unpin Comment
**Purpose**: Remove pin from a comment

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/comments/{commentId}/pin`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Removes pin from comment
- Sets isPinned flag to false
- Comment returns to normal sort order (by createdAt)
- Post author or collaborators can unpin
- No notification sent
- Idempotent: Unpinning non-pinned comment returns success

**Business Rules**:
1. **Post Author Only**: Only post author/collaborators can unpin
2. **Silent Operation**: No notification sent
3. **Idempotent**: Safe to call on non-pinned comments
4. **Sort Restoration**: Comment returns to chronological position

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of comment to unpin | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Comment unpinned successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Not post author/collaborator
- `404 NOT_FOUND`: Comment not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 51. Like Comment
**Purpose**: Add a like to a comment

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/e-social/posts/comments/{commentId}/like`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Adds current user's like to comment
- Increments comment's likesCount by 1
- Sends notification to comment author (unless self-like)
- Updates user's interaction state (hasLiked = true)
- Idempotent: Multiple like attempts don't create duplicates
- Must be able to view parent post to like comment

**Business Rules**:
1. **Visibility**: Must have access to parent post
2. **Blocking**: Cannot like comments from blocked users
3. **Deleted**: Cannot like deleted comments
4. **Idempotent**: Safe to call multiple times
5. **Notification**: Comment author notified (unless self-like)
6. **Counter Update**: Atomic increment

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of comment to like | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Comment liked successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Cannot like (blocked, no post access)
- `404 NOT_FOUND`: Comment not found
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---

## 52. Unlike Comment
**Purpose**: Remove a like from a comment

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/e-social/posts/comments/{commentId}/like`

**Access Level**: 🔒 Protected (Requires Bearer Token Authentication)

**Authentication**: Bearer Token

**Behavior**:
- Removes current user's like from comment
- Decrements comment's likesCount by 1
- No notification sent (silent operation)
- Updates user's interaction state (hasLiked = false)
- Idempotent: Multiple unlike attempts safe
- Can unlike even if comment deleted or user blocked

**Business Rules**:
1. **Silent Operation**: No notification sent
2. **Always Allowed**: Can unlike even if blocked or comment deleted
3. **Idempotent**: Safe to call multiple times
4. **Counter Decrement**: Atomic decrement

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| commentId | string | Yes | UUID of comment to unlike | Must be valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Comment unliked successfully",
  "action_time": "2025-12-11T10:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Authentication required
- `404 NOT_FOUND`: Comment not found (permanent deletion)
- `500 INTERNAL_SERVER_ERROR`: Unexpected errors

---
## 53. Share Post

**Purpose**: Record a post share action and track which platform was used for sharing

**Endpoint**: <span style="background-color: #49cc90; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/api/v1/e-social/posts/{postId}/share`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | Unique identifier of the post to share | Valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| platform | string | No | Platform where the post is being shared | Enum: WHATSAPP, TWITTER, FACEBOOK, INSTAGRAM, TELEGRAM, COPY_LINK, OTHER , IN_APP| OTHER |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post shared successfully",
  "action_time": "2025-12-29T14:30:45",
  "data": null
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| success | Indicates if the share was recorded successfully |
| message | Confirmation message |

**Error Response JSON Samples**:

*Post Not Found:*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Post not found",
  "action_time": "2025-12-29T14:30:45",
  "data": null
}
```

*Post Not Published:*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Can only share published posts",
  "action_time": "2025-12-29T14:30:45",
  "data": null
}
```

*Unauthorized:*
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "User not authenticated",
  "action_time": "2025-12-29T14:30:45",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Post not found or post is not published
- `401 UNAUTHORIZED`: Missing or invalid authentication token

**Share Platform Values**:
| Platform | Description |
|----------|-------------|
| WHATSAPP | Shared via WhatsApp |
| TWITTER | Shared via Twitter/X |
| FACEBOOK | Shared via Facebook |
| INSTAGRAM | Shared via Instagram |
| TELEGRAM | Shared via Telegram |
| COPY_LINK | User copied the post link |
| OTHER | Shared via other means or unspecified |

**Usage Notes**:
- Unlike likes and bookmarks, shares can be recorded multiple times by the same user
- Each share is tracked separately for analytics purposes
- The share count on the post increments with each share action
- Invalid platform values default to OTHER

**Usage Examples**:

```bash
# Share post to WhatsApp
curl -X POST "{base_url}/api/v1/e-social/posts/550e8400-e29b-41d4-a716-446655440000/share?platform=WHATSAPP" \
  -H "Authorization: Bearer {token}"

# Share post to Twitter
curl -X POST "{base_url}/api/v1/e-social/posts/550e8400-e29b-41d4-a716-446655440000/share?platform=TWITTER" \
  -H "Authorization: Bearer {token}"

# Record copy link action
curl -X POST "{base_url}/api/v1/e-social/posts/550e8400-e29b-41d4-a716-446655440000/share?platform=COPY_LINK" \
  -H "Authorization: Bearer {token}"

# Generic share (defaults to OTHER)
curl -X POST "{base_url}/api/v1/e-social/posts/550e8400-e29b-41d4-a716-446655440000/share" \
  -H "Authorization: Bearer {token}"
```

**Response in Post Object**:

After sharing, the post's engagement and user interaction data will reflect the share:

```json
{
  "engagement": {
    "likesCount": 42,
    "commentsCount": 15,
    "repostsCount": 8,
    "quotesCount": 3,
    "bookmarksCount": 12,
    "sharesCount": 25,
    "viewsCount": 1500,
    "canLike": true,
    "canComment": true,
    "canRepost": true,
    "canShare": true
  },
  "userInteraction": {
    "hasLiked": true,
    "hasBookmarked": false,
    "hasReposted": false,
    "hasCommented": true,
    "hasViewed": true,
    "hasShared": true
  }
}
```
## 54. Report Post

**Purpose**: Report a post for violating community guidelines or platform rules

**Endpoint**: <span style="background-color: #49cc90; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/api/v1/e-social/posts/{postId}/report`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | Unique identifier of the post to report | Valid UUID format |

**Request JSON Sample**:
```json
{
  "reason": "HATE_SPEECH",
  "description": "This post contains discriminatory language targeting a specific group."
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| reason | string | Yes | Reason for reporting | Enum: SPAM, MISLEADING_INFO, FALSE_INFORMATION, HATE_SPEECH, HARASSMENT, BULLYING, THREATS, NUDITY, SEXUAL_CONTENT, VIOLENCE, GRAPHIC_CONTENT, ILLEGAL_GOODS, FRAUD, SCAM, INTELLECTUAL_PROPERTY, SELF_HARM, SUICIDE, PRIVACY_VIOLATION, PERSONAL_INFO_EXPOSED, IMPERSONATION, FAKE_ACCOUNT, UNDERAGE_USER, OTHER |
| description | string | No | Additional details about the report | Max 1000 characters |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post reported successfully",
  "action_time": "2025-12-29T15:30:45",
  "data": null
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| success | Indicates if the report was submitted successfully |
| message | Confirmation message |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "You have already reported this post",
  "action_time": "2025-12-29T15:30:45",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Post not found, already reported, or attempting to report own post
- `401 UNAUTHORIZED`: Missing or invalid authentication token

---

## 55. Get Post Reports (Admin)

**Purpose**: Retrieve all reports for a specific post (Admin only)

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-social/posts/{postId}/reports`

**Access Level**: 🔒 Protected (Admin Only)

**Authentication**: Bearer Token (Admin role required)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for admin authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | Unique identifier of the post | Valid UUID format |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| page | integer | No | Page number for pagination | Min: 1 | 1 |
| size | integer | No | Number of results per page | Min: 1, Max: 100 | 20 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post reports retrieved successfully",
  "action_time": "2025-12-29T15:30:45",
  "data": {
    "content": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "postId": "660e8400-e29b-41d4-a716-446655440001",
        "reporter": {
          "id": "770e8400-e29b-41d4-a716-446655440002",
          "userName": "john_doe",
          "firstName": "John",
          "lastName": "Doe",
          "profilePictureUrl": "https://storage.example.com/profiles/john.jpg"
        },
        "postAuthor": {
          "id": "880e8400-e29b-41d4-a716-446655440003",
          "userName": "jane_smith",
          "firstName": "Jane",
          "lastName": "Smith",
          "profilePictureUrl": "https://storage.example.com/profiles/jane.jpg"
        },
        "reason": "HATE_SPEECH",
        "description": "This post contains discriminatory language",
        "status": "PENDING",
        "review": null,
        "createdAt": "2025-12-29T14:00:00",
        "updatedAt": "2025-12-29T14:00:00"
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440099",
        "postId": "660e8400-e29b-41d4-a716-446655440001",
        "reporter": {
          "id": "990e8400-e29b-41d4-a716-446655440004",
          "userName": "alex_wilson",
          "firstName": "Alex",
          "lastName": "Wilson",
          "profilePictureUrl": null
        },
        "postAuthor": {
          "id": "880e8400-e29b-41d4-a716-446655440003",
          "userName": "jane_smith",
          "firstName": "Jane",
          "lastName": "Smith",
          "profilePictureUrl": "https://storage.example.com/profiles/jane.jpg"
        },
        "reason": "HARASSMENT",
        "description": "Targeted harassment in the comments",
        "status": "ACTION_TAKEN",
        "review": {
          "reviewedBy": "aa0e8400-e29b-41d4-a716-446655440005",
          "reviewerName": "Admin User",
          "reviewedAt": "2025-12-29T15:00:00",
          "adminNotes": "Verified harassment, post removed",
          "actionTaken": "POST_REMOVED"
        },
        "createdAt": "2025-12-29T13:00:00",
        "updatedAt": "2025-12-29T15:00:00"
      }
    ],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 20,
      "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
      },
      "offset": 0,
      "paged": true,
      "unpaged": false
    },
    "last": true,
    "totalPages": 1,
    "totalElements": 2,
    "first": true,
    "size": 20,
    "number": 0,
    "numberOfElements": 2,
    "empty": false
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| content | Array of report objects for the post |
| content[].id | Unique identifier of the report |
| content[].postId | ID of the reported post |
| content[].reporter | Information about the user who filed the report |
| content[].postAuthor | Information about the post author |
| content[].reason | Reason for the report |
| content[].description | Additional details provided by reporter |
| content[].status | Current status of the report |
| content[].review | Review information if report has been reviewed |
| totalElements | Total number of reports for this post |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Access denied",
  "action_time": "2025-12-29T15:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Missing or invalid authentication token
- `403 FORBIDDEN`: User does not have admin role

---

## 56. Get All Reports (Admin)

**Purpose**: Retrieve all reports with optional filters for status, reason, and date range (Admin only)

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-social/posts/reports`

**Access Level**: 🔒 Protected (Admin Only)

**Authentication**: Bearer Token (Admin role required)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for admin authentication |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| status | string | No | Filter by report status | Enum: PENDING, UNDER_REVIEW, ACTION_TAKEN, DISMISSED, ESCALATED | null (all) |
| reason | string | No | Filter by report reason | Enum: SPAM, MISLEADING_INFO, FALSE_INFORMATION, HATE_SPEECH, HARASSMENT, BULLYING, THREATS, NUDITY, SEXUAL_CONTENT, VIOLENCE, GRAPHIC_CONTENT, ILLEGAL_GOODS, FRAUD, SCAM, INTELLECTUAL_PROPERTY, SELF_HARM, SUICIDE, PRIVACY_VIOLATION, PERSONAL_INFO_EXPOSED, IMPERSONATION, FAKE_ACCOUNT, UNDERAGE_USER, OTHER | null (all) |
| startDate | datetime | No | Filter reports created after this date | ISO 8601 format | null |
| endDate | datetime | No | Filter reports created before this date | ISO 8601 format | null |
| page | integer | No | Page number for pagination | Min: 1 | 1 |
| size | integer | No | Number of results per page | Min: 1, Max: 100 | 20 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Reports retrieved successfully",
  "action_time": "2025-12-29T15:30:45",
  "data": {
    "content": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "postId": "660e8400-e29b-41d4-a716-446655440001",
        "reporter": {
          "id": "770e8400-e29b-41d4-a716-446655440002",
          "userName": "john_doe",
          "firstName": "John",
          "lastName": "Doe",
          "profilePictureUrl": "https://storage.example.com/profiles/john.jpg"
        },
        "postAuthor": {
          "id": "880e8400-e29b-41d4-a716-446655440003",
          "userName": "jane_smith",
          "firstName": "Jane",
          "lastName": "Smith",
          "profilePictureUrl": "https://storage.example.com/profiles/jane.jpg"
        },
        "reason": "SPAM",
        "description": "This account is posting spam content repeatedly",
        "status": "PENDING",
        "review": null,
        "createdAt": "2025-12-29T14:00:00",
        "updatedAt": "2025-12-29T14:00:00"
      }
    ],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 20,
      "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
      },
      "offset": 0,
      "paged": true,
      "unpaged": false
    },
    "last": true,
    "totalPages": 1,
    "totalElements": 1,
    "first": true,
    "size": 20,
    "number": 0,
    "numberOfElements": 1,
    "empty": false
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| content | Array of report objects matching the filters |
| content[].id | Unique identifier of the report |
| content[].postId | ID of the reported post |
| content[].reporter | Information about the user who filed the report |
| content[].postAuthor | Information about the post author |
| content[].reason | Reason for the report |
| content[].status | Current status: PENDING, UNDER_REVIEW, ACTION_TAKEN, DISMISSED, ESCALATED |
| totalElements | Total number of matching reports |
| totalPages | Total number of pages |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Access denied",
  "action_time": "2025-12-29T15:30:45",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Missing or invalid authentication token
- `403 FORBIDDEN`: User does not have admin role

---

## 57. Review Report (Admin)

**Purpose**: Review and update the status of a report (Admin only)

**Endpoint**: <span style="background-color: #fca130; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> `{base_url}/api/v1/e-social/posts/reports/{reportId}/review`

**Access Level**: 🔒 Protected (Admin Only)

**Authentication**: Bearer Token (Admin role required)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for admin authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| reportId | UUID | Yes | Unique identifier of the report | Valid UUID format |

**Request JSON Sample**:
```json
{
  "status": "ACTION_TAKEN",
  "adminNotes": "Verified hate speech violation. Post has been removed and user warned.",
  "actionTaken": "POST_REMOVED_USER_WARNED"
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| status | string | Yes | New status for the report | Enum: PENDING, UNDER_REVIEW, ACTION_TAKEN, DISMISSED, ESCALATED |
| adminNotes | string | No | Internal notes about the review | Max 500 characters |
| actionTaken | string | No | Description of action taken | Max 50 characters |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Report reviewed successfully",
  "action_time": "2025-12-29T16:00:00",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "postId": "660e8400-e29b-41d4-a716-446655440001",
    "reporter": {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "userName": "john_doe",
      "firstName": "John",
      "lastName": "Doe",
      "profilePictureUrl": "https://storage.example.com/profiles/john.jpg"
    },
    "postAuthor": {
      "id": "880e8400-e29b-41d4-a716-446655440003",
      "userName": "jane_smith",
      "firstName": "Jane",
      "lastName": "Smith",
      "profilePictureUrl": "https://storage.example.com/profiles/jane.jpg"
    },
    "reason": "HATE_SPEECH",
    "description": "This post contains discriminatory language",
    "status": "ACTION_TAKEN",
    "review": {
      "reviewedBy": "aa0e8400-e29b-41d4-a716-446655440005",
      "reviewerName": "Admin User",
      "reviewedAt": "2025-12-29T16:00:00",
      "adminNotes": "Verified hate speech violation. Post has been removed and user warned.",
      "actionTaken": "POST_REMOVED_USER_WARNED"
    },
    "createdAt": "2025-12-29T14:00:00",
    "updatedAt": "2025-12-29T16:00:00"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| id | Unique identifier of the report |
| postId | ID of the reported post |
| reporter | Information about the user who filed the report |
| postAuthor | Information about the post author |
| reason | Reason for the report |
| status | Updated status of the report |
| review | Review details including reviewer info, notes, and action taken |
| review.reviewedBy | UUID of the admin who reviewed |
| review.reviewerName | Name of the admin who reviewed |
| review.reviewedAt | Timestamp of the review |
| review.adminNotes | Internal notes from the admin |
| review.actionTaken | Action taken on the report |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Report not found",
  "action_time": "2025-12-29T16:00:00",
  "data": null
}
```

**Standard Error Types**:
- `400 BAD_REQUEST`: Report not found or invalid status
- `401 UNAUTHORIZED`: Missing or invalid authentication token
- `403 FORBIDDEN`: User does not have admin role

---

## 58. Get Report Statistics (Admin)

**Purpose**: Retrieve aggregate statistics about reports (Admin only)

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-social/posts/reports/stats`

**Access Level**: 🔒 Protected (Admin Only)

**Authentication**: Bearer Token (Admin role required)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for admin authentication |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Report statistics retrieved successfully",
  "action_time": "2025-12-29T16:30:00",
  "data": {
    "totalReports": 1250,
    "pendingReports": 45,
    "underReviewReports": 12,
    "actionTakenReports": 890,
    "dismissedReports": 298,
    "escalatedReports": 5,
    "reportsByReason": {
      "SPAM": 320,
      "HATE_SPEECH": 180,
      "HARASSMENT": 150,
      "MISLEADING_INFO": 120,
      "NUDITY": 95,
      "VIOLENCE": 85,
      "SCAM": 75,
      "FALSE_INFORMATION": 60,
      "BULLYING": 55,
      "SEXUAL_CONTENT": 40,
      "PRIVACY_VIOLATION": 30,
      "IMPERSONATION": 20,
      "OTHER": 20
    }
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| totalReports | Total number of reports in the system |
| pendingReports | Number of reports awaiting review |
| underReviewReports | Number of reports currently being reviewed |
| actionTakenReports | Number of reports where action was taken |
| dismissedReports | Number of reports that were dismissed |
| escalatedReports | Number of reports escalated for further review |
| reportsByReason | Object containing count of reports grouped by reason |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Access denied",
  "action_time": "2025-12-29T16:30:00",
  "data": null
}
```

**Standard Error Types**:
- `401 UNAUTHORIZED`: Missing or invalid authentication token
- `403 FORBIDDEN`: User does not have admin role

## 59. Edit Published Post
**Purpose**: Edit a published post within the 60-minute edit window (max 3 edits)

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/e-social/posts/{postId}/edit`

**Access Level**: 🔒 Protected (Post owner only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the post to edit | Must be valid UUID, post must exist |

**Request JSON Sample (Regular/Quote Post)**:
```json
{
  "content": "Updated post content! 🔥",
  "media": [
    {
      "mediaType": "IMAGE",
      "mediaUrl": "https://cdn.nexgate.it/new-image.jpg",
      "placeholderBase64": "data:image/jpeg;base64,/9j/4AAQ...",
      "width": 1080,
      "height": 1080
    }
  ],
  "attachments": {
    "productIds": ["550e8400-e29b-41d4-a716-446655440001"],
    "shopIds": ["550e8400-e29b-41d4-a716-446655440002"],
    "eventIds": [],
    "buyTogetherGroupIds": null,
    "installmentPlanIds": null,
    "externalLink": {
      "url": "https://example.com/new-link"
    },
    "removeExternalLink": false
  },
  "collaboration": {
    "collaboratorIds": ["550e8400-e29b-41d4-a716-446655440003"]
  },
  "privacySettings": {
    "visibility": "PUBLIC",
    "whoCanComment": "FOLLOWERS",
    "whoCanRepost": "EVERYONE",
    "hideLikesCount": false,
    "hideCommentsCount": false
  }
}
```

**Request JSON Sample (Poll Post - Limited Fields)**:
```json
{
  "content": "Updated poll question context",
  "pollTitle": "Updated Poll Title",
  "pollDescription": "Updated poll description",
  "privacySettings": {
    "visibility": "PUBLIC",
    "whoCanComment": "EVERYONE"
  }
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| content | string | No | Updated post text content | Max: 5000 characters |
| media | array | No | Updated media attachments | Max: 10 items |
| media[].mediaType | string | Yes* | Type of media | enum: IMAGE, VIDEO, GIF |
| media[].mediaUrl | string | Yes* | URL to media file | Valid URL |
| media[].placeholderBase64 | string | No | BlurHash placeholder | Base64 encoded |
| media[].width | integer | No | Media width in pixels | Min: 1 |
| media[].height | integer | No | Media height in pixels | Min: 1 |
| media[].duration | integer | No | Video duration in seconds | Min: 1 (videos only) |
| attachments | object | No | Updated attachments | See attachment behavior below |
| attachments.productIds | array | No | Product IDs to attach | null=no change, []=remove all |
| attachments.shopIds | array | No | Shop IDs to attach | null=no change, []=remove all |
| attachments.eventIds | array | No | Event IDs to attach | null=no change, []=remove all |
| attachments.buyTogetherGroupIds | array | No | Buy Together Group IDs | null=no change, []=remove all |
| attachments.installmentPlanIds | array | No | Installment Plan IDs | null=no change, []=remove all |
| attachments.externalLink | object | No | External link to attach | null=no change |
| attachments.externalLink.url | string | Yes* | URL of external link | Valid URL |
| attachments.removeExternalLink | boolean | No | Remove existing external link | Default: false |
| collaboration | object | No | Updated collaboration settings | |
| collaboration.collaboratorIds | array | No | User IDs to invite as collaborators | Valid user UUIDs |
| privacySettings | object | No | Updated privacy settings | |
| privacySettings.visibility | string | No | Post visibility | enum: PUBLIC, FOLLOWERS, MENTIONED, PRIVATE |
| privacySettings.whoCanComment | string | No | Comment permissions | enum: EVERYONE, FOLLOWERS, MENTIONED, DISABLED |
| privacySettings.whoCanRepost | string | No | Repost permissions | enum: EVERYONE, FOLLOWERS, DISABLED |
| privacySettings.hideLikesCount | boolean | No | Hide likes count | Default: false |
| privacySettings.hideCommentsCount | boolean | No | Hide comments count | Default: false |
| pollTitle | string | No | Poll title (poll posts only) | Max: 200 characters |
| pollDescription | string | No | Poll description (poll posts only) | Max: 1000 characters |

**Attachment Edit Behavior**:
| Field Value | Behavior |
|-------------|----------|
| `null` | No change to that attachment type |
| `[]` (empty array) | Remove all attachments of that type |
| `[ids...]` | Replace with new attachment IDs |

**Collaboration Edit Behavior**:
| Collaborator Status | Behavior |
|---------------------|----------|
| ACCEPTED | Preserved (cannot be removed via edit) |
| PENDING | Can be removed |
| DECLINED | Can be removed |
| New IDs | Added as PENDING status |

**Poll Post Restrictions**:
| Field | Can Edit |
|-------|----------|
| content | ✅ Yes |
| pollTitle | ✅ Yes |
| pollDescription | ✅ Yes |
| privacySettings | ✅ Yes |
| Poll options | ❌ No (would invalidate votes) |
| media | ❌ No |
| attachments | ❌ No |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post edited successfully",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "author": {
      "id": "660e8400-e29b-41d4-a716-446655440000",
      "userName": "johndoe",
      "displayName": "John Doe",
      "profileImage": "https://cdn.nexgate.it/profiles/johndoe.jpg",
      "isVerified": true
    },
    "content": "Updated post content! 🔥",
    "postType": "REGULAR",
    "status": "PUBLISHED",
    "editInfo": {
      "isEdited": true,
      "editCount": 1,
      "canEdit": true,
      "lastEditedAt": "2025-01-03T10:30:45"
    },
    "createdAt": "2025-01-03T10:00:00",
    "publishedAt": "2025-01-03T10:00:00"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| id | Post UUID |
| author | Post author details |
| content | Updated post content |
| postType | Type of post (REGULAR, POLL, QUOTE, REPOST) |
| status | Post status (always PUBLISHED for edited posts) |
| editInfo.isEdited | Whether post has been edited |
| editInfo.editCount | Number of times post has been edited |
| editInfo.canEdit | Whether current user can still edit |
| editInfo.lastEditedAt | Timestamp of last edit |

**Error Response Examples**:

*Not Post Owner (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "You can only edit your own posts",
  "action_time": "2025-01-03T10:30:45",
  "data": "You can only edit your own posts"
}
```

*Edit Limit Reached (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Maximum edit limit reached. You can only edit a post 3 times.",
  "action_time": "2025-01-03T10:30:45",
  "data": "Maximum edit limit reached. You can only edit a post 3 times."
}
```

*Edit Window Expired (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Edit window has expired. Posts can only be edited within 60 minutes of publishing.",
  "action_time": "2025-01-03T10:30:45",
  "data": "Edit window has expired. Posts can only be edited within 60 minutes of publishing."
}
```

*Post Not Published (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Only published posts can be edited",
  "action_time": "2025-01-03T10:30:45",
  "data": "Only published posts can be edited"
}
```

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-01-03T10:30:45",
  "data": "Post not found"
}
```

*Unauthorized (401):*
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2025-01-03T10:30:45",
  "data": "Token has expired"
}
```

---

## 60. Get Post Edit Info
**Purpose**: Get detailed edit status and eligibility information for a post

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/edit-info`

**Access Level**: 🔒 Protected

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the post | Must be valid UUID, post must exist |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post edit info retrieved successfully",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "postId": "550e8400-e29b-41d4-a716-446655440000",
    "isEdited": true,
    "editCount": 2,
    "maxEdits": 3,
    "remainingEdits": 1,
    "canEdit": true,
    "cannotEditReason": null,
    "publishedAt": "2025-01-03T10:00:00",
    "editWindowExpiresAt": "2025-01-03T11:00:00",
    "lastEditedAt": "2025-01-03T10:30:00",
    "minutesRemainingInWindow": 25
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| postId | Post UUID |
| isEdited | Whether post has been edited at least once |
| editCount | Number of times post has been edited (0-3) |
| maxEdits | Maximum allowed edits (always 3) |
| remainingEdits | Edits remaining (maxEdits - editCount) |
| canEdit | Whether current user can edit this post now |
| cannotEditReason | Reason why editing is not allowed (null if canEdit is true) |
| publishedAt | When the post was originally published |
| editWindowExpiresAt | When the 60-minute edit window expires |
| lastEditedAt | Timestamp of most recent edit (null if never edited) |
| minutesRemainingInWindow | Minutes left in edit window (0 if expired) |

**Cannot Edit Reasons**:
| Reason | Description |
|--------|-------------|
| `"Authentication required"` | User not logged in |
| `"Only the author can edit this post"` | Current user is not the post owner |
| `"Only published posts can be edited"` | Post is in DRAFT or SCHEDULED status |
| `"Maximum edit limit reached (3 edits)"` | All 3 edits have been used |
| `"Edit window expired (60 minutes after publishing)"` | More than 60 minutes since publishing |

**Error Response Examples**:

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-01-03T10:30:45",
  "data": "Post not found"
}
```

---

## 61. Get Post Edit History
**Purpose**: View complete edit history with immutable snapshots of each version

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/edit-history`

**Access Level**: 🌐 Public (Anyone can view edit history of public posts)

**Authentication**: Bearer Token (Optional - required for non-public posts)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | No | Bearer token for accessing non-public posts |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the post | Must be valid UUID, post must exist |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post edit history retrieved successfully",
  "action_time": "2025-01-03T10:45:00",
  "data": {
    "postId": "550e8400-e29b-41d4-a716-446655440000",
    "totalEdits": 2,
    "remainingEdits": 1,
    "canStillEdit": true,
    "editWindowExpiresAt": "2025-01-03T11:00:00",
    "versions": [
      {
        "id": "770e8400-e29b-41d4-a716-446655440001",
        "version": 0,
        "snapshot": {
          "content": "Original post content",
          "media": [
            {
              "id": "m1",
              "mediaType": "IMAGE",
              "originalUrl": "https://cdn.nexgate.it/original.jpg",
              "placeholderBase64": "data:image/jpeg;base64,...",
              "width": 1080,
              "height": 1080
            }
          ],
          "attachments": {
            "productIds": ["550e8400-e29b-41d4-a716-446655440111"],
            "shopIds": [],
            "eventIds": [],
            "buyTogetherGroupIds": [],
            "installmentPlanIds": [],
            "externalLink": null
          },
          "collaboratorIds": [],
          "privacySettings": {
            "visibility": "PUBLIC",
            "whoCanComment": "EVERYONE",
            "whoCanRepost": "EVERYONE",
            "hideLikesCount": false,
            "hideCommentsCount": false
          },
          "pollTitle": null,
          "pollDescription": null
        },
        "editedAt": "2025-01-03T10:15:00",
        "isCurrent": false
      },
      {
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "version": 1,
        "snapshot": {
          "content": "First edit - added more details!",
          "media": [
            {
              "id": "m1",
              "mediaType": "IMAGE",
              "originalUrl": "https://cdn.nexgate.it/original.jpg"
            },
            {
              "id": "m2",
              "mediaType": "IMAGE",
              "originalUrl": "https://cdn.nexgate.it/new-image.jpg"
            }
          ],
          "attachments": {
            "productIds": ["550e8400-e29b-41d4-a716-446655440111", "550e8400-e29b-41d4-a716-446655440222"],
            "shopIds": ["550e8400-e29b-41d4-a716-446655440333"],
            "eventIds": [],
            "buyTogetherGroupIds": [],
            "installmentPlanIds": [],
            "externalLink": {
              "url": "https://example.com",
              "shortCode": "abc123",
              "domain": "example.com"
            }
          },
          "collaboratorIds": ["550e8400-e29b-41d4-a716-446655440444"],
          "privacySettings": {
            "visibility": "PUBLIC",
            "whoCanComment": "FOLLOWERS",
            "whoCanRepost": "EVERYONE",
            "hideLikesCount": false,
            "hideCommentsCount": false
          },
          "pollTitle": null,
          "pollDescription": null
        },
        "editedAt": "2025-01-03T10:30:00",
        "isCurrent": false
      },
      {
        "id": null,
        "version": 2,
        "snapshot": {
          "content": "Current version - final updates! 🔥",
          "media": [],
          "attachments": {
            "productIds": [],
            "shopIds": [],
            "eventIds": [],
            "buyTogetherGroupIds": [],
            "installmentPlanIds": [],
            "externalLink": null
          },
          "collaboratorIds": [],
          "privacySettings": {
            "visibility": "PUBLIC",
            "whoCanComment": "EVERYONE",
            "whoCanRepost": "EVERYONE",
            "hideLikesCount": false,
            "hideCommentsCount": false
          },
          "pollTitle": null,
          "pollDescription": null
        },
        "editedAt": "2025-01-03T10:45:00",
        "isCurrent": true
      }
    ]
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| postId | Post UUID |
| totalEdits | Total number of edits made |
| remainingEdits | Edits still available (3 - totalEdits) |
| canStillEdit | Whether post can still be edited |
| editWindowExpiresAt | When edit window expires |
| versions | Array of all versions (original + edits + current) |
| versions[].id | History record UUID (null for current version) |
| versions[].version | Version number (0 = original, N = after Nth edit) |
| versions[].snapshot | Complete immutable state at that version |
| versions[].snapshot.content | Post content at that version |
| versions[].snapshot.media | Media attachments at that version |
| versions[].snapshot.attachments | All attachments at that version |
| versions[].snapshot.collaboratorIds | Collaborator IDs at that version |
| versions[].snapshot.privacySettings | Privacy settings at that version |
| versions[].snapshot.pollTitle | Poll title (null for non-poll posts) |
| versions[].snapshot.pollDescription | Poll description (null for non-poll posts) |
| versions[].editedAt | Timestamp of this version |
| versions[].isCurrent | Whether this is the current live version |

**Version Numbering**:
| Version | Meaning |
|---------|---------|
| 0 | Original published state |
| 1 | State after 1st edit |
| 2 | State after 2nd edit |
| 3 | State after 3rd edit (final) |

**Error Response Examples**:

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-01-03T10:30:45",
  "data": "Post not found"
}
```

---

## 62. Check Post Edit Eligibility
**Purpose**: Quick boolean check if current user can edit a post

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/can-edit`

**Access Level**: 🔒 Protected

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the post | Must be valid UUID |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Edit eligibility checked",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "canEdit": true
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| canEdit | `true` if user can edit, `false` otherwise |

**Eligibility Checks Performed**:
| Check | Must Be |
|-------|---------|
| Post exists | Yes |
| User is post owner | Yes |
| Post status | PUBLISHED |
| Edit count | Less than 3 |
| Time since publish | Less than 60 minutes |

**Use Case**: Frontend can call this endpoint before showing the edit button to avoid displaying unnecessary UI elements.

**Error Response Examples**:

*Post Not Found (returns canEdit: false)*:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Edit eligibility checked",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "canEdit": false
  }
}
```

*Unauthorized (401):*
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2025-01-03T10:30:45",
  "data": "Token has expired"
}
```

---

## 63. Get Post Likers
**Purpose**: Retrieve a paginated list of users who liked a specific post. Authenticated viewers see people they follow prioritized at the top (Twitter-style). Returns social context flags (`followsYou`, `youFollow`) for each user.

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/likers`

**Access Level**: 🌐 Public (social context requires authentication)

**Authentication**: Bearer Token (optional — enhances response with follow-priority ordering and social flags)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | No | Bearer token — enables follow-priority ordering and `followsYou`/`youFollow` flags |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the post | Must be valid UUID |

**Query Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| page | int | No | 1 | Page number (1-indexed) |
| size | int | No | 20 | Items per page |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post likers retrieved successfully",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "items": [
      {
        "userId": "550e8400-e29b-41d4-a716-446655440000",
        "userName": "john_doe",
        "firstName": "John",
        "lastName": "Doe",
        "profilePictureUrls": ["https://cdn.example.com/photos/john.jpg"],
        "isVerified": true,
        "actedAt": "2025-01-03T09:15:00",
        "followsYou": true,
        "youFollow": true
      },
      {
        "userId": "660e8400-e29b-41d4-a716-446655440001",
        "userName": "juma_ali",
        "firstName": "Juma",
        "lastName": "Ali",
        "profilePictureUrls": [],
        "isVerified": false,
        "actedAt": "2025-01-03T08:45:00",
        "followsYou": false,
        "youFollow": true
      }
    ],
    "totalElements": 142,
    "totalPages": 8,
    "hasNext": true,
    "hasPrevious": false,
    "isFirst": true,
    "isLast": false,
    "page": 1
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| items | Array of users who liked the post |
| items[].userId | Unique identifier of the liker |
| items[].userName | Username handle |
| items[].firstName | First name |
| items[].lastName | Last name |
| items[].profilePictureUrls | List of profile picture URLs (empty if none set) |
| items[].isVerified | Whether the user has a verified badge |
| items[].actedAt | Timestamp when the user liked the post |
| items[].followsYou | `true` if this user follows the viewer; `null` when unauthenticated |
| items[].youFollow | `true` if the viewer follows this user; `null` when unauthenticated |
| totalElements | Total number of likes on this post |
| totalPages | Total number of pages |
| hasNext | Whether a next page exists |
| hasPrevious | Whether a previous page exists |
| isFirst | Whether this is the first page |
| isLast | Whether this is the last page |
| page | Current page number (1-indexed) |

**Ordering Behavior**:
| Viewer State | Ordering |
|--------------|----------|
| Authenticated | Users the viewer follows appear first, then by most recent like |
| Unauthenticated | Most recent likes first |

**Error Response Examples**:

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-01-03T10:30:45",
  "data": "Post not found"
}
```

**Use Case**: Power a "Liked by" bottom sheet or modal. The post card previews this as "John, Juma + 140 others liked this" using `topLikers` in `PostResponse`; tapping that label opens this paginated endpoint.

---

## 64. Get Post Reposters
**Purpose**: Retrieve a paginated list of users who reposted a specific post. A repost is a quote post with no added text content. Authenticated viewers see people they follow prioritized at the top. Returns social context flags for each user.

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/reposters`

**Access Level**: 🌐 Public (social context requires authentication)

**Authentication**: Bearer Token (optional — enhances response with follow-priority ordering and social flags)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | No | Bearer token — enables follow-priority ordering and `followsYou`/`youFollow` flags |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the post being reposted | Must be valid UUID |

**Query Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| page | int | No | 1 | Page number (1-indexed) |
| size | int | No | 20 | Items per page |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post reposters retrieved successfully",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "items": [
      {
        "userId": "550e8400-e29b-41d4-a716-446655440000",
        "userName": "josh_kamau",
        "firstName": "Josh",
        "lastName": "Kamau",
        "profilePictureUrls": ["https://cdn.example.com/photos/josh.jpg"],
        "isVerified": false,
        "actedAt": "2025-01-03T11:20:00",
        "followsYou": false,
        "youFollow": true
      },
      {
        "userId": "770e8400-e29b-41d4-a716-446655440002",
        "userName": "janeth_m",
        "firstName": "Janeth",
        "lastName": "Mwangi",
        "profilePictureUrls": [],
        "isVerified": true,
        "actedAt": "2025-01-03T10:55:00",
        "followsYou": true,
        "youFollow": false
      }
    ],
    "totalElements": 38,
    "totalPages": 2,
    "hasNext": true,
    "hasPrevious": false,
    "isFirst": true,
    "isLast": false,
    "page": 1
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| items | Array of users who reposted the post |
| items[].userId | Unique identifier of the reposter |
| items[].userName | Username handle |
| items[].firstName | First name |
| items[].lastName | Last name |
| items[].profilePictureUrls | List of profile picture URLs (empty if none set) |
| items[].isVerified | Whether the user has a verified badge |
| items[].actedAt | Timestamp when the user reposted (quote post creation time) |
| items[].followsYou | `true` if this user follows the viewer; `null` when unauthenticated |
| items[].youFollow | `true` if the viewer follows this user; `null` when unauthenticated |
| totalElements | Total number of reposts on this post |
| totalPages | Total number of pages |
| hasNext | Whether a next page exists |
| hasPrevious | Whether a previous page exists |
| isFirst | Whether this is the first page |
| isLast | Whether this is the last page |
| page | Current page number (1-indexed) |

**What Counts as a Repost**:
A repost is a quote post where `content` is null or blank. Quote posts with text content are not included here — they appear in the Quotes endpoint (65) instead. Users are rate-limited to **10 reposts per day**.

**Ordering Behavior**:
| Viewer State | Ordering |
|--------------|----------|
| Authenticated | Users the viewer follows appear first, then by most recent repost |
| Unauthenticated | Most recent reposts first |

**Error Response Examples**:

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-01-03T10:30:45",
  "data": "Post not found"
}
```

**Use Case**: Power a "Reposted by" bottom sheet. The post card previews this as "Josh, Janeth reshared this post" using `topReposters` in `PostResponse`; tapping that label opens this paginated endpoint.

---

## 65. Get Post Quotes
**Purpose**: Retrieve a paginated list of quote posts — posts that reference this post and include added text commentary. Authenticated viewers see people they follow prioritized at the top. Each item includes the quoter's text so the viewer can read the added commentary inline.

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/e-social/posts/{postId}/quotes`

**Access Level**: 🌐 Public (social context requires authentication)

**Authentication**: Bearer Token (optional — enhances response with follow-priority ordering and social flags)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | No | Bearer token — enables follow-priority ordering and `followsYou`/`youFollow` flags |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| postId | UUID | Yes | ID of the original post being quoted | Must be valid UUID |

**Query Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| page | int | No | 1 | Page number (1-indexed) |
| size | int | No | 20 | Items per page |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Post quotes retrieved successfully",
  "action_time": "2025-01-03T10:30:45",
  "data": {
    "items": [
      {
        "id": "aa0e8400-e29b-41d4-a716-446655440099",
        "author": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "userName": "alice_wanjiku",
          "firstName": "Alice",
          "lastName": "Wanjiku",
          "profilePictureUrl": "https://cdn.example.com/photos/alice.jpg",
          "isVerified": true
        },
        "content": "This is such an important point! Everyone should read this.",
        "postType": "REGULAR",
        "status": "PUBLISHED",
        "media": [],
        "topComments": [],
        "topLikers": [],
        "topReposters": [],
        "engagement": {
          "likesCount": 12,
          "commentsCount": 3,
          "repostsCount": 0,
          "quotesCount": 0,
          "bookmarksCount": 5,
          "sharesCount": 1,
          "viewsCount": 240,
          "canLike": true,
          "canComment": true,
          "canRepost": true,
          "canShare": true
        },
        "userInteraction": {
          "hasLiked": false,
          "hasBookmarked": false,
          "hasQuoted": false,
          "hasCommented": false,
          "hasViewed": true,
          "hasShared": false
        },
        "quotedPost": {
          "id": "orig-post-uuid",
          "author": {
            "id": "orig-author-uuid",
            "userName": "original_author",
            "firstName": "Original",
            "lastName": "Author",
            "profilePictureUrl": null,
            "isVerified": false
          },
          "content": "The original post content being quoted.",
          "postType": "REGULAR",
          "status": "PUBLISHED",
          "media": [],
          "createdAt": "2025-01-02T08:00:00Z",
          "publishedAt": "2025-01-02T08:00:00Z"
        },
        "publishedAt": "2025-01-03T12:05:00",
        "createdAt": "2025-01-03T12:05:00"
      }
    ],
    "totalElements": 17,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "isFirst": true,
    "isLast": true,
    "page": 1
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| items | Array of full `PostResponse` objects — one per quote post |
| items[].id | UUID of the quote post |
| items[].author | Author of the quote post (the person who quoted) |
| items[].content | Text commentary added by the quoter |
| items[].postType | Always `REGULAR` for quote posts |
| items[].status | Post status (usually `PUBLISHED`) |
| items[].media | Any media the quoter attached to their quote |
| items[].engagement | Full engagement counters and permission flags for the quote post |
| items[].userInteraction | Current viewer's interaction state with the quote post |
| items[].quotedPost | The embedded original post being quoted (its own author, content, media, engagement) |
| items[].topComments | Top comments on the quote post |
| items[].topLikers | Top likers preview for the quote post |
| items[].topReposters | Top reposters preview for the quote post |
| items[].publishedAt | When the quote post was published |
| totalElements | Total number of quote posts for this post |
| totalPages | Total number of pages |
| hasNext | Whether a next page exists |
| hasPrevious | Whether a previous page exists |
| isFirst | Whether this is the first page |
| isLast | Whether this is the last page |
| page | Current page number (1-indexed) |

**What Counts as a Quote**:
A quote is a post with `quotedPostId` set AND non-blank `content`. Posts with no content referencing this post are reposts and appear in endpoint 64 instead.

**Ordering Behavior**:
| Viewer State | Ordering |
|--------------|----------|
| Authenticated | Users the viewer follows appear first, then by most recent quote |
| Unauthenticated | Most recent quotes first |

**Error Response Examples**:

*Post Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Post not found",
  "action_time": "2025-01-03T10:30:45",
  "data": "Post not found"
}
```

**Use Case**: Display a "Quotes" tab in the post detail view. Each item is a full post card — the quoter's post with their commentary, media, engagement counts, and the original post embedded inside as `quotedPost`. This is the same UX as X's quote tweets tab.

---

## Post Editing Rules Summary

| Rule | Value |
|------|-------|
| Time window | 60 minutes after publish |
| Maximum edits | 3 per post |
| Poll options | ❌ Cannot edit (would invalidate votes) |
| Poll title/description | ✅ Can edit |
| Media | ✅ Can add/remove/replace |
| Attachments | ✅ Can add/remove/replace |
| Collaborators | ✅ Can add new (accepted ones preserved) |
| Privacy settings | ✅ Can edit |
| Engagement | ✅ Preserved (likes, comments, reposts, views) |
| After limits | Must delete and repost (loses engagement) |

## PostResponse.EditInfo Object

Every post response now includes an `editInfo` object:

```json
{
  "id": "post-uuid",
  "content": "...",
  "editInfo": {
    "isEdited": true,
    "editCount": 2,
    "canEdit": false,
    "lastEditedAt": "2025-01-03T10:45:00"
  }
}
```

**Frontend Usage**:
| Field | Use |
|-------|-----|
| `isEdited` | Show "Edited" label on post |
| `editCount` | Display "Edited 2 times" tooltip |
| `canEdit` | Show/hide edit button |
| `lastEditedAt` | Show "Last edited 2h ago" |

## Quick Reference Guide

### Common HTTP Status Codes
- `200 OK`: Successful GET/POST/PUT/DELETE request
- `400 Bad Request`: Invalid request data or business rule violation
- `401 Unauthorized`: Authentication required/failed
- `404 Not Found`: Resource not found
- `422 Unprocessable Entity`: Validation errors
- `500 Internal Server Error`: Server error

### Authentication
- **Bearer Token**: Include `Authorization: Bearer your_token` in headers

### Data Format Standards
- **Dates**: Use ISO 8601 format (2025-12-11T14:30:00Z)
- **IDs**: UUID format (e.g., 550e8400-e29b-41d4-a716-446655440000)
- **Pagination**: 1-indexed pages (page=1 for first page), default size=20

### Post Type Values
- `REGULAR`: Standard text/media post
- `POLL`: Poll post with voting options

### Post Status Values
- `DRAFT`: Unpublished draft
- `SCHEDULED`: Scheduled for future publication
- `PUBLISHED`: Live and visible
- `DELETED`: Soft deleted

### Visibility Levels
- `PUBLIC`: Anyone can see
- `FOLLOWERS`: Only followers can see
- `MENTIONED`: Only mentioned users can see
- `PRIVATE`: Only author can see

### Permission Levels
- `EVERYONE`: All users
- `FOLLOWERS`: Only followers
- `MENTIONED`: Only mentioned users (for comments)
- `DISABLED`: Feature disabled