E_social-nexgate-service(8)
Nexgate Social Media service Overview
Key Differentiators
- Commerce Integration: Users can link products, shops, and events directly in posts
- Shop Mentions: Uses
$shopnamesyntax for shop mentions/tags - Product-Context Messaging: Messages can include product banners with details
- Collaborative Commerce: Social discovery engine for products and events
Core Features
1. User Relationships
Follow System
- Follow User: User A follows User B
- Unfollow User: User A unfollows User B
- Get Followers: Retrieve list of users following a specific user
- Get Following: Retrieve list of users a specific user follows
- Follow Counts: Display follower and following counts on profiles
Privacy Controls
- Block User: Prevent interaction with specific users
- Unblock User: Remove block restriction
- Mute User: Hide user's content without unfollowing
- Unmute User: Restore muted user's content visibility
Account Privacy
- Public Account: Anyone can see posts and follow
- Private Account: Followers must be approved before seeing content
2. Posts
Post Creation
Posts are the central content type in the platform.
Post Types:
- Text-only posts
- Image posts (single or multiple images)
- Video posts
- Poll posts
- Mixed media posts
Post States:
- Draft: Saved but not published
- Published: Live and visible based on privacy settings
- Deleted: Soft-deleted (retained for audit)
Creation Features:
- Rich text content
- Image uploads (multiple)
- Video uploads
- Add polls with multiple options
- Link products using product IDs
- Link buy-together groups (to boost group participation)
- Link installment plans (specific payment plans for products)
- Link shops using
$shopnamesyntax - Link events using event IDs
- Tag users using
@usernamesyntax - Tag shops using
$shopnamesyntax - Tag products on images (coordinates-based like Instagram)
- Tag users on images (coordinates-based)
- Add hashtags using
#hashtagsyntax - Set as collaborative post (multiple co-authors)
- Save as draft for later publishing
Post Management
- Edit Post: Modify post content (with edit history)
- Delete Post: Soft delete post
- Publish Draft: Convert draft to published post
- View Post: Display single post with all details
- Get User Posts: Retrieve all posts by specific user
- Get Feed: Get posts from followed users (chronological)
Image Tagging System
Similar to Instagram's tag functionality:
- Click/tap on image at specific coordinates
- Tag can be: User (
@username), Product (product_id), Shop ($shopname) - Multiple tags per image
- Tags display as interactive overlay on image
- Clicking tag navigates to tagged entity
Collaborative Posts
Collaborative posts allow multiple users to co-author a single post, with all authors displayed and the post appearing on all collaborators' profiles.
Features:
- Invite Collaborators: During post creation or editing, invite other users as co-authors
- Multiple Authors: Support up to 5-10 co-authors per post
- Accept/Decline: Invited collaborators receive notification and can accept or decline
- Pending Status: Post shows "Pending collaboration with @username" until accepted
- Profile Display: Accepted collaborative posts appear on all co-authors' profiles
- Byline: Post displays "By @user1, @user2, and @user3"
- Equal Rights: All collaborators can edit or delete the post (with consent logic)
- Attribution: All engagement (likes, comments) benefits all collaborators
Use Cases:
Fashion Brand + Influencer Collaboration:
- Brand creates post about new collection
- Adds influencer as co-author
- Influencer accepts
- Post appears on both profiles
- Both audiences see the content
- Both get engagement metrics
Joint Product Launch:
- Two shops collaborate on bundle deal
- Create one post together
- Shows on both shop pages
- Shared promotion effort
Workflow:
- User A creates post
- User A adds User B, User C as collaborators during creation
- Post saved with status = "pending_collaboration"
- Notifications sent to User B and User C: "User A invited you to collaborate on a post"
- User B accepts → PostCollaborator status = "accepted"
- User C declines → PostCollaborator deleted, User A notified
- Once all pending invites resolved (accepted/declined), post can be published
- Published collaborative post appears on User A and User B profiles
- Byline shows: "By @userA and @userB"
Rules:
- Post creator is always primary author (cannot be removed)
- Collaborators can decline invitation without penalty
- If any collaborator leaves (removes themselves), post remains but their name removed
- Post creator can remove collaborators anytime
- Deleted collaborative posts removed from all profiles
3. Interactions
Likes
- Like Post: User likes a post
- Unlike Post: User removes like
- Like Comment: User likes a comment
- Unlike Comment: User removes comment like
- View Likes: See list of users who liked post/comment
- Like Count: Display total likes on post/comment
Comments
- Comment on Post: Add text comment to post
- Edit Comment: Modify own comment
- Delete Comment: Remove own comment (soft delete)
- Reply to Comment: Nested comment system (up to N levels)
- Tag in Comments: Tag users using
@usernamein comments - Mention Products: Reference products in comments
- Comment Count: Display total comments on post
Reposting/Sharing
Bookmarks
- Bookmark Post: Save post for later viewing
- Remove Bookmark: Unsave post
- View Bookmarks: Access all saved posts
- Collections: Organize bookmarks into named collections/folders
- Create collection
- Add post to collection
- Remove post from collection
- Delete collection
4. Polls
Polls are embedded within posts.
Poll Features:
- Multiple choice options (2-10 options)
- Single vote or multiple votes allowed
- Vote duration/expiry time
- Vote anonymity settings
- Real-time result updates
Poll Actions:
- Create Poll: Define options and settings during post creation
- Vote: Cast vote on poll option(s)
- Change Vote: Modify vote before poll expires
- View Results: See voting statistics
- Percentage per option
- Total votes
- Time remaining
- Voter list (if not anonymous)
5. Direct Messaging (DMs)
Basic Messaging
- Send Message: Text, images, videos, links
- Edit Message: Modify sent message (within time window)
- Delete Message: Remove message (for self or everyone)
- React to Message: Emoji reactions
- Reply to Message: Quote/reply to specific message in thread
- Forward Message: Send message to another conversation
Conversation Management
- One-on-One Chat: Private conversation between two users
- Mark as Read/Unread: Message read status
- Search Messages: Full-text search within conversations
- Mute Conversation: Disable notifications for conversation
- Block in DMs: Prevent messaging from specific user
- Delete Conversation: Remove conversation thread
Product-Context Messaging
Unique feature for commerce integration:
- Contact from Product: Initiate message from product page
- Product Banner: Message includes product image, name, price
- Product Context: Conversation maintains product reference
- Quick Actions: Direct links to product, shop, or seller profile
Use Case Example:
User sees a product → Clicks "Contact Seller"
→ DM opens with product banner at top
→ Conversation starts with product context
→ Easy reference throughout discussion
6. Commerce Integration
Product Linking
- Link Product in Post: Attach product(s) during post creation
- Product Card Display: Show product preview (image, name, price)
- Click-through: Navigate to product page from post
- Multiple Products: Support multiple product links per post
Shop Linking & Tagging
- Shop Mention: Use
$shopnamesyntax in post text - Tag Shop: Explicitly tag shop in post metadata
- Shop Card Display: Show shop preview when linked
- Navigate to Shop: Click-through to shop page
Event Linking
- Link Event in Post: Attach event during post creation
- Event Card Display: Show event preview (image, title, date, location)
- Event Status: Display ticket availability, price range
- Click-through: Navigate to event page from post
Buy-Together Group Linking
Features:
- Link Group in Post: Attach active buy-together group during post creation
- Group Card Display: Show group preview with:
- Product image and name
- Group target price (discounted price when full)
- Current participants count / Total spots
- Time remaining to fill group
- Savings amount vs individual purchase
- "Join Group" CTA button
- Group Status Indicators:
- Open (spots available)
- Almost Full (1-2 spots left)
- Full (no spots available)
- Expired (time ran out)
- Click-through: Navigate to group detail page
- Real-time Updates: Group status updates as people join
Use Case:
User creates buy-together group for iPhone 15
→ User shares group in post: "Join my iPhone group! Only 2 spots left, save $100!"
→ Followers see post with group card showing discount and spots
→ Click "Join Group" → Navigate to group page to complete joining
→ Group fills up → Post updates to show "Group Full"
Installment Plan Linking
Users can showcase flexible payment options for products in their posts.
Features:
- Link Installment Plan in Post: Attach specific payment plan(s) during post creation
- Multiple Plans Support: Product can have different installment options (3 months, 6 months, 12 months)
- Plan Card Display: Show plan preview with:
- Product image and name
- Plan duration (e.g., "6 months")
- Monthly payment amount
- Total price (may include interest)
- Interest rate (if applicable)
- Down payment (if required)
- "Buy with Plan" CTA button
- Plan Comparison: If multiple plans linked, show side-by-side comparison
- Eligibility Badge: Show if user pre-qualifies for plan
- Click-through: Navigate to product page with plan pre-selected
Use Case:
Shop owner selling laptop at $1200
→ Has 3 installment plans: 3mo ($400/mo), 6mo ($200/mo), 12mo ($100/mo)
→ Creates post: "Get this gaming laptop! Pay as low as $100/month 💳"
→ Links 6-month and 12-month plans
→ Post shows both plans with monthly amounts
→ User clicks → Taken to checkout with 6mo plan selected
Integrated Discovery
Posts with linked products/shops/events/groups/plans appear in:
- Regular feed (from followed users)
- Explore/discover feed
- Search results (filtered by product/shop/event/group/plan)
- Shop pages (posts mentioning the shop)
- Product pages (posts featuring the product)
- Event pages (posts about the event)
- Buy-together group pages (posts promoting the group)
- Installment plan searches (posts showcasing payment options)
7. Hashtags & Discovery
Hashtags
- Create/Use Hashtag: Add
#hashtagto posts - Search by Hashtag: View all posts with specific hashtag
- Trending Hashtags: Most used hashtags in time period
- Follow Hashtag: See posts with hashtag in feed (optional feature)
- Hashtag Count: Number of posts using hashtag
Lists (Curated Feeds)
- Create List: Named collection of users
- Add User to List: Include user in list
- Remove from List: Exclude user from list
- View List Feed: See posts only from list members
- Private/Public Lists: Control list visibility
Search & Explore
- Search Users: Find users by username, name
- Search Posts: Full-text search in post content
- Search Hashtags: Find relevant hashtags
- Search Products: Find posts with specific products
- Search Shops: Find posts mentioning shops
- Search Events: Find posts about events
- Search Buy-Together Groups: Find posts promoting active groups
- Search Installment Plans: Find posts with payment plans
- Explore Feed: Algorithm-free discovery (recent/trending posts)
- Suggested Users: Recommended follows based on connections/interests
8. User Profile
Profile Information
- Username: Unique identifier
- Display Name: Public-facing name
- Bio: Text description (max 150-250 chars)
- Profile Picture: Avatar image
- Cover Photo: Header image
- Location: City/country (optional)
- Website: External link (optional)
- Join Date: Account creation date
Profile Stats
- Posts Count: Total published posts
- Followers Count: Total followers
- Following Count: Total following
- Likes Received: Aggregate likes across all posts (optional)
Profile Actions
- View Profile: Display user's profile and posts
- Edit Profile: Update profile information
- Account Privacy: Toggle public/private
- Verification Badge: For verified accounts (if applicable)
9. Notifications
Users receive notifications for various activities:
Social Interactions
- User followed you
- User liked your post
- User commented on your post
- User replied to your comment
- User mentioned you in post
- User mentioned you in comment
- User tagged you in post
- User tagged you in image
- User reposted your post
- User quote posted your post
- User invited you to collaborate on a post
- User accepted your collaboration invite
- User declined your collaboration invite
- Collaborator edited your shared post
Direct Messages
- New message received
- Someone started conversation with product context
Commerce-Related
- User liked post with your product
- User commented on post with your product
- User shared post with your product
- Shop you tagged shared/liked your post
Notification Settings
- Push Notifications: Mobile/browser push
- In-App Notifications: Notification center
- Email Notifications: Email digest (optional)
- Notification Preferences: Granular control per notification type
10. Moderation & Safety
Reporting
- Report Post: Flag inappropriate content
- Spam
- Harassment
- Misinformation
- Inappropriate content
- Intellectual property violation
- Other (with description)
- Report User: Flag user account
- Report Comment: Flag inappropriate comment
Content Control
- Hide Post: Remove post from your feed without reporting
- Not Interested: Signal to reduce similar content (if using algorithm)
- Mute Keywords: Hide posts containing specific keywords
Account Actions
- Block User: Comprehensive blocking
- Can't see your posts
- Can't follow you
- Can't message you
- Can't tag you
- Can't mention you
- Unblock User: Restore interaction ability
- Restrict User: Limited interaction (shadow restriction)
API Endpoints
Authentication
POST /api/auth/register - Register new user
POST /api/auth/login - Login user
POST /api/auth/logout - Logout user
POST /api/auth/refresh-token - Refresh access token
POST /api/auth/forgot-password - Request password reset
POST /api/auth/reset-password - Reset password
User Profile
GET /api/users/:username - Get user profile
GET /api/users/:id - Get user by ID
PATCH /api/users/:id - Update user profile
GET /api/users/:id/posts - Get user's posts
GET /api/users/:id/followers - Get followers list
GET /api/users/:id/following - Get following list
GET /api/users/:id/stats - Get user statistics
Follow System
POST /api/users/:id/follow - Follow user
DELETE /api/users/:id/unfollow - Unfollow user
GET /api/follows/requests - Get pending follow requests (private accounts)
POST /api/follows/:id/accept - Accept follow request
POST /api/follows/:id/decline - Decline follow request
Block & Mute
POST /api/users/:id/block - Block user
DELETE /api/users/:id/unblock - Unblock user
POST /api/users/:id/mute - Mute user
DELETE /api/users/:id/unmute - Unmute user
GET /api/users/blocked - Get blocked users list
GET /api/users/muted - Get muted users list
Posts
POST /api/posts - Create post
GET /api/posts/:id - Get single post
PATCH /api/posts/:id - Edit post
DELETE /api/posts/:id - Delete post
GET /api/posts/:id/likes - Get post likes
GET /api/posts/:id/comments - Get post comments
GET /api/posts/:id/reposts - Get post reposts
GET /api/posts - Get feed (from followed users)
GET /api/posts/explore - Get explore/discover feed
GET /api/posts/drafts - Get user's draft posts
POST /api/posts/:id/publish - Publish draft post
Post Interactions
POST /api/posts/:id/like - Like post
DELETE /api/posts/:id/unlike - Unlike post
POST /api/posts/:id/repost - Repost (simple repost)
POST /api/posts/:id/quote - Quote post (with text)
DELETE /api/posts/:id/unrepost - Remove repost/quote
POST /api/posts/:id/bookmark - Bookmark post
DELETE /api/posts/:id/unbookmark - Remove bookmark
POST /api/posts/:id/hide - Hide post from feed
POST /api/posts/:id/report - Report post
Image Tags
POST /api/posts/:id/media/:mediaId/tags - Add tag to image
DELETE /api/posts/:id/media/:mediaId/tags/:tagId - Remove image tag
GET /api/posts/:id/media/:mediaId/tags - Get all tags on image
Comments
POST /api/posts/:id/comments - Add comment to post
GET /api/comments/:id - Get single comment
PATCH /api/comments/:id - Edit comment
DELETE /api/comments/:id - Delete comment
POST /api/comments/:id/like - Like comment
DELETE /api/comments/:id/unlike - Unlike comment
POST /api/comments/:id/reply - Reply to comment
GET /api/comments/:id/replies - Get comment replies
POST /api/comments/:id/report - Report comment
Polls
POST /api/polls/:id/vote - Vote on poll
PATCH /api/polls/:id/vote - Change vote
GET /api/polls/:id/results - Get poll results
POST /api/polls/:id/close - Close poll early (creator only)
Bookmarks & Collections
GET /api/bookmarks - Get all bookmarks
POST /api/bookmarks/collections - Create bookmark collection
GET /api/bookmarks/collections - Get all collections
GET /api/bookmarks/collections/:id - Get collection bookmarks
PATCH /api/bookmarks/collections/:id - Update collection
DELETE /api/bookmarks/collections/:id - Delete collection
POST /api/bookmarks/:id/move - Move bookmark to collection
Hashtags
GET /api/hashtags/:tag - Get hashtag info
GET /api/hashtags/:tag/posts - Get posts with hashtag
GET /api/hashtags/trending - Get trending hashtags
POST /api/hashtags/:tag/follow - Follow hashtag
DELETE /api/hashtags/:tag/unfollow - Unfollow hashtag
Lists
POST /api/lists - Create list
GET /api/lists - Get user's lists
GET /api/lists/:id - Get single list
PATCH /api/lists/:id - Update list
DELETE /api/lists/:id - Delete list
POST /api/lists/:id/members - Add user to list
DELETE /api/lists/:id/members/:userId - Remove user from list
GET /api/lists/:id/feed - Get list feed (posts from members)
Direct Messages
GET /api/conversations - Get user's conversations
POST /api/conversations - Create new conversation
GET /api/conversations/:id - Get conversation details
DELETE /api/conversations/:id - Delete conversation
POST /api/conversations/:id/mute - Mute conversation
DELETE /api/conversations/:id/unmute - Unmute conversation
GET /api/conversations/:id/messages - Get conversation messages
POST /api/conversations/:id/messages - Send message
PATCH /api/messages/:id - Edit message
DELETE /api/messages/:id - Delete message
POST /api/messages/:id/reactions - Add reaction to message
DELETE /api/messages/:id/reactions/:emoji - Remove reaction
POST /api/conversations/:id/read - Mark conversation as read
Product Context Messaging
POST /api/products/:id/contact - Initiate message with product context
GET /api/messages/:id/product-context - Get product context for message
Notifications
GET /api/notifications - Get user notifications
GET /api/notifications/unread-count - Get unread count
PATCH /api/notifications/:id/read - Mark notification as read
PATCH /api/notifications/read-all - Mark all as read
DELETE /api/notifications/:id - Delete notification
PATCH /api/notifications/settings - Update notification preferences
Search
GET /api/search/users?q=:query - Search users
GET /api/search/posts?q=:query - Search posts
GET /api/search/hashtags?q=:query - Search hashtags
GET /api/search/products?q=:query - Search posts with products
GET /api/search/shops?q=:query - Search posts with shops
GET /api/search/events?q=:query - Search posts with events
GET /api/search/groups?q=:query - Search posts with buy-together groups
GET /api/search/installment-plans?q=:query - Search posts with installment plans
GET /api/search/all?q=:query - Search across all types
Reports & Moderation
POST /api/reports - Submit report
GET /api/reports - Get user's reports (admin only)
PATCH /api/reports/:id - Update report status (admin only)
Business Logic
Feed Generation
Personal Feed (Home Feed)
Algorithm: Reverse Chronological (no complex algorithm for v1)
Logic:
1. Get list of users current user follows
2. Exclude blocked users
3. Exclude muted users
4. Fetch posts from followed users
5. Include reposts from followed users
6. Sort by published_at DESC
7. Apply pagination (cursor-based recommended)
8. Return posts with engagement counts
Optimizations:
- Cache follower list
- Use database indexes on (author_id, published_at)
- Consider read replicas for heavy read operations
Explore Feed
Logic:
1. Fetch recent posts from public accounts
2. Exclude posts from users you follow (already in home feed)
3. Exclude blocked/muted users
4. Optionally: boost posts with higher engagement
5. Optionally: filter by interests/hashtags
6. Sort by published_at DESC or trending_score
7. Apply pagination
v1 Recommendation: Simple recent public posts
v2: Add trending/engagement scoring
List Feed
Logic:
1. Get list members
2. Fetch posts from list members only
3. Sort by published_at DESC
4. Apply pagination
Hashtag Feed
Logic:
1. Fetch posts containing specific hashtag
2. Sort by published_at DESC
3. Apply pagination
Privacy & Visibility Rules
Post Visibility
Rules:
1. Public posts: Visible to everyone
2. Followers-only posts: Visible only to followers
3. Private posts: Visible only to mentioned users (if implemented)
Checks before showing post:
- Is viewer blocked by author? → Hide
- Is author blocked by viewer? → Hide
- Is author muted by viewer? → Hide (optional, depends on UX)
- Is account private and viewer not a follower? → Hide
- Is post status = published? → Show
Follow Request Logic (Private Accounts)
Flow:
1. User A clicks "Follow" on User B (private account)
2. Create Follow record with status = "pending"
3. Send notification to User B
4. User B approves/declines request
5. If approved: status = "accepted", User A sees User B's posts
6. If declined: Delete Follow record
Engagement Counters
Counter Management Strategy
Approach: Cached Counters with Periodic Sync
Counters to cache:
- posts.likes_count
- posts.comments_count
- posts.reposts_count
- comments.likes_count
- comments.replies_count
- users.followers_count
- users.following_count
- users.posts_count
Update Strategy:
1. On engagement action (like, comment, etc.):
- Increment/decrement counter in database (same transaction)
- Return updated count to client
2. Consistency:
- Use database triggers or application-level transactions
- Periodic reconciliation job to fix drift (run nightly)
3. Performance:
- Index counter columns for sorting/filtering
- Consider Redis cache for high-traffic posts
Notification Logic
Notification Triggers
When to create notification:
1. User followed: follower → followee
2. Post liked: liker → post author (exclude self-likes)
3. Post commented: commenter → post author (exclude self-comments)
4. Comment replied: replier → parent comment author
5. User mentioned in post: post author → mentioned user
6. User mentioned in comment: commenter → mentioned user
7. User tagged in post: post author → tagged user
8. User tagged in image: tagger → tagged user
9. Post reposted: reposter → original post author
10. Post quote-posted: quote poster → original post author
Batch Notifications:
- "X and 10 others liked your post" (if multiple users like same post)
- Group similar notifications within time window
Notification Delivery
Channels:
1. In-app: Store in notifications table, show in notification center
2. Push: Send to mobile/browser if user has push enabled
3. Email: Send digest email based on user preferences
Delivery Logic:
- Check user's notification preferences
- Respect mute/block relationships
- Don't notify for actions on muted content
- Rate limit notifications per user (prevent spam)
Message Delivery
Real-time Messaging
Technology: WebSockets or Server-Sent Events (SSE)
Flow:
1. User connects to WebSocket server
2. Authenticate connection
3. Subscribe to user's conversation channels
4. On new message:
- Persist to database
- Broadcast to conversation participants via WebSocket
- Send push notification if recipient offline
- Update conversation.last_message_at
Message Status:
- Sent: Message persisted to database
- Delivered: Recipient connected and received message
- Read: Recipient opened conversation and viewed message
Product Context Messages
Flow:
1. User on product page clicks "Contact Seller"
2. Check if conversation exists between user and seller
3. If exists: Open existing conversation
4. If not: Create new conversation
5. Create message with:
- message_type = "product_context"
- product_id = current product ID
- content = optional user text or default template
6. Conversation UI shows product banner at top
7. Product info (image, name, price) displayed persistently
Template:
"Hi, I'm interested in [Product Name]. Is this still available?"
Search Implementation
Full-Text Search
Technology Options:
1. PostgreSQL Full-Text Search (simple, built-in)
2. Elasticsearch (powerful, scalable, recommended for production)
3. Algolia (managed, fast, but paid)
Search Scope:
- Users: username, display_name, bio
- Posts: content, hashtags
- Products: name, description (if linked)
- Shops: name, description (if linked)
- Events: title, description (if linked)
Ranking Factors:
- Relevance score
- Recency (for posts)
- Engagement (likes, comments) for popular content
- User's network (prioritize followed users)
Implementation with Elasticsearch:
1. Index documents on create/update
2. Use multi-field search (content, hashtags, etc.)
3. Apply filters (date range, post type, engagement threshold)
4. Return results with highlights
Content Moderation
Automated Moderation (Future)
Techniques:
1. Keyword filtering (offensive words, spam patterns)
2. Image moderation (AI-based NSFW detection)
3. Rate limiting (prevent spam posting)
4. URL reputation checking (malicious links)
Implementation:
- Run checks on post creation
- Flag suspicious content for review
- Auto-hide or require manual approval
Manual Moderation
Workflow:
1. User reports content
2. Report enters moderation queue
3. Moderator reviews report
4. Actions:
- Dismiss: No violation
- Warn user: Send warning notification
- Remove content: Soft delete post/comment
- Suspend user: Temporary ban
- Ban user: Permanent account ban
Tools:
- Admin dashboard for reports
- Content review interface
- User management panel
Integration Points
eCommerce Integration
Product Linking
Data Flow:
1. During post creation, user searches products
2. API call to Product Service: GET /api/products/search?q=:query
3. Return product list with: id, name, image, price, shop_id
4. User selects products to link
5. On post save, create PostProduct records
6. On post display, fetch product details and show product cards
Product Card Display:
- Product image (thumbnail)
- Product name
- Price
- Shop name
- "View Product" CTA button
- Click → Navigate to product detail page
Shop Mentions/Tags
Shop Mention Parsing ($shopname):
1. On post creation, parse content for $shopname pattern
2. Validate shop exists: Query Shop Service
3. Create PostShop records with mention_type = "mention"
4. On post display, convert $shopname to clickable link
Shop Tagging:
1. User explicitly tags shop (separate from content)
2. Create PostShop record with mention_type = "tag"
3. Shop sees "tagged posts" feed
Shop Profile Integration:
- Show posts where shop is tagged/mentioned
- "Posts about this shop" section on shop page
Event Linking
Data Flow:
1. During post creation, user searches events
2. API call to Event Service: GET /api/events/search?q=:query
3. Return event list with: id, title, date, location, image, ticket_status
4. User selects events to link
5. On post save, create PostEvent records
6. On post display, fetch event details and show event cards
Event Card Display:
- Event image
- Event title
- Date & time
- Location
- Ticket availability status
- "View Event" CTA button
- Click → Navigate to event detail page
Buy-Together Group Linking
Data Flow:
1. During post creation, user searches active buy-together groups (own or others they've joined)
2. API call to BuyTogether Service: GET /api/buy-together/groups?status=active&user=:userId
3. Return group list with: id, product_info, current_count, total_slots, target_price, expires_at
4. User selects group(s) to promote
5. On post save, create PostBuyTogetherGroup records
6. On post display, fetch real-time group status and show group cards
Group Card Display:
- Product image
- Product name with group label "Buy Together 🤝"
- Progress bar: "3/5 spots filled"
- Target price with discount badge: "$800 (Save $100!)"
- Time remaining: "2 days left"
- Status indicator: Open/Almost Full/Full/Expired
- "Join Group" CTA button (disabled if full/expired)
- Click → Navigate to group detail page
Real-Time Updates:
- WebSocket connection for live group status updates
- When spots fill up, button changes to "Group Full"
- When time expires, entire card grays out with "Expired" badge
- Post feed auto-refreshes group status every 30 seconds
Boost Strategy:
User creates group → Shares on social feed → Friends see discount → Join group → Group fills faster
Installment Plan Linking
Data Flow:
1. During post creation, user searches products with installment plans
2. API call to Product Service: GET /api/products/:id/installment-plans
3. Return available plans for product: id, duration_months, monthly_amount, total_cost, interest_rate, down_payment
4. User selects which plan(s) to feature (can select multiple for comparison)
5. On post save, create PostInstallmentPlan records
6. On post display, fetch plan details and show plan cards
Single Plan Display:
- Product image
- Product name
- "Pay in installments 💳" badge
- Monthly payment: "$99/month"
- Plan duration: "6 months"
- Total cost (if different from base price): "$594 total"
- Interest rate (if applicable): "0% interest" or "5% APR"
- "Buy with Plan" CTA button
- Click → Navigate to product page with plan pre-selected
Multiple Plans Comparison Display:
- Product image at top
- Side-by-side plan cards:
[3 Months] [6 Months] [12 Months]
$200/mo $100/mo $50/mo
$600 total $600 total $650 total
0% interest 0% interest 5% APR
[Select] [Select] [Select]
- Each card clickable to product page with that plan
Marketing Use Cases:
- Shop owner: "Get this laptop for just $99/month! 💳"
- Influencer: "No need to pay all at once! Check out these flexible plans"
- User sharing deal: "Found this phone with 0% financing!"
Integration with Checkout:
- When user clicks "Buy with Plan"
- Deep link to product page: /products/:id?plan=:planId
- Checkout page has plan pre-selected
- User completes purchase with chosen installment plan
Notification Integration
Cross-Service Notifications
Scenario 1: User likes a post that has a linked product
Notification Recipients:
1. Post author: "X liked your post"
2. Product owner (if different from post author): "X liked a post featuring your product"
Scenario 2: User joins a buy-together group from a post
Notification Recipients:
1. Post author: "X joined your buy-together group from your post!"
2. Group creator (if different): "X joined your group"
3. Other group members: "X joined the group - 1 more spot to go!"
Scenario 3: Buy-together group gets filled from post shares
Notification to:
- Post author: "Your group is full! 🎉 Your post helped fill 3 spots"
- All group members: "Group is full! Order will be placed soon"
Scenario 4: User clicks installment plan from post
Analytics Event (not push notification):
- Track which posts drive installment plan conversions
- Shop owner can see: "5 users viewed installment plans from your post"
Implementation:
- Social media service creates Like/Share/Join record
- Triggers notification service
- Notification service:
- Checks PostProduct for linked products
- Identifies product owner via Product Service
- Creates notifications for both post author and product owner
Notifications System
Notification Types & Templates
Social Notifications
Type: FOLLOW
Template: "{actor_name} started following you"
Action: View {actor_name}'s profile
Type: LIKE_POST
Template: "{actor_name} liked your post"
Action: View post
Type: COMMENT_POST
Template: "{actor_name} commented on your post: '{comment_preview}'"
Action: View post/comment
Type: REPLY_COMMENT
Template: "{actor_name} replied to your comment: '{reply_preview}'"
Action: View comment thread
Type: MENTION_POST
Template: "{actor_name} mentioned you in a post"
Action: View post
Type: MENTION_COMMENT
Template: "{actor_name} mentioned you in a comment"
Action: View comment
Type: TAG_POST
Template: "{actor_name} tagged you in a post"
Action: View post
Type: TAG_IMAGE
Template: "{actor_name} tagged you in an image"
Action: View image/post
Type: REPOST
Template: "{actor_name} reposted your post"
Action: View repost
Type: QUOTE_POST
Template: "{actor_name} quoted your post: '{quote_preview}'"
Action: View quote post
Type: COLLABORATION_INVITE
Template: "{actor_name} invited you to collaborate on a post"
Action: View post to accept/decline
Type: COLLABORATION_ACCEPTED
Template: "{actor_name} accepted your collaboration invite"
Action: View collaborative post
Type: COLLABORATION_DECLINED
Template: "{actor_name} declined your collaboration invite"
Action: Dismiss notification
Type: COLLABORATOR_EDITED_POST
Template: "{actor_name} edited your collaborative post"
Action: View post changes
Messaging Notifications
Type: NEW_MESSAGE
Template: "New message from {actor_name}"
Action: View conversation
Type: PRODUCT_MESSAGE
Template: "{actor_name} messaged you about {product_name}"
Action: View conversation with product context
Commerce Notifications
Type: PRODUCT_POST_LIKE
Template: "{actor_name} liked a post featuring your product '{product_name}'"
Action: View post
Type: PRODUCT_POST_COMMENT
Template: "{actor_name} commented on a post featuring your product"
Action: View post/comment
Type: SHOP_TAGGED
Template: "{actor_name} tagged your shop in a post"
Action: View post
Notification Batching
Batching Strategy
Rules:
- Same type + same entity within 5 minutes → Batch
- Max 100 actors in batch
- Update existing notification instead of creating new
Example:
Instead of:
- "Alice liked your post"
- "Bob liked your post"
- "Charlie liked your post"
Show:
- "Alice, Bob, Charlie and 10 others liked your post"
Implementation:
- Use transaction to check for recent similar notifications
- Update existing notification's actor list
- Increment count
- Update timestamp to latest
Notification Preferences
Preference Options
Categories:
1. Social Interactions
- Follows
- Likes
- Comments
- Mentions
- Tags
- Reposts
- Collaborations
2. Messages
- Direct messages
- Message reactions
3. Commerce
- Product interactions
- Shop tags
Settings per category:
- Push: ON/OFF
- In-app: ON/OFF (always ON for critical notifications)
- Email: ON/OFF/DIGEST (daily/weekly)
- Frequency: All / Important only / None
Default:
- All push notifications: ON
- All in-app: ON
- Email: DIGEST (daily)
Data Privacy
Sensitive Data Handling
Personal Information:
- Email: Never exposed in API responses (except to owner)
- Password: Always hashed (bcrypt, Argon2)
- Profile: Respect account privacy (public/private)
Data Retention:
- Deleted posts: Soft delete, retain for 30 days, then hard delete
- Deleted messages: Soft delete, retain for 90 days for legal compliance
- Deleted accounts: Anonymize after 30 days, hard delete after 90 days
Export & Deletion (GDPR):
- User data export: Provide JSON export of all user data
- Right to be forgotten: Delete all user content and personal info
Privacy Controls
Block Functionality
When User A blocks User B:
- B cannot see A's profile or posts
- B cannot follow A
- B cannot message A
- B cannot comment on A's posts
- B cannot tag or mention A
- Existing follows are removed
- B is not notified of the block
Implementation:
- Before showing content, check if viewer is blocked
- Before allowing action, check if actor is blocked
Mute Functionality
When User A mutes User B:
- B's posts don't appear in A's feed
- B's comments on others' posts may still appear (optional)
- B is not notified of the mute
- A can still see B's profile if directly visited
Implementation:
- Filter out muted users in feed queries
- Store mute relationship in Mute table
Conclusion
This documentation provides a complete blueprint for implementing a social media platform integrated with eCommerce and events. The architecture is designed to scale from MVP to millions of users while maintaining code quality and user experience.
User Relations Endpoints
Account Privacy APIs
Base URL: https://api.nexgate.com/api/v1
Short Description: The Account Privacy API allows users to manage their account privacy settings, controlling whether their account is private or public. Private accounts require follow request approval before users can see their content and follow them.
Hints:
- All endpoints require authentication via Bearer token
- Privacy settings are automatically created with default values (public account) on first access
- Changing privacy settings does not affect existing followers
- Private accounts require follow request approval for new followers
Standard Response Format
All API responses follow a consistent structure using our Globe Response Builder pattern:
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-09-23T10:30:45",
"data": {
// Actual response data goes here
}
}
Error Response Structure
{
"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 - GET - Green (Safe, read-only operations)
- POST - POST - Blue (Create new resources)
- PUT - PUT - Yellow (Update/replace entire resource)
- PATCH - PATCH - Orange (Partial updates)
- DELETE - DELETE - Red (Remove resources)
Endpoints
1. Get Privacy Settings
Purpose: Retrieve the current privacy settings for the authenticated user's account
Endpoint: GET {base_url}/e-social/privacy/account
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:
{
"success": true,
"httpStatus": "OK",
"message": "Privacy settings retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": "123e4567-e89b-12d3-a456-426614174000",
"isPrivate": false,
"createdAt": "2025-01-15T08:20:30",
"updatedAt": "2025-12-11T10:30:45"
}
}
Success Response Fields:
| Field | Description |
|---|---|
| id | Unique identifier for the privacy settings record (UUID format) |
| userId | Unique identifier of the user who owns these settings (UUID format) |
| isPrivate | Boolean flag indicating if the account is private (true) or public (false) |
| createdAt | Timestamp when the privacy settings were first created (ISO 8601 format) |
| updatedAt | Timestamp when the privacy settings were last modified (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Authentication token is required",
"action_time": "2025-12-11T10:30:45",
"data": "Authentication token is required"
}
2. Update Privacy Settings
Purpose: Update the privacy settings for the authenticated user's account (switch between private and public)
Endpoint: PUT {base_url}/e-social/privacy/account
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:
{
"isPrivate": true
}
Request Body Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
| isPrivate | boolean | Yes | Flag to set account as private (true) or public (false) | Must not be null |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Privacy settings updated successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": "123e4567-e89b-12d3-a456-426614174000",
"isPrivate": true,
"createdAt": "2025-01-15T08:20:30",
"updatedAt": "2025-12-11T10:30:45"
}
}
Success Response Fields:
| Field | Description |
|---|---|
| id | Unique identifier for the privacy settings record (UUID format) |
| userId | Unique identifier of the user who owns these settings (UUID format) |
| isPrivate | Boolean flag indicating if the account is private (true) or public (false) |
| createdAt | Timestamp when the privacy settings were first created (ISO 8601 format) |
| updatedAt | Timestamp when the privacy settings were last modified (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "UNPROCESSABLE_ENTITY",
"message": "Validation failed",
"action_time": "2025-12-11T10:30:45",
"data": {
"isPrivate": "must not be null"
}
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
Validation Error (422):
{
"success": false,
"httpStatus": "UNPROCESSABLE_ENTITY",
"message": "Validation failed",
"action_time": "2025-12-11T10:30:45",
"data": {
"isPrivate": "must not be null"
}
}
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
Quick Reference Guide
Common HTTP Status Codes
200 OK: Successful GET/PUT request401 Unauthorized: Authentication required/failed422 Unprocessable Entity: Validation errors500 Internal Server Error: Server error
Authentication
- Bearer Token: Include
Authorization: Bearer your_tokenin headers
Data Format Standards
- Dates: Use ISO 8601 format (2025-12-11T14:30:00Z)
- IDs: UUID format (e.g., 550e8400-e29b-41d4-a716-446655440000)
Follow System APIs
Base URL: https://api.nexgate.com/api/v1
Short Description: The Follow System API manages user relationships including following/unfollowing users, handling follow requests for private accounts, retrieving followers and following lists, and discovering featured users. It supports both immediate follows (public accounts) and pending follow requests (private accounts). The system intelligently tracks mutual relationships and provides contextual follow/unfollow buttons based on the authenticated user's relationship with other users.
Hints:
- All endpoints require authentication via Bearer token
- Following a private account creates a PENDING follow request that requires acceptance
- Following a public account creates an ACCEPTED follow immediately
- Featured users are sorted by follower count and exclude users you already follow
- Pagination is 1-indexed (page=1 for first page)
- Default page size is 20 items
- Follower/Following lists include relationship indicators (
followsYou,youFollow) for smart button rendering
How The Follow System Works
Relationship Context Logic
The Follow System provides intelligent relationship tracking between the authenticated user (you) and listed users across different profile views:
1. Your Own Followers List (/followers/{yourUserId})
- Shows users who follow you
- Each follower includes:
followsYou: Alwaystrue(they follow you)youFollow: Whether you follow them back
- Button Logic:
youFollow: false→ Show "Follow Back" buttonyouFollow: true→ Show "Following" button
2. Your Own Following List (/following/{yourUserId})
- Shows users you follow
- Each user includes:
followsYou: Whether they follow you back (mutual relationship)youFollow: Alwaystrue(you follow them)
- UI Logic:
followsYou: true→ Display "Follows You" badge- Always show "Following" button (can unfollow)
3. Friend's Followers List (/followers/{friendUserId})
- Shows users who follow your friend
- Each follower includes:
followsYou: Whether this follower follows you (authenticated user)youFollow: Whether you follow this follower
- Button Logic:
followsYou: trueANDyouFollow: false→ Show "Follow Back"youFollow: true→ Show "Following"- Both
false→ Show "Follow"
4. Friend's Following List (/following/{friendUserId})
- Shows users your friend follows
- Each user includes:
followsYou: Whether this user follows you (authenticated user)youFollow: Whether you follow this user
- Button Logic:
followsYou: trueANDyouFollow: false→ Show "Follow Back"youFollow: true→ Show "Following"- Both
false→ Show "Follow"
Privacy & Status Rules
Private Accounts:
- Follow requests enter PENDING status
- Require manual acceptance by the account owner
- Can be accepted or declined
Public Accounts:
- Follows are immediately ACCEPTED
- No manual approval needed
Blocking Behavior:
- Blocking a user removes all follow relationships (bidirectional)
- Blocked users cannot follow you or see your content
Key Principle: All relationship indicators (followsYou, youFollow) are always calculated relative to the authenticated user, regardless of whose profile is being viewed.
UI Implementation Best Practices
Button State Management
The follow system uses a single button that changes state based on the relationship context. This follows industry standards (Twitter/X, Instagram, LinkedIn).
Button Logic by Relationship State:
if (youFollow) {
// Button: "Following"
// Action: Click to unfollow (with confirmation)
// Style: Secondary/outlined button
} else if (followsYou) {
// Button: "Follow Back"
// Action: Click to follow immediately (no confirmation)
// Style: Primary/solid button
} else {
// Button: "Follow"
// Action: Click to follow immediately (no confirmation)
// Style: Primary/solid button
}
Why Single Button Design:
✅ Advantages:
- Clean, uncluttered interface
- Clear current state indication
- Industry-standard pattern
- Saves screen space
- Intuitive user experience
❌ Avoid:
- Separate "Unfollow" button (clutters UI)
- Multiple buttons for same relationship
- Ambiguous action labels
Confirmation Dialog Best Practices
Show confirmation ONLY for destructive actions:
Unfollow Action (Destructive):
User clicks "Following" button
↓
Show confirmation modal:
"Unfollow @username?"
[Cancel] [Unfollow]
↓
On confirm: Call DELETE /e-social/follows/{userId}
Follow/Follow Back Actions (Constructive):
User clicks "Follow" or "Follow Back" button
↓
Immediately call POST /e-social/follows/{userId}
No confirmation needed
Rationale:
- Unfollowing removes an existing connection (requires confirmation)
- Following creates a new connection (no barrier needed)
- Matches user expectations from major platforms
Visual States & Feedback
| State | Label | Style | On Click |
|---|---|---|---|
| Not Following | "Follow" | Primary/Solid | Follow immediately |
| Mutual Follow Possible | "Follow Back" | Primary/Solid | Follow immediately |
| Currently Following | "Following" | Secondary/Outlined | Show unfollow confirmation |
| Pending Request | "Requested" | Secondary/Disabled | Cancel request (optional) |
Loading States:
- Show spinner on button during API call
- Disable button to prevent double-clicks
- Optimistic UI update (change button immediately, rollback on error)
Error Handling:
- Show toast/snackbar on API errors
- Revert button state on failure
- Clear error messages (e.g., "Failed to follow user. Try again.")
Additional UI Elements
Badges:
- Show "Follows You" badge when
followsYou: truein Following list - Display badge near username, not on button
- Use subtle styling (gray text or small pill)
Private Account Indicators:
- Show lock icon for private accounts
- Update button to "Request" for private accounts
- After clicking, change to "Requested" state
Standard Response Format
All API responses follow a consistent structure using our Globe Response Builder pattern:
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-09-23T10:30:45",
"data": {
// Actual response data goes here
}
}
Error Response Structure
{
"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 - Green (Safe, read-only operations)
- POST - Blue (Create new resources)
- DELETE - Red (Remove resources)
Endpoints
1. Follow User
Purpose: Follow a user or create a follow request if the target account is private
Endpoint: POST {base_url}/e-social/follows/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to follow | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User followed successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"id": "660e8400-e29b-41d4-a716-446655440011",
"followerId": "123e4567-e89b-12d3-a456-426614174000",
"followingId": "987e6543-e21b-12d3-a456-426614174999",
"follower": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"userName": "john_doe",
"firstName": "John",
"lastName": "Doe",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/john_doe.jpg"],
"isVerified": true
},
"following": {
"id": "987e6543-e21b-12d3-a456-426614174999",
"userName": "jane_smith",
"firstName": "Jane",
"lastName": "Smith",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/jane_smith.jpg"],
"isVerified": false
},
"status": "ACCEPTED",
"createdAt": "2025-12-11T10:30:45"
}
}
Success Response Fields:
| Field | Description |
|---|---|
| id | Unique identifier for the follow relationship (UUID format) |
| followerId | UUID of the user who initiated the follow |
| followingId | UUID of the user being followed |
| follower | Object containing summary information about the follower user |
| follower.id | UUID of the follower user |
| follower.userName | Username of the follower |
| follower.firstName | First name of the follower |
| follower.lastName | Last name of the follower |
| follower.profilePictureUrls | Array of profile picture URLs for the follower |
| follower.isVerified | Boolean indicating if the follower account is verified |
| following | Object containing summary information about the user being followed |
| following.id | UUID of the user being followed |
| following.userName | Username of the user being followed |
| following.firstName | First name of the user being followed |
| following.lastName | Last name of the user being followed |
| following.profilePictureUrls | Array of profile picture URLs for the user being followed |
| following.isVerified | Boolean indicating if the followed account is verified |
| status | Follow status - "ACCEPTED" for public accounts, "PENDING" for private accounts |
| createdAt | Timestamp when the follow relationship was created (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Cannot follow yourself",
"action_time": "2025-12-11T10:30:45",
"data": "Cannot follow yourself"
}
Standard Error Types:
Application-Level Exceptions (400-499)
400 BAD_REQUEST: Cannot follow yourself, already following this user, or invalid request data401 UNAUTHORIZED: Authentication issues (empty, invalid, expired, or malformed tokens)404 NOT_FOUND: User to follow not found500 INTERNAL_SERVER_ERROR: Unexpected server errors
Error Response Examples:
Bad Request - Self Follow (400):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Cannot follow yourself",
"action_time": "2025-12-11T10:30:45",
"data": "Cannot follow yourself"
}
Bad Request - Already Following (400):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Already following this user",
"action_time": "2025-12-11T10:30:45",
"data": "Already following this user"
}
Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
2. Unfollow User
Purpose: Remove a follow relationship with another user
Endpoint: DELETE {base_url}/e-social/follows/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to unfollow | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User unfollowed successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Always null for this endpoint |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
3. Accept Follow Request
Purpose: Accept a pending follow request from another user
Endpoint: POST {base_url}/e-social/follows/requests/{followId}/accept
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 |
|---|---|---|---|---|
| followId | string | Yes | UUID of the follow request to accept | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Follow request accepted",
"action_time": "2025-12-11T10:30:45",
"data": {
"id": "660e8400-e29b-41d4-a716-446655440011",
"followerId": "987e6543-e21b-12d3-a456-426614174999",
"followingId": "123e4567-e89b-12d3-a456-426614174000",
"follower": {
"id": "987e6543-e21b-12d3-a456-426614174999",
"userName": "jane_smith",
"firstName": "Jane",
"lastName": "Smith",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/jane_smith.jpg"],
"isVerified": false
},
"following": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"userName": "john_doe",
"firstName": "John",
"lastName": "Doe",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/john_doe.jpg"],
"isVerified": true
},
"status": "ACCEPTED",
"createdAt": "2025-12-11T09:15:30"
}
}
Standard Error Types:
Application-Level Exceptions (400-499)
400 BAD_REQUEST: Follow request is not pending401 UNAUTHORIZED: Authentication issues404 NOT_FOUND: Follow request not found500 INTERNAL_SERVER_ERROR: Unexpected server errors
4. Decline Follow Request
Purpose: Decline a pending follow request from another user
Endpoint: DELETE {base_url}/e-social/follows/requests/{followId}/decline
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 |
|---|---|---|---|---|
| followId | string | Yes | UUID of the follow request to decline | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Follow request declined",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
Application-Level Exceptions (400-499)
400 BAD_REQUEST: Follow request is not pending401 UNAUTHORIZED: Authentication issues404 NOT_FOUND: Follow request not found500 INTERNAL_SERVER_ERROR: Unexpected server errors
5. Get Followers
Purpose: Retrieve the complete list of users following a specified user with relationship context
Endpoint: GET {base_url}/e-social/follows/followers/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user whose followers to retrieve | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Followers retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": [
{
"id": "660e8400-e29b-41d4-a716-446655440011",
"user": {
"id": "987e6543-e21b-12d3-a456-426614174999",
"userName": "jane_smith",
"firstName": "Jane",
"lastName": "Smith",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/jane_smith.jpg"],
"isVerified": false
},
"status": "ACCEPTED",
"createdAt": "2025-12-10T14:20:15",
"followsYou": true,
"youFollow": false
},
{
"id": "770e8400-e29b-41d4-a716-446655440022",
"user": {
"id": "111e1111-e11b-11d1-a111-111111111111",
"userName": "bob_wilson",
"firstName": "Bob",
"lastName": "Wilson",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/bob_wilson.jpg"],
"isVerified": true
},
"status": "ACCEPTED",
"createdAt": "2025-12-09T08:45:30",
"followsYou": true,
"youFollow": true
}
]
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Array of follower objects |
| data[].id | Unique identifier for the follow relationship (UUID format) |
| data[].user | Object containing information about the follower user |
| data[].user.id | UUID of the follower user |
| data[].user.userName | Username of the follower |
| data[].user.firstName | First name of the follower |
| data[].user.lastName | Last name of the follower |
| data[].user.profilePictureUrls | Array of profile picture URLs for the follower |
| data[].user.isVerified | Boolean indicating if the follower account is verified |
| data[].status | Follow status - always "ACCEPTED" for this endpoint |
| data[].createdAt | Timestamp when the follow relationship was created (ISO 8601 format) |
| data[].followsYou | NEW: Does this follower follow the authenticated user? |
| data[].youFollow | NEW: Does the authenticated user follow this follower back? |
Standard Error Types:
Application-Level Exceptions (400-499)
6. Get Following
Purpose: Retrieve the complete list of users that a specified user is following with relationship context
Endpoint: GET {base_url}/e-social/follows/following/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user whose following list to retrieve | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Following retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": [
{
"id": "660e8400-e29b-41d4-a716-446655440011",
"user": {
"id": "222e2222-e22b-22d2-a222-222222222222",
"userName": "alice_johnson",
"firstName": "Alice",
"lastName": "Johnson",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/alice_johnson.jpg"],
"isVerified": true
},
"status": "ACCEPTED",
"createdAt": "2025-12-08T16:30:00",
"followsYou": true,
"youFollow": true
}
]
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Array of following objects |
| data[].id | Unique identifier for the follow relationship (UUID format) |
| data[].user | Object containing information about the followed user |
| data[].user.id | UUID of the followed user |
| data[].user.userName | Username of the followed user |
| data[].user.firstName | First name of the followed user |
| data[].user.lastName | Last name of the followed user |
| data[].user.profilePictureUrls | Array of profile picture URLs for the followed user |
| data[].user.isVerified | Boolean indicating if the followed account is verified |
| data[].status | Follow status - always "ACCEPTED" for this endpoint |
| data[].createdAt | Timestamp when the follow relationship was created (ISO 8601 format) |
| data[].followsYou | NEW: Does this user follow the authenticated user back? |
| data[].youFollow | NEW: Does the authenticated user follow this user? |
Standard Error Types:
Application-Level Exceptions (400-499)
7. Get Followers (Paginated)
Purpose: Retrieve a paginated list of users following a specified user with relationship context
Endpoint: GET {base_url}/e-social/follows/followers/{userId}/paged
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user whose followers 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 | Number of items per page | Min: 1, Max: 100 | 20 |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Followers retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"content": [
{
"id": "660e8400-e29b-41d4-a716-446655440011",
"user": {
"id": "987e6543-e21b-12d3-a456-426614174999",
"userName": "jane_smith",
"firstName": "Jane",
"lastName": "Smith",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/jane_smith.jpg"],
"isVerified": false
},
"status": "ACCEPTED",
"createdAt": "2025-12-10T14:20:15",
"followsYou": true,
"youFollow": false
}
],
"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,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"numberOfElements": 1,
"empty": false
}
}
Success Response Fields:
| Field | Description |
|---|---|
| data.content | Array of follower objects for the current page (includes followsYou and youFollow fields) |
| data.content[].followsYou | NEW: Does this follower follow the authenticated user? |
| data.content[].youFollow | NEW: Does the authenticated user follow this follower back? |
| data.pageable | Pagination metadata |
| data.totalPages | Total number of pages available |
| data.totalElements | Total number of followers |
| data.first | Boolean indicating if this is the first page |
| data.last | Boolean indicating if this is the last page |
| data.size | Page size |
| data.number | Current page number (0-indexed) |
| data.numberOfElements | Number of elements in current page |
| data.empty | Boolean indicating if the page is empty |
8. Get Following (Paginated)
Purpose: Retrieve a paginated list of users that a specified user is following with relationship context
Endpoint: GET {base_url}/e-social/follows/following/{userId}/paged
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user whose following list 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 | Number of items per page | Min: 1, Max: 100 | 20 |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Following retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"content": [
{
"id": "660e8400-e29b-41d4-a716-446655440011",
"user": {
"id": "222e2222-e22b-22d2-a222-222222222222",
"userName": "alice_johnson",
"firstName": "Alice",
"lastName": "Johnson",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/alice_johnson.jpg"],
"isVerified": true
},
"status": "ACCEPTED",
"createdAt": "2025-12-08T16:30:00",
"followsYou": true,
"youFollow": true
}
],
"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,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"numberOfElements": 1,
"empty": false
}
}
Success Response Fields:
| Field | Description |
|---|---|
| data.content | Array of following objects for the current page (includes followsYou and youFollow fields) |
| data.content[].followsYou | NEW: Does this user follow the authenticated user back? |
| data.content[].youFollow | NEW: Does the authenticated user follow this user? |
| data.pageable | Pagination metadata |
| data.totalPages | Total number of pages available |
| data.totalElements | Total number of users being followed |
| data.first | Boolean indicating if this is the first page |
| data.last | Boolean indicating if this is the last page |
| data.size | Page size |
| data.number | Current page number (0-indexed) |
| data.numberOfElements | Number of elements in current page |
| data.empty | Boolean indicating if the page is empty |
9. Get Pending Follow Requests
Purpose: Retrieve all pending follow requests for the authenticated user (requests they need to approve)
Endpoint: GET {base_url}/e-social/follows/requests/pending
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:
{
"success": true,
"httpStatus": "OK",
"message": "Pending requests retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": [
{
"id": "660e8400-e29b-41d4-a716-446655440011",
"user": {
"id": "333e3333-e33b-33d3-a333-333333333333",
"userName": "charlie_brown",
"firstName": "Charlie",
"lastName": "Brown",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/charlie_brown.jpg"],
"isVerified": false
},
"status": "PENDING",
"createdAt": "2025-12-11T09:15:30",
"followsYou": false,
"youFollow": false
}
]
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Array of pending follow request objects |
| data[].id | Unique identifier for the follow relationship (UUID format) |
| data[].user | Object containing information about the user requesting to follow |
| data[].status | Follow status - always "PENDING" for this endpoint |
| data[].createdAt | Timestamp when the follow request was created (ISO 8601 format) |
| data[].followsYou | NEW: Does this requester follow the authenticated user? |
| data[].youFollow | NEW: Does the authenticated user follow this requester? |
10. Get User Stats
Purpose: Retrieve follower, following, and pending request counts for a specified user
Endpoint: GET {base_url}/e-social/follows/stats/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user whose stats to retrieve | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User stats retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"userId": "123e4567-e89b-12d3-a456-426614174000",
"followersCount": 1250,
"followingCount": 487,
"pendingRequestsCount": 15
}
}
Success Response Fields:
| Field | Description |
|---|---|
| userId | UUID of the user these stats belong to |
| followersCount | Total number of accepted followers for this user |
| followingCount | Total number of users this user is following (accepted follows only) |
| pendingRequestsCount | Total number of pending follow requests for this user |
11. Check Follow Status
Purpose: Check if the authenticated user is following a specified user
Endpoint: GET {base_url}/e-social/follows/check/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to check follow status for | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Follow status checked",
"action_time": "2025-12-11T10:30:45",
"data": {
"userId": "987e6543-e21b-12d3-a456-426614174999",
"isFollowing": true
}
}
Success Response Fields:
| Field | Description |
|---|---|
| userId | UUID of the user being checked |
| isFollowing | Boolean indicating if the authenticated user is following this user (includes pending and accepted follows) |
12. Get Featured Users
Purpose: Retrieve a list of suggested users to follow, sorted by follower count, excluding users already followed by the authenticated user
Endpoint: GET {base_url}/e-social/follows/featured
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 |
|---|---|---|---|---|---|
| limit | integer | No | Maximum number of featured users to return | Min: 1, Max: 100 | 20 |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Featured users retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": [
{
"id": "555e5555-e55b-55d5-a555-555555555555",
"userName": "tech_guru",
"firstName": "Sarah",
"lastName": "Martinez",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/tech_guru.jpg"],
"isVerified": true,
"followersCount": 125000,
"followsMe": false
},
{
"id": "666e6666-e66b-66d6-a666-666666666666",
"userName": "travel_explorer",
"firstName": "Mike",
"lastName": "Chen",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/travel_explorer.jpg"],
"isVerified": true,
"followersCount": 98500,
"followsMe": true
}
]
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Array of featured user objects |
| data[].id | UUID of the featured user |
| data[].userName | Username of the featured user |
| data[].firstName | First name of the featured user |
| data[].lastName | Last name of the featured user |
| data[].profilePictureUrls | Array of profile picture URLs for the featured user |
| data[].isVerified | Boolean indicating if the featured account is verified |
| data[].followersCount | Total number of followers the featured user has |
| data[].followsMe | Boolean indicating if this featured user follows the authenticated user |
13. Get Featured Users (Paginated)
Purpose: Retrieve a paginated list of suggested users to follow, sorted by follower count, excluding users already followed by the authenticated user
Endpoint: GET {base_url}/e-social/follows/featured/paged
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:
{
"success": true,
"httpStatus": "OK",
"message": "Featured users retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"content": [
{
"id": "555e5555-e55b-55d5-a555-555555555555",
"userName": "tech_guru",
"firstName": "Sarah",
"lastName": "Martinez",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/tech_guru.jpg"],
"isVerified": true,
"followersCount": 125000,
"followsMe": false
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 20,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"offset": 0,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 5,
"totalElements": 87,
"first": true,
"size": 20,
"number": 0,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"numberOfElements": 20,
"empty": false
}
}
Success Response Fields:
| Field | Description |
|---|---|
| data.content | Array of featured user objects for the current page |
| data.content[].id | UUID of the featured user |
| data.content[].userName | Username of the featured user |
| data.content[].firstName | First name of the featured user |
| data.content[].lastName | Last name of the featured user |
| data.content[].profilePictureUrls | Array of profile picture URLs |
| data.content[].isVerified | Boolean indicating if account is verified |
| data.content[].followersCount | Total number of followers |
| data.content[].followsMe | Boolean indicating if this user follows the authenticated user |
| data.pageable | Pagination metadata |
| data.totalPages | Total number of pages available |
| data.totalElements | Total number of featured users |
| data.first | Boolean indicating if this is the first page |
| data.last | Boolean indicating if this is the last page |
| data.size | Page size |
| data.number | Current page number (0-indexed) |
| data.numberOfElements | Number of elements in current page |
| data.empty | Boolean indicating if the page is empty |
Quick Reference Guide
Common HTTP Status Codes
200 OK: Successful GET/POST/DELETE request400 Bad Request: Invalid request data or business rule violation401 Unauthorized: Authentication required/failed404 Not Found: Resource not found500 Internal Server Error: Server error
Authentication
- Bearer Token: Include
Authorization: Bearer your_tokenin 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
Relationship Field Reference
| Field | Meaning |
|---|---|
followsYou |
Does this user follow you (authenticated user)? |
youFollow |
Do you (authenticated user) follow this user? |
These fields enable smart UI rendering:
- Show "Follow Back" when
followsYou: trueandyouFollow: false - Show "Follows You" badge when
followsYou: true - Show "Following" when
youFollow: true - Show "Follow" when both are
false
Version History
v1.1 (2025-12-20)
- Added relationship context fields (
followsYou,youFollow) to all follower/following endpoints - Added "How The Follow System Works" section with detailed relationship logic
- Added "UI Implementation Best Practices" section with button state management and confirmation patterns
- Updated all response samples to include new relationship fields
- Enhanced documentation with button logic examples and visual state guidelines
v1.0 (2025-12-11)
- Initial release
- All core follow system endpoints
- Basic follower/following management
- Featured users discovery
Privacy Control API
Base URL: https://api.nexgate.com/api/v1
Short Description: The Privacy Control API allows users to block and mute other users to control their social interactions. Blocking removes all follow relationships and prevents future interactions, while muting hides content without breaking the follow relationship. These features give users granular control over their social experience.
Hints:
- All endpoints require authentication via Bearer token
- Blocking a user automatically unfollows both users from each other
- Muting only affects content visibility and does not break follow relationships
- Users cannot block or mute themselves
- Blocked and muted lists are retrieved in reverse chronological order (newest first)
Standard Response Format
All API responses follow a consistent structure using our Globe Response Builder pattern:
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-09-23T10:30:45",
"data": {
// Actual response data goes here
}
}
Error Response Structure
{
"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 - GET - Green (Safe, read-only operations)
- POST - POST - Blue (Create new resources)
- DELETE - DELETE - Red (Remove resources)
Endpoints
1. Block User
Purpose: Block a user to prevent all interactions and remove existing follow relationships
Endpoint: POST {base_url}/e-social/privacy-control/block/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to block | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User blocked successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"id": "880e8400-e29b-41d4-a716-446655440033",
"user": {
"id": "777e7777-e77b-77d7-a777-777777777777",
"userName": "spam_account",
"firstName": "Spam",
"lastName": "Account",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/spam_account.jpg"],
"isVerified": false
},
"createdAt": "2025-12-11T10:30:45"
}
}
Success Response Fields:
| Field | Description |
|---|---|
| id | Unique identifier for the block record (UUID format) |
| user | Object containing information about the blocked user |
| user.id | UUID of the blocked user |
| user.userName | Username of the blocked user |
| user.firstName | First name of the blocked user |
| user.lastName | Last name of the blocked user |
| user.profilePictureUrls | Array of profile picture URLs for the blocked user |
| user.isVerified | Boolean indicating if the blocked account is verified |
| createdAt | Timestamp when the block was created (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Cannot block yourself",
"action_time": "2025-12-11T10:30:45",
"data": "Cannot block yourself"
}
Standard Error Types:
Application-Level Exceptions (400-499)
400 BAD_REQUEST: Cannot block yourself or user already blocked401 UNAUTHORIZED: Authentication issues (empty, invalid, expired, or malformed tokens)404 NOT_FOUND: User to block not found500 INTERNAL_SERVER_ERROR: Unexpected server errors
Error Response Examples:
Bad Request - Self Block (400):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Cannot block yourself",
"action_time": "2025-12-11T10:30:45",
"data": "Cannot block yourself"
}
Bad Request - Already Blocked (400):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "User already blocked",
"action_time": "2025-12-11T10:30:45",
"data": "User already blocked"
}
Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
2. Unblock User
Purpose: Remove a block on a previously blocked user
Endpoint: DELETE {base_url}/e-social/privacy-control/unblock/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to unblock | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User unblocked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Always null for this endpoint |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
3. Mute User
Purpose: Mute a user to hide their content without breaking the follow relationship
Endpoint: POST {base_url}/e-social/privacy-control/mute/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to mute | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User muted successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
"id": "990e8400-e29b-41d4-a716-446655440044",
"user": {
"id": "888e8888-e88b-88d8-a888-888888888888",
"userName": "noisy_user",
"firstName": "Noisy",
"lastName": "User",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/noisy_user.jpg"],
"isVerified": true
},
"createdAt": "2025-12-11T10:30:45"
}
}
Success Response Fields:
| Field | Description |
|---|---|
| id | Unique identifier for the mute record (UUID format) |
| user | Object containing information about the muted user |
| user.id | UUID of the muted user |
| user.userName | Username of the muted user |
| user.firstName | First name of the muted user |
| user.lastName | Last name of the muted user |
| user.profilePictureUrls | Array of profile picture URLs for the muted user |
| user.isVerified | Boolean indicating if the muted account is verified |
| createdAt | Timestamp when the mute was created (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Cannot mute yourself",
"action_time": "2025-12-11T10:30:45",
"data": "Cannot mute yourself"
}
Standard Error Types:
Application-Level Exceptions (400-499)
400 BAD_REQUEST: Cannot mute yourself or user already muted401 UNAUTHORIZED: Authentication issues (empty, invalid, expired, or malformed tokens)404 NOT_FOUND: User to mute not found500 INTERNAL_SERVER_ERROR: Unexpected server errors
Error Response Examples:
Bad Request - Self Mute (400):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Cannot mute yourself",
"action_time": "2025-12-11T10:30:45",
"data": "Cannot mute yourself"
}
Bad Request - Already Muted (400):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "User already muted",
"action_time": "2025-12-11T10:30:45",
"data": "User already muted"
}
Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
4. Unmute User
Purpose: Remove a mute on a previously muted user
Endpoint: DELETE {base_url}/e-social/privacy-control/unmute/{userId}
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 |
|---|---|---|---|---|
| userId | string | Yes | UUID of the user to unmute | Must be valid UUID format |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "User unmuted successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Always null for this endpoint |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User not found",
"action_time": "2025-12-11T10:30:45",
"data": "User not found"
}
5. Get Blocked Users
Purpose: Retrieve the complete list of users blocked by the authenticated user
Endpoint: GET {base_url}/e-social/privacy-control/blocked
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:
{
"success": true,
"httpStatus": "OK",
"message": "Blocked users retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": [
{
"id": "880e8400-e29b-41d4-a716-446655440033",
"user": {
"id": "777e7777-e77b-77d7-a777-777777777777",
"userName": "spam_account",
"firstName": "Spam",
"lastName": "Account",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/spam_account.jpg"],
"isVerified": false
},
"createdAt": "2025-12-11T10:30:45"
},
{
"id": "991e8400-e29b-41d4-a716-446655440044",
"user": {
"id": "999e9999-e99b-99d9-a999-999999999999",
"userName": "troll_user",
"firstName": "Troll",
"lastName": "User",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/troll_user.jpg"],
"isVerified": false
},
"createdAt": "2025-12-10T15:20:00"
}
]
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Array of blocked user objects, sorted by createdAt in descending order (newest first) |
| data[].id | Unique identifier for the block record (UUID format) |
| data[].user | Object containing information about the blocked user |
| data[].user.id | UUID of the blocked user |
| data[].user.userName | Username of the blocked user |
| data[].user.firstName | First name of the blocked user |
| data[].user.lastName | Last name of the blocked user |
| data[].user.profilePictureUrls | Array of profile picture URLs for the blocked user |
| data[].user.isVerified | Boolean indicating if the blocked account is verified |
| data[].createdAt | Timestamp when the block was created (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
6. Get Muted Users
Purpose: Retrieve the complete list of users muted by the authenticated user
Endpoint: GET {base_url}/e-social/privacy-control/muted
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:
{
"success": true,
"httpStatus": "OK",
"message": "Muted users retrieved successfully",
"action_time": "2025-12-11T10:30:45",
"data": [
{
"id": "990e8400-e29b-41d4-a716-446655440044",
"user": {
"id": "888e8888-e88b-88d8-a888-888888888888",
"userName": "noisy_user",
"firstName": "Noisy",
"lastName": "User",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/noisy_user.jpg"],
"isVerified": true
},
"createdAt": "2025-12-11T10:30:45"
},
{
"id": "101e8400-e29b-41d4-a716-446655440055",
"user": {
"id": "100e1000-e10b-10d1-a100-100000000000",
"userName": "oversharer",
"firstName": "Over",
"lastName": "Sharer",
"profilePictureUrls": ["https://cdn.nexgate.com/profiles/oversharer.jpg"],
"isVerified": false
},
"createdAt": "2025-12-09T08:15:30"
}
]
}
Success Response Fields:
| Field | Description |
|---|---|
| data | Array of muted user objects, sorted by createdAt in descending order (newest first) |
| data[].id | Unique identifier for the mute record (UUID format) |
| data[].user | Object containing information about the muted user |
| data[].user.id | UUID of the muted user |
| data[].user.userName | Username of the muted user |
| data[].user.firstName | First name of the muted user |
| data[].user.lastName | Last name of the muted user |
| data[].user.profilePictureUrls | Array of profile picture URLs for the muted user |
| data[].user.isVerified | Boolean indicating if the muted account is verified |
| data[].createdAt | Timestamp when the mute was created (ISO 8601 format) |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
Standard Error Types:
Application-Level Exceptions (400-499)
Error Response Examples:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-12-11T10:30:45",
"data": "Token has expired"
}
Quick Reference Guide
Common HTTP Status Codes
200 OK: Successful GET/POST/DELETE request400 Bad Request: Invalid request data or business rule violation401 Unauthorized: Authentication required/failed404 Not Found: Resource not found500 Internal Server Error: Server error
Authentication
- Bearer Token: Include
Authorization: Bearer your_tokenin headers
Data Format Standards
- Dates: Use ISO 8601 format (2025-12-11T14:30:00Z)
- IDs: UUID format (e.g., 550e8400-e29b-41d4-a716-446655440000)
Key Differences Between Block and Mute
| Feature | Block | Mute |
|---|---|---|
| Removes follow relationships | Yes | No |
| Hides content | Yes | Yes |
| User knows they're blocked/muted | Possibly (can't follow) | No |
| Can send messages | No | Yes (if messaging exists) |
| Can view profile | Limited | Yes |
Posts Management API
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
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-09-23T10:30:45",
"data": {
// Actual response data goes here
}
}
Error Response Structure
{
"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 - GET - Green (Safe, read-only operations)
- POST - POST - Blue (Create new resources)
- PUT - PUT - Yellow (Update/replace entire resource)
- DELETE - DELETE - 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:
{
"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:
{
"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: POST {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:
- Draft System: Only one draft allowed per user at a time
- Content Requirements: Either content text OR media OR poll must be provided (not all optional)
- Poll Requirements: If postType is POLL, poll object with 2-10 options is required
- Media Limits: Maximum 10 media items per post
- Commerce Limits: Max 10 products, 5 shops, 3 events per post
- Collaboration Limits: Maximum 5 collaborators per post
- Scheduling Rules: Scheduled time must be at least 5 minutes in future, max 6 months ahead
- Mention Extraction: System automatically detects and links @username and $shopname mentions
- Hashtag Indexing: All #hashtags automatically indexed for search and trending
- 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:
{
"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: truewithout anexpiresAtmeans 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:
{
"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 errors401 UNAUTHORIZED: Authentication issues (empty, invalid, expired, or malformed tokens)422 UNPROCESSABLE_ENTITY: Validation errors with detailed field information500 INTERNAL_SERVER_ERROR: Unexpected server errors
Error Response Examples:
Bad Request - Existing Draft (400):
{
"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):
{
"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):
{
"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):
{
"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):
{
"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):
{
"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):
{
"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
{
"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
{
"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
{
"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
{
"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: POST {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:
- Draft Requirement: User must have exactly one draft post
- Content Validation: Draft must pass all content validation rules (see table above)
- Media Rule: Posts with media (images/videos) must include text content
- Collaboration Status: All invited collaborators must have accepted (status = ACCEPTED)
- Attachment Validation: All attached products/shops/events must still be active/valid
- Scheduled Posts: Cannot publish a scheduled post using this endpoint
- Feed Distribution: Published posts immediately appear in followers' feeds
- 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:
{
"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:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Media posts must include text content",
"action_time": "2025-12-29T10:30:45",
"data": null
}
Empty Post:
{
"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:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "No draft found",
"action_time": "2025-12-29T10:30:45",
"data": null
}
Pending Collaborator Approval:
{
"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:
{
"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 text401 UNAUTHORIZED: Missing or invalid authentication token403 FORBIDDEN: Collaborative post pending approval from collaborators404 NOT_FOUND: No draft post found for user422 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: DELETE {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:
- 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:
{
"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 posts401 UNAUTHORIZED: Authentication issues403 FORBIDDEN: Not the post author (collaborators cannot delete)404 NOT_FOUND: Post not found or already deleted500 INTERNAL_SERVER_ERROR: Unexpected server errors
Error Response Examples:
{
"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):
{
"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: GET {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)
Error Response Examples:
Post Not Found (404):
{
"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):
{
"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):
{
"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: GET {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:
{
"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)
6. Get Posts by Author
Purpose: Retrieve all posts from a specific author with pagination
Endpoint: GET {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)
7. Get My Scheduled Posts
Purpose: Retrieve all scheduled posts for the authenticated user
Endpoint: GET {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)
8. Get My Current Draft
Purpose: Retrieve the authenticated user's current draft post
Endpoint: GET {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)
9. Discard Draft
Purpose: Delete the current draft post permanently
Endpoint: DELETE {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:
{
"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 draft401 UNAUTHORIZED: Authentication issues500 INTERNAL_SERVER_ERROR: Unexpected server errors
10. Update Draft
Purpose: Update content, media, or privacy settings of the current draft
Endpoint: PUT {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:
{
"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 updated401 UNAUTHORIZED: Authentication issues422 UNPROCESSABLE_ENTITY: Validation errors500 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: POST {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: DELETE {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: PUT {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: PUT {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: PUT {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: PUT {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: POST {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: POST {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: DELETE {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: POST {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
repostsCountincremented by 1 - Response engagement and interaction data comes from the original post, not this new repost post
Response (postKind: REPOST):
{
"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:
engagementanduserInteractionon 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
quotesCountincremented by 1 - This new post has its own independent engagement — its own likes, comments, views
Response (postKind: QUOTE):
{
"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:
"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 toPOST_A) - You repost
POST_B→ your new repost points toPOST_A, notPOST_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 = truewith 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:
- Original post must be PUBLISHED
- Cannot repost the same post twice (duplicate check by author + quotedPostId)
- Daily limit applies to pure reposts (no-content) — configurable server-side
- Reposting a repost always resolves to the original (chain flattening)
- Privacy: original post's
whoCanRepostsetting is enforced - Repost engagement (likes, comments, views) always routes to the original post
- 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 published401 UNAUTHORIZED: Authentication required403 FORBIDDEN: Original post'swhoCanRepostsetting blocks you404 NOT_FOUND: Original post not found
Interaction Endpoints (29-38)
29. Like Post
Purpose: Add a like to a post
Endpoint: POST {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:
- Visibility Check: User must have permission to view post based on privacy settings
- Blocking: Cannot like posts from blocked users or if author blocked you
- Deleted Posts: Cannot like deleted posts (isDeleted = true)
- Draft/Scheduled: Cannot like unpublished posts (must be PUBLISHED status)
- Idempotent: Multiple like attempts don't create duplicate likes
- Notification: Author receives notification (unless self-like)
- Counter Update: Post's engagement counter incremented atomically
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Post liked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
Error Response Examples:
Cannot Like - Blocked User (403):
{
"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):
{
"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: DELETE {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:
- Silent Operation: No notification sent to post author
- Always Allowed: Can unlike even if blocked or post deleted (to clean up state)
- Idempotent: Multiple unlike attempts don't cause errors
- Counter Decrement: Post's engagement counter decremented atomically
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Post unliked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
31. Bookmark Post
Purpose: Save a post to bookmarks for later viewing
Endpoint: POST {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:
- Private Action: Bookmarks are private, author doesn't get notified
- Visibility Required: Must have permission to view post based on privacy settings
- Persistent: Bookmarks persist even if post is deleted (for user's reference)
- Organization: Can be organized into collections (future feature)
- No Limit: Unlimited bookmarks allowed per user
- Idempotent: Multiple bookmark attempts don't create duplicates
- Counter Update: Post's engagement counter incremented
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Post bookmarked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
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: DELETE {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:
- Silent Operation: No notification sent
- Always Allowed: Can unbookmark anytime, even if post deleted
- Idempotent: Multiple unbookmark attempts don't cause errors
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Post unbookmarked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
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:
- Fetch the original post. The response contains:
"userInteraction": {
"hasReposted": true,
"myRepostId": "bbb-222"
}
- Call the standard delete endpoint with the repost's ID:
Endpoint: DELETE {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
repostsCountdecremented by 1 userInteraction.hasRepostedreturnsfalse,myRepostIdreturnsnullon next fetch- No notification sent (silent)
Business Rules:
- Only the repost author can delete their own repost
- To remove a quote post (which has content), use the same
DELETE /posts/{quotePostId}— this decrementsquotesCountinstead - If original post is already deleted, the repost will have been auto-deleted by the cascade — nothing to undo
Success Response JSON Sample:
{
"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 repost401 UNAUTHORIZED: Authentication required404 NOT_FOUND: Repost not found or already deleted
35. Record View
Purpose: Track post views for analytics
Endpoint: POST {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:
- One View Per User: Each user counted only once regardless of repeat views
- Private Metric: View counts not shown publicly (author sees in analytics)
- No Notification: Viewing doesn't notify author
- Automatic Tracking: Frontend typically calls this when post visible for 2+ seconds
- Respects Visibility: Must have permission to view post
- Author Excluded: Author's own views don't count
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "View recorded successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
36. Get My Bookmarks
Purpose: Retrieve all posts bookmarked by current user
Endpoint: GET {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:
- Personal Collection: Only user's own bookmarks visible
- Persists Deletions: Bookmarked posts remain accessible even if deleted
- Persists Blocking: Can view bookmarked posts even if author blocked
- Chronological: Sorted by bookmark time (newest first)
- Full Details: Each post includes complete information
- 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:
{
"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:
37. Get My Reposts
Purpose: Retrieve all posts reposted by current user
Endpoint: GET {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:
- Simple Reposts Only: Only shows direct reposts, not quote posts
- Quote Posts: Appear in regular "Get User Posts" endpoint as authored posts
- Current Visibility: If original post now private/deleted, it's excluded
- Chronological: Sorted by repost time (newest first)
- 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:
{
"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:
38. Get User Reposts
Purpose: Retrieve all posts reposted by a specific user
Endpoint: GET {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:
- Simple Reposts Only: Only shows direct reposts, not quote posts
- Visibility Filtering: Only shows original posts requesting user can see
- Privacy Respected: Follows same visibility rules as original posts
- Blocking: Hidden if original author blocked requesting user
- 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:
{
"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:
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:
- Single vs Multiple: Respects poll's allowMultipleVotes setting
- Vote Changes: Allowed only if allowVoteChange is true
- Expiration: Cannot vote on expired polls
- Visibility: Must have access to view post
- Blocking: Cannot vote on polls from blocked users
- Option Validation: All optionIds must belong to this poll
- Single Vote Mode: If allowMultipleVotes false, only 1 optionId allowed
- Multiple Vote Mode: If allowMultipleVotes true, can select multiple options
- Anonymous: If isAnonymous true, voter identity hidden
- 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):
{
"optionIds": ["option-uuid-1"]
}
Request JSON Sample (Multiple Votes):
{
"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:
{
"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 options401 UNAUTHORIZED: Authentication required403 FORBIDDEN: Cannot vote (blocked, visibility restrictions)404 NOT_FOUND: Post or poll not found422 UNPROCESSABLE_ENTITY: Validation errors (too many options, invalid IDs)500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Poll Expired (400):
{
"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):
{
"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):
{
"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):
{
"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:
- Vote Change Required: Only works if allowVoteChange is true
- Complete Removal: Removes ALL votes (if multiple vote poll)
- Idempotent: Safe to call if not voted
- Expired Polls: Can remove votes even after expiration
- Counter Updates: All affected option counts decremented
- 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:
{
"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 required403 FORBIDDEN: Cannot access poll404 NOT_FOUND: Post or poll not found500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Vote Removal Not Allowed (400):
{
"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:
- Visibility: Must have access to view parent post
- Real-Time: Results update immediately after each vote
- Percentages: Calculated as (optionVotes / totalVotes) × 100
- User State: Shows which options current user voted for
- Expiration Check: Indicates if poll has expired
- Zero Votes: Shows 0.0% for options with no votes
- Anonymous Respect: Voter identities hidden if isAnonymous true
- 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:
{
"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:
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:
- Non-Anonymous Only: Returns error if
isAnonymous = true - Any Authenticated User: No author/collaborator restriction
- Secondary Onboarding Required: Caller must have username + profilePic set
- Deduplication: Users who voted on multiple options appear only once
- Follow Status: Response reflects the calling user's follow relationship with each voter
- 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:
{
"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 available401 UNAUTHORIZED: Authentication required403 FORBIDDEN: Secondary onboarding not complete404 NOT_FOUND: Post not found or not a poll post500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Anonymous Poll (400):
{
"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):
{
"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:
- Non-Anonymous Only: Only works if
isAnonymous = false - Author/Collaborator Access: Only post author and collaborators can view
- Privacy Protection: Regular users cannot see who voted per option
- Vote Time: Includes when each user voted
- Profile Info: Returns basic user profile (name, photo, verified status)
- Pagination: Default 50 voters per page
- 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:
{
"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 available401 UNAUTHORIZED: Authentication required403 FORBIDDEN: Not post author/collaborator404 NOT_FOUND: Post or option not found500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Anonymous Poll (400):
{
"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."
}
{
"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: POST {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:
- Permission Check: Post must allow comments (whoCanComment setting)
- Visibility Check: Must have permission to view post to comment
- Blocking: Cannot comment on posts from blocked users
- Nesting: Supports unlimited reply depth (replies to replies to replies...)
- Mentions: All @mentioned users notified
- Notification: Post author and parent comment author notified
- Content Length: Min 1, Max 2000 characters
- Spam Prevention: Rate limited (max 10 comments per minute per user)
- Edit Window: Can edit within 15 minutes of posting
- 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):
{
"content": "Great post! @john_doe you should check this out",
"parentCommentId": null
}
Request JSON Sample (Nested Reply):
{
"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:
{
"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 detected401 UNAUTHORIZED: Authentication required403 FORBIDDEN: Cannot comment (permissions, blocked, disabled comments)404 NOT_FOUND: Post not found422 UNPROCESSABLE_ENTITY: Validation errors (content too long/short)429 TOO_MANY_REQUESTS: Rate limit exceeded500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Comments Disabled (403):
{
"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):
{
"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):
{
"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):
{
"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: GET {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:
- Top-Level Only: Returns only direct comments on post, not replies
- Pinned Priority: Pinned comments always appear first
- Sorting: After pinned, sorted by newest first
- Reply Count: Each comment shows number of replies
- Pagination: Default 20 per page, max 100
- 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:
45. Get Comment
Purpose: Retrieve a single comment by ID with full details
Endpoint: GET {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:
- Visibility: Must be able to view parent post
- Deleted: Returns 404 if comment deleted
- Full Details: Includes all engagement metrics
- 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:
46. Get Replies
Purpose: Retrieve all replies to a specific comment (nested comments)
Endpoint: GET {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:
- Direct Replies Only: Returns immediate children, not grandchildren
- Conversation Order: Sorted oldest first (chronological conversation)
- Nested Support: Each reply shows its own replyCount
- Recursive Loading: Frontend loads nested replies on-demand
- 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:
47. Update Comment
Purpose: Edit an existing comment's content
Endpoint: PUT {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:
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:
{
"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 required403 FORBIDDEN: Not comment author404 NOT_FOUND: Comment not found422 UNPROCESSABLE_ENTITY: Validation errors500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Edit Window Expired (400):
{
"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"
}
{
"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: DELETE {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:
- Who Can Delete: Comment author OR post author
- Soft Delete: Data retained in database
- Replies Preserved: Child replies remain visible
- Parent Reference: Replies show "[deleted]" as parent content
- Counter Update: Post's comment count decremented
- Cascade Option: Post author can cascade delete all replies (optional)
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Comment deleted successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
Use Case Examples:
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: POST {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:
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:
{
"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 comments401 UNAUTHORIZED: Authentication required403 FORBIDDEN: Not post author/collaborator404 NOT_FOUND: Post or comment not found500 INTERNAL_SERVER_ERROR: Unexpected errors
Error Response Examples:
Cannot Pin Reply (400):
{
"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: DELETE {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:
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:
{
"success": true,
"httpStatus": "OK",
"message": "Comment unpinned successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
51. Like Comment
Purpose: Add a like to a comment
Endpoint: POST {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:
- Visibility: Must have access to parent post
- Blocking: Cannot like comments from blocked users
- Deleted: Cannot like deleted comments
- Idempotent: Safe to call multiple times
- Notification: Comment author notified (unless self-like)
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Comment liked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
52. Unlike Comment
Purpose: Remove a like from a comment
Endpoint: DELETE {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:
- Silent Operation: No notification sent
- Always Allowed: Can unlike even if blocked or comment deleted
- Idempotent: Safe to call multiple times
- 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:
{
"success": true,
"httpStatus": "OK",
"message": "Comment unliked successfully",
"action_time": "2025-12-11T10:30:45",
"data": null
}
Standard Error Types:
53. Share Post
Purpose: Record a post share action and track which platform was used for sharing
Endpoint: POST {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:
{
"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:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Post not found",
"action_time": "2025-12-29T14:30:45",
"data": null
}
Post Not Published:
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Can only share published posts",
"action_time": "2025-12-29T14:30:45",
"data": null
}
{
"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 published401 UNAUTHORIZED: Missing or invalid authentication token
| Platform | Description |
|---|---|
| Shared via WhatsApp | |
| Shared via Twitter/X | |
| Shared via Facebook | |
| 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:
# 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:
{
"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: POST {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:
{
"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:
{
"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:
{
"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 post401 UNAUTHORIZED: Missing or invalid authentication token
55. Get Post Reports (Admin)
Purpose: Retrieve all reports for a specific post (Admin only)
Endpoint: GET {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:
{
"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:
{
"success": false,
"httpStatus": "FORBIDDEN",
"message": "Access denied",
"action_time": "2025-12-29T15:30:45",
"data": null
}
Standard Error Types:
56. Get All Reports (Admin)
Purpose: Retrieve all reports with optional filters for status, reason, and date range (Admin only)
Endpoint: GET {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:
{
"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:
{
"success": false,
"httpStatus": "FORBIDDEN",
"message": "Access denied",
"action_time": "2025-12-29T15:30:45",
"data": null
}
Standard Error Types:
57. Review Report (Admin)
Purpose: Review and update the status of a report (Admin only)
Endpoint: PATCH {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:
{
"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:
{
"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:
{
"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 status401 UNAUTHORIZED: Missing or invalid authentication token403 FORBIDDEN: User does not have admin role
58. Get Report Statistics (Admin)
Purpose: Retrieve aggregate statistics about reports (Admin only)
Endpoint: GET {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:
{
"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:
{
"success": false,
"httpStatus": "FORBIDDEN",
"message": "Access denied",
"action_time": "2025-12-29T16:30:00",
"data": null
}
Standard Error Types:
59. Edit Published Post
Purpose: Edit a published post within the 60-minute edit window (max 3 edits)
Endpoint: PUT {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):
{
"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):
{
"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:
{
"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):
{
"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):
{
"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):
{
"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):
{
"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):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "Post not found",
"action_time": "2025-01-03T10:30:45",
"data": "Post not found"
}
{
"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: GET {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:
{
"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):
{
"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: GET {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:
{
"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):
{
"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: GET {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:
{
"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):
{
"success": true,
"httpStatus": "OK",
"message": "Edit eligibility checked",
"action_time": "2025-01-03T10:30:45",
"data": {
"canEdit": false
}
}
{
"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: GET {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:
{
"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):
{
"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: GET {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:
{
"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):
{
"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: GET {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:
{
"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):
{
"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:
{
"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 request400 Bad Request: Invalid request data or business rule violation401 Unauthorized: Authentication required/failed404 Not Found: Resource not found422 Unprocessable Entity: Validation errors500 Internal Server Error: Server error
Authentication
- Bearer Token: Include
Authorization: Bearer your_tokenin 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 postPOLL: Poll post with voting options
Post Status Values
DRAFT: Unpublished draftSCHEDULED: Scheduled for future publicationPUBLISHED: Live and visibleDELETED: Soft deleted
Visibility Levels
PUBLIC: Anyone can seeFOLLOWERS: Only followers can seeMENTIONED: Only mentioned users can seePRIVATE: Only author can see
Permission Levels
EVERYONE: All usersFOLLOWERS: Only followersMENTIONED: Only mentioned users (for comments)DISABLED: Feature disabled
Hashtag API
Base URL: https://api.nexgate.com/api/v1
Short Description: The Hashtag API enables content discovery through hashtags. Users can browse posts by hashtag with optional filters for products, shops, and events, and explore trending hashtags across configurable time windows.
Hints:
Standard Response Format
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-09-23T10:30:45",
"data": {}
}
HTTP Method Badge Standards
- GET - GET - Green
Endpoints
1. Get Posts By Hashtag
Purpose: Retrieve paginated posts that contain a specific hashtag, with optional filtering by attachment type
Endpoint: GET {base_url}/e-social/hashtags/{hashtag}/posts
Access Level: 🔒 Protected (Requires Bearer Token Authentication)
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| hashtag | string | Yes | The hashtag to search (without #, e.g. MbeyaFashion) |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| filter | string | No | ALL |
Filter by attachment type. Accepted values: ALL, PRODUCTS, SHOPS, EVENTS |
| page | integer | No | 1 |
Page number |
| size | integer | No | 20 |
Number of posts per page |
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer <token> |
| Content-Type | string | Yes | application/json |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Posts retrieved",
"action_time": "2026-03-02T10:30:45",
"data": {
"content": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"author": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"userName": "kibuti_dev",
"firstName": "Kibuti",
"lastName": "Dev",
"profilePictureUrl": "https://cdn.nexgate.com/profiles/kibuti.jpg",
"isVerified": false
},
"content": "New collection just dropped! #MbeyaFashion #Summer",
"contentParsed": {
"text": "New collection just dropped! #MbeyaFashion #Summer",
"entities": [
{
"type": "HASHTAG",
"text": "#MbeyaFashion",
"startIndex": 29,
"endIndex": 42,
"hashtag": "mbeyafashion"
}
]
},
"postType": "REGULAR",
"status": "PUBLISHED",
"attachments": {
"products": [
{
"id": "789e0123-e89b-12d3-a456-426614174000",
"name": "Summer Dress",
"price": 45000.00,
"imageUrl": "https://cdn.nexgate.com/products/dress.jpg",
"shopName": "Mbeya Styles",
"shopId": "abc12345-e89b-12d3-a456-426614174000",
"inStock": true,
"socialContext": null
}
],
"shops": [],
"events": [],
"buyTogetherGroups": [],
"installmentPlans": [],
"externalLink": null
},
"engagement": {
"likesCount": 24,
"commentsCount": 5,
"repostsCount": 3,
"bookmarksCount": 8,
"viewsCount": 120,
"sharesCount": 2,
"quotesCount": 1
},
"publishedAt": "2026-03-01T14:22:00",
"createdAt": "2026-03-01T14:20:00"
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 20
},
"totalElements": 54,
"totalPages": 3,
"last": false,
"first": true
}
}
Filter Behavior:
| Filter Value | Returns |
|---|---|
ALL |
All posts containing the hashtag |
PRODUCTS |
Only posts with the hashtag that have attached products |
SHOPS |
Only posts with the hashtag that have attached shops |
EVENTS |
Only posts with the hashtag that have attached events |
Standard Error Types:
Error Response Example:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2026-03-02T10:30:45",
"data": "Token has expired"
}
2. Get Trending Hashtags
Purpose: Retrieve the most used hashtags within a given time window, ordered by post count descending
Endpoint: GET {base_url}/e-social/hashtags/trending
Access Level: 🔒 Protected (Requires Bearer Token Authentication)
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| window | string | No | 24h |
Time window for trending calculation. Accepted values: 24h, 7d, 30d |
| limit | integer | No | 15 |
Maximum number of trending hashtags to return |
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer <token> |
| Content-Type | string | Yes | application/json |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Trending hashtags",
"action_time": "2026-03-02T10:30:45",
"data": [
{
"hashtag": "mbeyafashion",
"postCount": 312
},
{
"hashtag": "daressalaamtech",
"postCount": 278
},
{
"hashtag": "tanzaniastartups",
"postCount": 195
},
{
"hashtag": "summer2026",
"postCount": 143
}
]
}
Response Fields:
| Field | Type | Description |
|---|---|---|
| hashtag | string | The hashtag text (always lowercase, without #) |
| postCount | long | Number of posts using this hashtag within the selected time window |
Window Behavior:
| Window Value | Looks Back |
|---|---|
24h |
Last 24 hours — what's trending right now |
7d |
Last 7 days — weekly trends |
30d |
Last 30 days — monthly trends |
Standard Error Types:
3. Search Hashtags
Purpose: Search hashtags by prefix query, used in post composer to suggest similar hashtags while typing. Results are sorted by a weighted score combining recent activity and all-time popularity.
Endpoint: GET {base_url}/e-social/hashtags/search
Access Level: 🔒 Protected (Requires Bearer Token Authentication)
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| q | string | Yes | — | Prefix query string (e.g. Mbe matches mbeyafashion, mbeyatech) |
| limit | integer | No | 5 |
Maximum number of suggestions to return |
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer <token> |
| Content-Type | string | Yes | application/json |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Hashtag suggestions",
"action_time": "2026-03-02T10:30:45",
"data": [
{
"hashtag": "mbeyafashion",
"postCount": 312
},
{
"hashtag": "mbeyatech",
"postCount": 89
},
{
"hashtag": "mbeyafoods",
"postCount": 45
}
]
}
Response Fields:
| Field | Type | Description |
|---|---|---|
| hashtag | string | Matched hashtag text (always lowercase, without #) |
| postCount | long | Total number of posts using this hashtag |
Scoring & Sort Logic:
Results are sorted by a weighted score combining recency and popularity:
| Post Age | Score Weight |
|---|---|
| Posted within last 7 days | 2 points per post |
| Older than 7 days | 1 point per post |
So a hashtag with 50 recent posts can outrank one with 200 old posts — fresh and trending tags float to the top naturally.
Empty Query Behavior: If q is empty or blank, returns an empty list immediately without querying the database.
Standard Error Types:
Error Response Example:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2026-03-02T10:30:45",
"data": "Token has expired"
}
Quick Reference Guide
Endpoints Summary
| Method | Endpoint | Description |
|---|---|---|
| GET | /e-social/hashtags/{hashtag}/posts |
Get posts by hashtag with optional filter |
| GET | /e-social/hashtags/trending |
Get trending hashtags by time window |
Filter Values
ALL PRODUCTS SHOPS EVENTS
Trending Window Values
24h 7d 30d
Authentication
- Bearer Token: Include
Authorization: Bearer your_tokenin all headers
Data Format Standards
- Dates: ISO 8601 format (
2026-03-02T14:30:00) - IDs: UUID format (
550e8400-e29b-41d4-a716-446655440000) - Hashtags: Always lowercase, without
#symbol in API responses