Shop Management
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_ADMINorROLE_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:
- Client calls
POST api/v1/files/request-uploadwithcontext: "SHOP_LOGO"or"SHOP_BANNER"→ gets back a presigned upload URL and afileId - Client
PUTs the raw file bytes directly to that URL - Client sends that
fileIdaslogoFileId/bannerFileIdwhen creating/updating the shop — validated at request time against the FileThunder Redis cache (ownership + expected context), not necessarily fully processed yet - FileThunder finishes processing (virus scan + variant generation) asynchronously and webhooks back into
ShopMediaUpdater.onFileReady, which upgrades the storedshopLogoMedia/shopBannerMediatostatus: READYwith resolvedvariants
- There is no shop image gallery (
shopImages) field — only a singleshopLogoMediaand singleshopBannerMedia, each aShopMediaobject (fileId,status,variants,mimeType). URLs are assembled fresh on every read viaFileThunderUrlAssembler, never stored as raw strings - Owner/user avatars follow the same rule:
ownerProfileMediaonShopResponse(single-shop detail view) is a fullProfileMediaobject (fileId,status,variants,mimeType) resolved viaFileThunderUrlAssembler.assembleProfileMedia().userProfileMediaon review responses and on subscriber responses (list/card contexts) is instead the leanUserProfilePrimaryMedia(fileId,status,thumbUrl,mimeType) resolved viaFileThunderUrlAssembler.getUserProfilePrimaryMedia()— a single resolved thumbnail URL instead of the full variants map. Neither is ever a rawavatarUrl/userAvatarUrlstring
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 |
| 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/shopBannerMediaare resolved from the FileThunder Redis cache at request time — the file must have been requested viaPOST api/v1/files/request-upload(matching context), be owned by the caller, and be at leastPROCESSING. They start out withvariants: nulland get upgraded tostatus: "READY"with resolved CDN URLs invariantsasynchronously once FileThunder finishes processing, via a webhook intoShopMediaUpdater.onFileReady— re-fetch the shop to see the upgrade- Shops are created directly as
status: "ACTIVE"andisApproved: true— there's currently noPENDING-on-create/manual-approval-required flow in code, despite 9. Approve/Reject Shop existing for admins to flipisApproved
Error Responses:
400: Shop name already exists, orlogoFileId/bannerFileIdwas never uploaded (upload expired or never started), or does not belong to the caller401: Authentication required422: 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}$ |
| 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
shopImagesfield here either, and no way to remove a logo/banner via this endpoint (noclearLogo/clearBannerflag) — sending a newlogoFileId/bannerFileIdreplaces the existing one, but there's no null-out path - Unlike product media, a replaced
shopLogoMedia/shopBannerMediais not merged/preserved — it's a straight overwrite with a freshly Redis-resolvedShopMedia
Response: Full ShopResponse (same shape as Create Shop response)
Error Responses:
400: Not the shop owner, shop is deleted, orlogoFileId/bannerFileIdwas never uploaded/doesn't belong to the caller401: Authentication required404: 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"
}
}
10. Get Featured Shops
Endpoint: GET api/v1/e-commerce/shops/featured
Access Level: 🌐 Public
Returns up to 20 randomly selected featured shops (ShopSummaryListResponse list).
11. Get Featured Shops (Paginated)
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
| 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
No comments to display
No comments to display