Shop Subscription

Author: Josh S. Sakweli, Backend Lead Team
Last Updated: 2026-06-04
Version: v1.0

Base URL: api/v1/e-commerce/shops

Short Description: The Shop Subscription API allows users to subscribe and unsubscribe from shops on the Nexgate marketplace. Subscriptions drive personalised product discovery — subscribed shops receive a ranking boost in the Trending and For You feeds, and a dedicated "from your shops" signal in the relevance formula. Shop owners can view who has subscribed to their shop.

Hints:


Standard Response Format

All API responses follow a consistent structure using the Globe Response Builder pattern:

Success Response Structure

{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": { }
}

Error Response Structure

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2026-06-04T10:30:45",
  "data": "Error description"
}

Standard Response Fields

Field Type Description
success boolean true for success, false for errors
httpStatus string HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.)
message string Human-readable operation result
action_time string ISO 8601 timestamp of response generation
data object Response payload for success, error detail for failures

HTTP Method Badge Standards


Subscription State Reference

┌─────────────────────────────────────────────────────────────────────┐
│                    SUBSCRIPTION STATE FLOW                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   User not subscribed                                               │
│         │                                                           │
│         ▼  POST /{shopId}/subscribe                                 │
│   User subscribed  ──────────────────────────────────────────────► │
│         │               subscriberCount + 1                        │
│         │               subscribed: true                           │
│         │                                                           │
│         ▼  POST /{shopId}/subscribe  (toggle again)                 │
│   User not subscribed ◄──────────────────────────────────────────  │
│                         subscriberCount - 1                        │
│                         subscribed: false                          │
│                                                                     │
│  Side effects:                                                      │
│  - Shop's subscriberCount is updated atomically                    │
│  - Marketplace FOR_YOU feed recalculates on next request           │
│  - Marketplace TRENDING feed gives +0.25 boost to subscribed shops │
└─────────────────────────────────────────────────────────────────────┘

How Subscriptions Affect the Marketplace

┌─────────────────────────────────────────────────────────────────────┐
│              SUBSCRIPTION → MARKETPLACE SIGNALS                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  TRENDING feed:                                                     │
│    personalizedScore = trendingScore + 0.25                        │
│    (applied to every product from a subscribed shop)               │
│                                                                     │
│  FOR YOU feed:                                                      │
│    relevanceScore = categoryMatch × 0.40                           │
│                   + favShopBoost  × 0.40  ← subscription signal    │
│                   + trendingScore × 0.20                           │
│    favShopBoost = 1.0 if subscribed, 0.0 otherwise                 │
│                                                                     │
│  Result: products from subscribed shops surface near the top of    │
│  both feeds while still allowing genuinely trending products to     │
│  compete (soft priority, not guaranteed top position).              │
└─────────────────────────────────────────────────────────────────────┘

Endpoints


1. Toggle Subscribe / Unsubscribe

Purpose: Subscribes the authenticated user to a shop if they are not already subscribed, or unsubscribes them if they are. Returns the new subscription state and the updated subscriber count.

Endpoint: POST api/v1/e-commerce/shops/{shopId}/subscribe

Access Level: 🔒 Protected (authentication required)

Authentication: Bearer Token (required)

Request Headers:

Header Type Required Description
Authorization string Yes Bearer <token>

Path Parameters:

Parameter Type Required Description Validation
shopId UUID Yes The shop to subscribe or unsubscribe from Must be a valid UUID of an active, non-deleted shop

Success Response JSON Sample — Subscribe:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Subscribed to shop successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "subscribed": true,
    "subscriberCount": 1284
  }
}

Success Response JSON Sample — Unsubscribe (same endpoint, called again):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Unsubscribed from shop successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "subscribed": false,
    "subscriberCount": 1283
  }
}

Success Response Fields:

Field Type Description
subscribed boolean Current subscription state after the toggle — true if now subscribed, false if now unsubscribed
subscriberCount long Updated total number of subscribers for this shop

Error Response JSON Samples:

Unauthorized (401):

{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2026-06-04T10:30:45",
  "data": "Token has expired"
}

Shop Not Found (404):

{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Shop not found",
  "action_time": "2026-06-04T10:30:45",
  "data": "Shop not found"
}

Standard Error Types:


2. My Subscriptions

Purpose: Returns a paginated list of all shops the authenticated user is currently subscribed to, ordered by most recently subscribed first. Each entry includes shop details and the current subscriber count.

Endpoint: GET api/v1/e-commerce/shops/my-subscriptions

Access Level: 🔒 Protected (authentication required)

Authentication: Bearer Token (required)

Request Headers:

Header Type Required Description
Authorization string Yes Bearer <token>

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 10

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "My subscriptions retrieved successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "content": [
      {
        "subscriptionId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
        "shopId": "7cb3a812-1234-4abc-b3fc-9d84f55bce12",
        "shopName": "TechStore Tanzania",
        "shopSlug": "techstore-tanzania",
        "logoUrl": "https://cdn.nexgate.com/shops/techstore-logo.jpg",
        "bannerUrl": "https://cdn.nexgate.com/shops/techstore-banner.jpg",
        "status": "ACTIVE",
        "isVerified": true,
        "verificationBadge": "GOLD",
        "trustScore": 4.80,
        "subscriberCount": 1284,
        "subscribedAt": "2026-05-20T14:32:00"
      }
    ],
    "currentPage": 1,
    "pageSize": 10,
    "totalElements": 7,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false
  }
}

Success Response Fields:

Field Description
content[].subscriptionId Unique identifier of this subscription record
content[].shopId Shop's unique identifier
content[].shopName Shop display name
content[].shopSlug Shop URL slug — use for navigating to the shop page
content[].logoUrl Shop logo image URL
content[].bannerUrl Shop banner image URL
content[].status Shop status — ACTIVE, TEMPORARILY_OFFLINE, etc.
content[].isVerified Whether the shop has passed platform verification
content[].verificationBadge Verification badge level — BRONZE, SILVER, GOLD
content[].trustScore Shop trust rating 0.00–5.00
content[].subscriberCount Total subscribers this shop currently has
content[].subscribedAt ISO 8601 timestamp of when the user subscribed
currentPage Current page number (1-based)
totalElements Total shops the user is subscribed to
hasNext / hasPrevious Pagination navigation flags

Error Response JSON Samples:

Unauthorized (401):

{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2026-06-04T10:30:45",
  "data": "Token has expired"
}

Standard Error Types:


3. Shop Subscribers (Owner Only)

Purpose: Returns a paginated list of users who have subscribed to a specific shop, ordered by most recently subscribed first. Only the shop owner can access this endpoint — calling it for a shop you do not own returns a 403 error.

Endpoint: GET api/v1/e-commerce/shops/{shopId}/subscribers

Access Level: 🔒 Protected (shop owner only)

Authentication: Bearer Token (required — must be the owner of the shop)

Request Headers:

Header Type Required Description
Authorization string Yes Bearer <token>

Path Parameters:

Parameter Type Required Description Validation
shopId UUID Yes The shop whose subscriber list to retrieve Caller must own this shop

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 10

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Subscribers retrieved successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "content": [
      {
        "userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "fullName": "Amina Hassan",
        "userName": "amina.h",
        "avatarUrl": "https://cdn.nexgate.com/avatars/amina-h.jpg",
        "subscribedAt": "2026-06-01T09:45:00"
      },
      {
        "userId": "1cb2e847-3f1a-4bcd-a3fc-9e84f22bce99",
        "fullName": "John Mwangi",
        "userName": "jmwangi",
        "avatarUrl": null,
        "subscribedAt": "2026-05-29T16:12:00"
      }
    ],
    "currentPage": 1,
    "pageSize": 10,
    "totalElements": 1284,
    "totalPages": 129,
    "hasNext": true,
    "hasPrevious": false
  }
}

Success Response Fields:

Field Description
content[].userId Subscriber's unique account identifier
content[].fullName Subscriber's first + last name
content[].userName Subscriber's public username
content[].avatarUrl Subscriber's profile picture URL — null if no avatar set
content[].subscribedAt ISO 8601 timestamp of when this user subscribed
currentPage Current page number (1-based)
totalElements Total subscriber count for this shop
hasNext / hasPrevious Pagination navigation flags

Error Response JSON Samples:

Unauthorized (401):

{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2026-06-04T10:30:45",
  "data": "Token has expired"
}

Forbidden — Not the shop owner (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "You do not own this shop",
  "action_time": "2026-06-04T10:30:45",
  "data": "You do not own this shop"
}

Shop Not Found (404):

{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Shop not found",
  "action_time": "2026-06-04T10:30:45",
  "data": "Shop not found"
}

Standard Error Types:


isSubscribed & subscriberCount in ShopResponse

Subscription state is automatically embedded in the standard ShopResponse returned by all shop detail endpoints — no separate call needed.

{
  "shopId": "7cb3a812-...",
  "shopName": "TechStore Tanzania",
  "isVerified": true,
  "trustScore": 4.80,
  "isSubscribed": true,
  "subscriberCount": 1284,
  "productCount": 47
}
Field Type Description
isSubscribed boolean true if the requesting authenticated user is subscribed to this shop. Always false for anonymous users
subscriberCount long Total number of subscribers this shop currently has
productCount long Number of active (published) products in this shop

Quick Reference — All Subscription Endpoints

# Endpoint Method Auth Who can call
1 api/v1/e-commerce/shops/{shopId}/subscribe POST Required Any authenticated user
2 api/v1/e-commerce/shops/my-subscriptions GET Required Any authenticated user
3 api/v1/e-commerce/shops/{shopId}/subscribers GET Required Shop owner only

Revision #1
Created 4 June 2026 09:56:30 by Admin Qbit
Updated 4 June 2026 09:56:50 by Admin Qbit