Skip to main content

Shop Management

Author: Josh S. Sakweli, Backend Lead Team
Last Updated: 2026-07-07
Version: v2.1

Short Description: The Shop Management API provides endpoints for creating, managing, and retrieving shop information on the NextGate platform. Covers shop registration, updates, approvals, WABA (WhatsApp Business) integration, AI chatbot toggling, and conversation history.

Hints:

  • All shop endpoints use the prefix api/v1/e-commerce/shops
  • Shop creation requires authentication; public users can only read
  • Pagination uses 1-based page numbering
  • Shop approval operations require ROLE_SUPER_ADMIN or ROLE_STAFF_ADMIN
  • Featured shops are randomized on each request
  • Rating and review data is automatically included in shop responses (read-only — managed by separate review service)
  • WABA = WhatsApp Business Account integration for the shop's AI-powered chatbot

Shop media (logo, banner) goes through FileThunder — no raw URLs are sent by the client, same pipeline used by product media elsewhere in the API:

  1. Client calls POST api/v1/files/request-upload with context: "SHOP_LOGO" or "SHOP_BANNER" → gets back a presigned upload URL and a fileId
  2. Client PUTs the raw file bytes directly to that URL
  3. Client sends that fileId as logoFileId/bannerFileId when creating/updating the shop — validated at request time against the FileThunder Redis cache (ownership + expected context), not necessarily fully processed yet
  4. FileThunder finishes processing (virus scan + variant generation) asynchronously and webhooks back into ShopMediaUpdater.onFileReady, which upgrades the stored shopLogoMedia/shopBannerMedia to status: READY with resolved variants
  • There is no shop image gallery (shopImages) field — only a single shopLogoMedia and single shopBannerMedia, each a ShopMedia object (fileId, status, variants, mimeType). URLs are assembled fresh on every read via FileThunderUrlAssembler, never stored as raw strings
  • Owner/user avatars follow the same rule: ownerProfileMedia on ShopResponse (single-shop detail view) is a full ProfileMedia object (fileId, status, variants, mimeType) resolved via FileThunderUrlAssembler.assembleProfileMedia(). userProfileMedia on review responses and on subscriber responses (list/card contexts) is instead the lean UserProfilePrimaryMedia (fileId, status, thumbUrl, mimeType) resolved via FileThunderUrlAssembler.getUserProfilePrimaryMedia() — a single resolved thumbnail URL instead of the full variants map. Neither is ever a raw avatarUrl/userAvatarUrl string

Standard Response Format

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

Error responses follow the same envelope with "success": false and "data" set to the error message string.


Endpoints

1. Create Shop

Endpoint: POST api/v1/e-commerce/shops

Access Level: 🔒 Protected

Request Body:

Parameter Type Required Description Validation
shopName string Yes Name of the shop Min: 2, Max: 100 chars
shopDescription string Yes Detailed description Max: 1000 chars
logoFileId UUID Yes FileThunder file ID for the shop logo Uploaded via POST api/v1/files/request-upload with context SHOP_LOGO, owned by the caller
phoneNumber string Yes Contact phone Pattern: ^\+?[0-9]{10,15}$
city string Yes City Min: 2, Max: 50 chars
region string Yes Region/state Min: 2, Max: 50 chars
bannerFileId UUID No FileThunder file ID for the shop banner Uploaded with context SHOP_BANNER
email string No Contact email Valid email, max 100 chars
countryCode string No Country code Max: 3 chars, Default: "TZ"
streetAddress string No Street address Max: 255 chars
landmark string No Landmark / location notes Max: 300 chars
latitude decimal No GPS latitude Range: -90.0 to 90.0
longitude decimal No GPS longitude Range: -180.0 to 180.0

Note: there is no shopImages field — shops only ever have a shopLogoMedia and a shopBannerMedia, never a gallery.

Request JSON Sample:

{
  "shopName": "Mama Lucy's Restaurant",
  "shopDescription": "Authentic Tanzanian cuisine in the heart of Dar es Salaam",
  "logoFileId": "f77e4567-e89b-12d3-a456-426614174777",
  "phoneNumber": "+255123456789",
  "city": "Dar es Salaam",
  "region": "Dar es Salaam",
  "bannerFileId": "f88e4567-e89b-12d3-a456-426614174888",
  "email": "info@mamalucy.co.tz",
  "countryCode": "TZ",
  "streetAddress": "Msimbazi Street, Block 45",
  "landmark": "Near the main bus stop",
  "latitude": -6.7924,
  "longitude": 39.2083
}

Response JSON Sample (ShopResponse):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Shop created successfully",
  "action_time": "2026-05-19T10:30:45",
  "data": {
    "shopId": "123e4567-e89b-12d3-a456-426614174000",
    "shopName": "Mama Lucy's Restaurant",
    "shopSlug": "mama-lucys-restaurant",
    "shopDescription": "Authentic Tanzanian cuisine...",
    "shopLogoMedia": {
      "fileId": "f77e4567-e89b-12d3-a456-426614174777",
      "status": "PROCESSING",
      "variants": null,
      "mimeType": "image/jpeg"
    },
    "shopBannerMedia": {
      "fileId": "f88e4567-e89b-12d3-a456-426614174888",
      "status": "PROCESSING",
      "variants": null,
      "mimeType": "image/jpeg"
    },
    "ownerId": "456e7890-e89b-12d3-a456-426614174001",
    "ownerName": "Lucy Mwalimu",
    "ownerProfileMedia": {
      "fileId": "p11e4567-e89b-12d3-a456-426614174p11",
      "status": "READY",
      "variants": {
        "medium": "https://cdn.example.com/profiles/owner-456/medium.jpg"
      },
      "mimeType": "image/jpeg"
    },
    "status": "ACTIVE",
    "phoneNumber": "+255123456789",
    "email": "info@mamalucy.co.tz",
    "streetAddress": "Msimbazi Street, Block 45",
    "city": "Dar es Salaam",
    "region": "Dar es Salaam",
    "countryCode": "TZ",
    "latitude": -6.7924,
    "longitude": 39.2083,
    "landmark": "Near the main bus stop",
    "isVerified": false,
    "verificationBadge": null,
    "trustScore": 0.00,
    "lastSeenTime": null,
    "isApproved": true,
    "createdAt": "2026-05-19T10:30:45",
    "updatedAt": "2026-05-19T10:30:45",
    "approvedAt": null,
    "averageRating": null,
    "totalRatings": 0,
    "totalActiveReviews": 0,
    "reviews": [],
    "isSubscribed": false,
    "subscriberCount": 0,
    "productCount": 0
  }
}

Notes:

  • shopLogoMedia/shopBannerMedia are resolved from the FileThunder Redis cache at request time — the file must have been requested via POST api/v1/files/request-upload (matching context), be owned by the caller, and be at least PROCESSING. They start out with variants: null and get upgraded to status: "READY" with resolved CDN URLs in variants asynchronously once FileThunder finishes processing, via a webhook into ShopMediaUpdater.onFileReady — re-fetch the shop to see the upgrade
  • Shops are created directly as status: "ACTIVE" and isApproved: true — there's currently no PENDING-on-create/manual-approval-required flow in code, despite 9. Approve/Reject Shop existing for admins to flip isApproved

Error Responses:

  • 400: Shop name already exists, or logoFileId/bannerFileId was never uploaded (upload expired or never started), or does not belong to the caller
  • 401: Authentication required
  • 422: Validation errors

2. Get All Shops

Endpoint: GET api/v1/e-commerce/shops/all

Access Level: 🌐 Public

Returns a list of all shops with summary info and top 5 reviews per shop.


3. Get All Shops (Paginated)

Endpoint: GET api/v1/e-commerce/shops/all-paged

Access Level: 🌐 Public

Query Parameters:

Parameter Type Default Description
page integer 1 Page number (1-based)
size integer 10 Items per page (max: 100)

Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Shops retrieved successfully",
  "action_time": "2026-05-19T10:30:45",
  "data": {
    "shops": [ "...ShopSummary fields..." ],
    "currentPage": 1,
    "pageSize": 10,
    "totalElements": 150,
    "totalPages": 15,
    "hasNext": true,
    "hasPrevious": false,
    "isFirst": true,
    "isLast": false
  }
}

4. Update Shop

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

Access Level: 🔒 Protected (Shop Owner only)

Path Parameters:

Parameter Type Description
shopId UUID ID of the shop

All request body fields are optional — only provided fields are updated:

Parameter Type Validation
shopName string Min: 2, Max: 100 chars — slug is regenerated only if the name actually changed
shopDescription string Max: 1000 chars
logoFileId UUID Replaces shopLogoMedia — same FileThunder validation as Create (context SHOP_LOGO, owned by caller)
bannerFileId UUID Replaces shopBannerMedia — context SHOP_BANNER
phoneNumber string ^\+?[0-9]{10,15}$
email string Valid email
streetAddress string Max: 255 chars
city string Min: 2, Max: 50 chars
region string Min: 2, Max: 50 chars
countryCode string Max: 3 chars
latitude decimal -90.0 to 90.0
longitude decimal -180.0 to 180.0
landmark string Max: 300 chars

Notes:

  • There is no shopImages field here either, and no way to remove a logo/banner via this endpoint (no clearLogo/clearBanner flag) — sending a new logoFileId/bannerFileId replaces the existing one, but there's no null-out path
  • Unlike product media, a replaced shopLogoMedia/shopBannerMedia is not merged/preserved — it's a straight overwrite with a freshly Redis-resolved ShopMedia

Response: Full ShopResponse (same shape as Create Shop response)

Error Responses:

  • 400: Not the shop owner, shop is deleted, or logoFileId/bannerFileId was never uploaded/doesn't belong to the caller
  • 401: Authentication required
  • 404: Shop not found

5. Get Shop by ID (Summary)

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

Access Level: 🌐 Public

Returns ShopSummaryListResponse — public-facing fields including top 5 reviews and rating.


6. Get Shop by ID (Detailed)

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

Access Level: 🔒 Protected (Shop Owner or Admin)

Returns full ShopResponse including all reviews and management fields.


7. Get My Shops

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

Access Level: 🔒 Protected

Returns all shops owned by the authenticated user (ShopSummaryListResponse list).


8. Get My Shops (Paginated)

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

Access Level: 🔒 Protected

Query Parameters:

Parameter Default Description
page 1 Page number (1-based)
size 10 Items per page (max: 100)

9. Approve / Reject Shop

Endpoint: PATCH api/v1/e-commerce/shops/{shopId}/approve-shop

Access Level: 🔒 Protected (ROLE_SUPER_ADMIN or ROLE_STAFF_ADMIN)

Query Parameters:

Parameter Type Required Description
approve boolean Yes true to approve, false to reject

Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Shop approval status changed successfully",
  "action_time": "2026-05-19T10:30:45",
  "data": {
    "shopId": "123e4567-e89b-12d3-a456-426614174000",
    "shopName": "Mama Lucy's Restaurant",
    "isApproved": true,
    "approvedAt": "2026-05-19T10:30:45"
  }
}

Endpoint: GET api/v1/e-commerce/shops/featured

Access Level: 🌐 Public

Returns up to 20 randomly selected featured shops (ShopSummaryListResponse list).


Endpoint: GET api/v1/e-commerce/shops/featured-paged

Access Level: 🌐 Public

Query Parameters:

Parameter Default Description
page 1 Page number
size 10 Items per page (max: 100)

12. Search Shops

Endpoint: GET api/v1/e-commerce/shops/search

Access Level: 🌐 Public

Query Parameters:

Parameter Type Required Default Description
q string No Search query
page integer No 1 Page number
size integer No 10 Items per page

Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Shops retrieved successfully",
  "action_time": "2026-05-19T10:30:45",
  "data": {
    "content": [ "...ShopSearchResponse fields..." ],
    "totalElements": 8,
    "totalPages": 1,
    "currentPage": 1,
    "pageSize": 10,
    "hasNext": false,
    "hasPrevious": false
  }
}

13. Get Shop Summary Stats

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

Access Level: 🌐 Public

Returns aggregated review and rating statistics for a shop, including per-user activity.

Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Shop summary stats retrieved successfully",
  "data": {
    "shopId": "123e4567-e89b-12d3-a456-426614174000",
    "shopName": "Mama Lucy's Restaurant",
    "averageRating": 4.5,
    "totalRatings": 25,
    "ratingDistribution": { "1": 1, "2": 2, "3": 5, "4": 7, "5": 10 },
    "totalReviews": 15,
    "activeReviews": 12,
    "hiddenReviews": 2,
    "flaggedReviews": 1,
    "userActivities": [
      {
        "userId": "user-123",
        "userName": "John Doe",
        "feedbackId": "rev-123",
        "reviewText": "Amazing food and service!",
        "reviewStatus": "ACTIVE",
        "ratingValue": 5,
        "date": "2026-05-19T14:30:00",
        "hasReview": true,
        "hasRating": true
      }
    ]
  }
}

14. WABA (WhatsApp Business) Integration

WABA allows shops to receive and respond to WhatsApp customer messages via an AI-powered chatbot. The flow is: shop registers a WABA → admin approves with Meta credentials → shop toggles AI on/off and monitors conversation history.

Base path for all WABA endpoints: api/v1/e-commerce/shops/{shopId}/waba

Shared Path Parameter:

Parameter Type Description
shopId UUID ID of the shop

14a. Register WABA

Purpose: Shop owner submits a WhatsApp number and display name to begin WABA registration.

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

Access Level: 🔒 Protected (Shop Owner)

Request Body:

Parameter Type Required Validation
phoneNumber string Yes Max: 20 chars
displayName string Yes Max: 100 chars

Request JSON Sample:

{
  "phoneNumber": "+255712345678",
  "displayName": "Mama Lucy's Restaurant"
}

14b. Approve WABA

Purpose: Admin approves a pending WABA registration by supplying Meta WABA credentials.

Endpoint: PATCH api/v1/e-commerce/shops/{shopId}/waba/approve

Access Level: 🔒 Protected (ROLE_SUPER_ADMIN or ROLE_STAFF_ADMIN)

Request Body:

Parameter Type Required Validation
wabaId string Yes Max: 64 chars (Meta WABA ID)
phoneNumberId string Yes Max: 64 chars (Meta Phone Number ID)
phoneNumber string Yes Max: 20 chars

14c. Resubmit WABA

Purpose: Shop owner resubmits a rejected or pending WABA with corrected info.

Endpoint: PATCH api/v1/e-commerce/shops/{shopId}/waba/resubmit

Access Level: 🔒 Protected (Shop Owner)

Request Body:

Parameter Type Validation
phoneNumber string Max: 20 chars
displayName string Max: 100 chars

14d. Admin Update WABA

Purpose: Admin updates Meta credentials on an existing WABA account.

Endpoint: PATCH api/v1/e-commerce/shops/{shopId}/waba/admin-update

Access Level: 🔒 Protected (ROLE_SUPER_ADMIN or ROLE_STAFF_ADMIN)

Same request body as Resubmit WABA.


14e. Update WABA Status

Purpose: Admin changes the status of a WABA account (e.g., suspend or reactivate).

Endpoint: PATCH api/v1/e-commerce/shops/{shopId}/waba/status

Access Level: 🔒 Protected (ROLE_SUPER_ADMIN or ROLE_STAFF_ADMIN)

Query Parameters:

Parameter Type Required Description
status ShopWabaStatus Yes PENDING, ACTIVE, SUSPENDED, REJECTED

14f. Toggle AI Chatbot

Purpose: Shop owner enables or disables the AI chatbot for their WABA.

Endpoint: PATCH api/v1/e-commerce/shops/{shopId}/waba/toggle-ai

Access Level: 🔒 Protected (Shop Owner)

Query Parameters:

Parameter Type Required Description
enabled boolean Yes true to enable, false to disable

Response JSON Sample:

{
  "success": true,
  "message": "AI enabled successfully",
  "data": {
    "shopId": "...",
    "aiEnabled": true,
    "updatedAt": "2026-05-19T11:00:00"
  }
}

14g. Get WABA Conversations

Purpose: Retrieves paginated WhatsApp conversation sessions for the shop.

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

Access Level: 🔒 Protected (Shop Owner or Admin)

Query Parameters:

Parameter Default Description
page 1 Page number
size 10 Items per page

Response JSON Sample:

{
  "success": true,
  "message": "Conversations retrieved",
  "data": {
    "content": [ "...WabaSessionResponse fields..." ],
    "currentPage": 1,
    "pageSize": 10,
    "totalElements": 42,
    "totalPages": 5,
    "hasNext": true,
    "hasPrevious": false
  }
}

14h. Get Session Messages

Purpose: Retrieves paginated messages within a specific conversation session.

Endpoint: GET api/v1/e-commerce/shops/{shopId}/waba/conversations/{sessionId}/messages

Access Level: 🔒 Protected (Shop Owner or Admin)

Additional Path Parameter:

Parameter Type Description
sessionId UUID ID of the conversation session

Query Parameters:

Parameter Default Description
page 1 Page number
size 20 Items per page

14i. Get Session Messages by Date Range

Purpose: Retrieves messages in a session filtered by date range.

Endpoint: GET api/v1/e-commerce/shops/{shopId}/waba/conversations/{sessionId}/messages/by-date

Access Level: 🔒 Protected (Shop Owner or Admin)

Additional Path Parameter:

Parameter Type Description
sessionId UUID ID of the conversation session

Query Parameters:

Parameter Type Required Description
from LocalDateTime Yes Start datetime (ISO 8601)
to LocalDateTime Yes End datetime (ISO 8601)
page integer No Default: 1
size integer No Default: 20

Quick Reference

Enums

ShopStatus: PENDING, ACTIVE, SUSPENDED, CLOSED, UNDER_REVIEW

ShopType: PHYSICAL, ONLINE, HYBRID

VerificationBadge: BRONZE, SILVER, GOLD, PREMIUM

ShopWabaStatus: PENDING, ACTIVE, SUSPENDED, REJECTED

Error Response Codes

Code Meaning
400 Business logic violation or item already exists
401 Authentication required or token invalid/expired
403 Insufficient permissions
404 Resource not found
422 Field-level validation errors

Validation Error (422):

{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "data": {
    "shopName": "Shop name must be between 2 and 100 characters",
    "phoneNumber": "Phone number must be between 10-15 digits and may start with +"
  }
}

Access Control Summary

Role Capabilities
Public Read shops, search, featured, summary stats
Authenticated user All public + create shop, view own shops
Shop Owner All authenticated + update own shop, WABA management, view conversations
ROLE_SUPER_ADMIN / ROLE_STAFF_ADMIN All + approve/reject shops, approve/update/status WABA

WABA Registration Flow

1. POST /{shopId}/waba/register       — shop owner submits phone + display name
2. PATCH /{shopId}/waba/approve       — admin supplies Meta wabaId + phoneNumberId
3. PATCH /{shopId}/waba/toggle-ai     — shop owner enables AI chatbot
4. GET  /{shopId}/waba/conversations  — shop owner monitors customer chats