22 KiB
title, tags
| title | tags | |||
|---|---|---|---|---|
| Marketplace API |
|
Marketplace API
Last updated: 2026-05-31 — request-template delivery mode and payment rail validation updated.
All marketplace endpoints live under /api/marketplace/*. The router is composed of several files mounted from app.ts:
- New controller-pattern routes:
backend/src/services/marketplace/controllerRoutes.ts(marketplaceControllerRouter) - Legacy + delivery-code routes:
backend/src/services/marketplace/routes.ts(marketplaceRouter) - Request templates:
requestTemplateRoutes.ts(/api/marketplace/request-templates/*) - Shop settings:
shopSettingsRoutes.ts(/api/marketplace/shop/*) - Reviews:
reviewRoutes.ts(/api/marketplace/reviews/*)
Core models: PurchaseRequest, SellerOffer, Category, RequestTemplate, ShopSettings, Review. Real-time updates are emitted on the request-<id>, seller-<id>, buyer-<id>, and sellers/buyers rooms — see Socket Events.
Categories
GET /api/marketplace/categories
Description: Flat list of all categories.
Auth required: No
Response 200: { success: true, data: [Category, ...] }
GET /api/marketplace/categories/tree
Description: Nested tree based on parentId.
Auth required: No
GET /api/marketplace/categories/:id
Description: Single category by id.
Auth required: No
Errors: 404 not found.
Sellers
GET /api/marketplace/sellers
Description: Public seller directory (users with role: 'seller'). Includes aggregated stats (completed orders, ratings).
Auth required: No
GET /api/marketplace/request-templates/sellers
Description: Sellers who currently expose at least one active RequestTemplate (used by the shop discovery page). Auth required: No
GET /api/marketplace/request-templates/sellers/:sellerId
Description: Public shop profile for a seller, including their templates and shop settings. Auth required: No
Purchase Requests
The buyer-facing CRUD plus seller-side workflow endpoints. Model: PurchaseRequest.
POST /api/marketplace/purchase-requests
Description: Create a purchase request (new controller path). Auth required: Bearer JWT (buyer) Request body:
{
title: string;
description: string;
categoryId: string;
productLink?: string;
size?: string;
color?: string;
quantity?: number; // default 1
budget?: { min?: number; max?: number; currency: "USD" | "EUR" | "IRR" | "USDT" | "USDC" };
urgency?: "low" | "medium" | "high" | "urgent";
deliveryInfo?: {
deliveryType: "physical" | "online";
addressId?: string; // when physical
email?: string; // when online
};
preferredSellerIds?: string[];
attachments?: string[]; // URLs from [[File API]]
}
Response 201: { success, data: { purchaseRequest } }
Side effects: Emits new-purchase-request to the sellers room (broadcast).
Source: marketplaceController.createPurchaseRequest
POST /api/marketplace/purchase-requests/bulk
Description: Bulk creation (used by the template checkout flow).
Auth required: Bearer JWT
Request body: { items: Array<PurchaseRequestInput> }
GET /api/marketplace/purchase-requests
Description: List requests with filters. Buyers see their own; sellers see ones routed to them; admins see all.
Auth required: Bearer JWT
Query params: status, categoryId, urgency, search, page, limit, sortBy, sortOrder
Note: Use query params on this endpoint for filtering/searching. The separate search and stats endpoints documented in earlier versions do not exist — see below.
⚠️ NOT IMPLEMENTED: GET /api/marketplace/purchase-requests/search
This endpoint does not exist. Use query params (search, status, categoryId, etc.) on GET /api/marketplace/purchase-requests instead.
⚠️ NOT IMPLEMENTED: GET /api/marketplace/purchase-requests/stats
This endpoint does not exist in the backend.
GET /api/marketplace/purchase-requests/my
Description: Shortcut for the caller's own purchase requests. Auth required: Bearer JWT
GET /api/marketplace/purchase-requests/:id
Description: Single request with populated category and selected offer.
Auth required: No (public read for shareable links)
Errors: 404 not found.
PATCH /api/marketplace/purchase-requests/:id
Description: Buyer edits draft / pending request fields. Auth required: Bearer JWT (owner)
⚠️ KNOWN BUG: The frontend sends
PUTbut the backend registersPATCH. Requests from clients usingPUTwill receive404. UsePATCH.
PATCH /api/marketplace/purchase-requests/:id/status
Description: Transition the request status (draft → pending → payment → processing → delivery → delivered → seller_paid → completed, or cancelled).
Auth required: Bearer JWT (owner or admin)
Request body: { status: string }
Side effects: Emits purchase-request-update to request-<id>.
DELETE /api/marketplace/purchase-requests/:id
Description: Cancel/delete a request that has no committed payment. Auth required: Bearer JWT (owner)
GET /api/marketplace/purchase-requests/:id/workflow-steps
Description: Returns the ordered workflow steps + current pointer used by the frontend stepper. Auth required: No
GET /api/marketplace/purchase-requests/:id/payment-status
Description: Returns the latest payment + escrow state for the request. Auth required: Bearer JWT
POST /api/marketplace/purchase-requests/:id/sync-payment-status
Description: Force a re-check against the payment provider (Payment API). Auth required: Bearer JWT
POST /api/marketplace/purchase-requests/:id/confirm-payment
Description: Legacy buyer-confirmation endpoint kept for old clients. Auth required: Bearer JWT
POST /api/marketplace/purchase-requests/:id/release-payment
Description: Triggers admin escrow release (mirror of POST /api/payment/shkeeper/:id/release).
Auth required: Bearer JWT (admin)
PUT /api/marketplace/purchase-requests/:id/delivery (controller route)
Description: Seller submits shipping details (carrier, tracking number, expected date). Auth required: Bearer JWT (selected seller)
POST /api/marketplace/purchase-requests/:id/update-delivery (legacy)
Description: Older equivalent retained for compatibility.
POST /api/marketplace/requests/:id/start-delivery
Description: Marks the request as delivery and notifies the buyer.
Auth required: Bearer JWT (seller)
Side effects: Emits purchase-request-update event.
PATCH /api/marketplace/purchase-requests/:id/confirm-delivery
Description: Buyer confirms goods received (transitions to delivered).
Auth required: Bearer JWT (buyer)
POST /api/marketplace/purchase-requests/:id/final-approval
Description: Buyer's final approval that releases escrow to the seller. Auth required: Bearer JWT (buyer)
GET /api/marketplace/buyers/:buyerId/purchase-requests
Description: Admin/seller view of a buyer's request history. Auth required: Bearer JWT
Delivery codes
Six-digit codes the buyer hands to the seller at handover. Backed by deliveryService.
POST /api/marketplace/purchase-requests/:id/delivery-code/generate
Description: Buyer generates a delivery code. Request must be in delivery status.
Auth required: Bearer JWT (buyer)
Response 200: { success: true, data: { deliveryCode: "123456" } }
Errors: 400 wrong status, 403 not buyer, 404 request not found.
Side effects: Emits delivery-code-generated on request-<id>.
POST /api/marketplace/purchase-requests/:id/delivery-code/verify
Description: Seller verifies the code. On success the status moves to delivered.
Auth required: Bearer JWT (selected seller)
Request body: { code: string } (exactly 6 chars)
Errors: 400 bad code, 403 not the selected seller.
Side effects: Emits delivery-confirmed and buyer-confirmed-delivery.
GET /api/marketplace/purchase-requests/:id/delivery-code
Description: Buyer or seller fetches the current code metadata. Auth required: Bearer JWT
GET /api/marketplace/purchase-requests/:id/delivery-code/status
Description: Returns { isValid, hasDeliveryCode, ... } so the UI can decide whether to show "regenerate".
Auth required: Bearer JWT
Seller Offers
Model: SellerOffer.
Valid status values: pending | accepted | rejected | withdrawn
Note: The status value
activedoes not exist on SellerOffer. Earlier docs were incorrect.
POST /api/marketplace/purchase-requests/:id/offers
Description: Submit an offer against the purchase request identified by :id in the path. The purchase request must be in pending, received_offers, or active status.
Auth required: Bearer JWT (seller)
Path param: :id — the purchaseRequestId (not a body field)
Request body:
{
price: { amount: number; currency: "USDT" }; // USDT only for escrow MVP
deliveryEstimate: { days: number; note?: string };
notes?: string;
attachments?: string[];
}
Response 201: { success, data: { offer } }
Side effects: Emits new-offer to buyer-<buyerId> and seller-offer-update to seller-<sellerId>.
Note: Currency is locked to
USDTfor the escrow MVP (commit 3aaa2fe). The frontendCURRENCY_SYMBOLSmap insrc/sections/request/constants.tsexposes onlyUSDT.
PUT /api/marketplace/purchase-requests/:id/offers (legacy)
Description: Older offer-update endpoint kept for compatibility.
GET /api/marketplace/purchase-requests/:id/offers
Description: List all offers for a request. Auth required: No (buyers and prospective sellers compare offers)
GET /api/marketplace/purchase-requests/:id/has-offer
Description: Returns { hasOffer: boolean } for the caller (seller-side helper).
Auth required: Bearer JWT (seller)
GET /api/marketplace/purchase-requests/:id/offers/:sellerId
Description: Fetch a specific seller's offer on a request. Auth required: No
⚠️ NOT IMPLEMENTED: GET /api/marketplace/offers/request/:requestId
This endpoint does not exist. Use GET /api/marketplace/purchase-requests/:id/offers instead.
GET /api/marketplace/offers/seller/:sellerId
Description: Returns all offers submitted by the given seller, across all purchase requests. Used by the Offer Management dashboard page (/dashboard/seller/marketplace/offers).
Auth required: Bearer JWT (seller, own :sellerId only)
Response 200: { data: [SellerOffer, ...] }
Frontend action: getSellerOffers(sellerId) in src/actions/marketplace.ts (added commit 240a668)
PATCH /api/marketplace/offers/:id
Description: Seller edits their pending offer (price, delivery estimate, notes). Auth required: Bearer JWT (offer owner)
✅ Fixed (commit 240a668): The frontend
updateOfferandacceptOfferactions now correctly sendPATCH.
DELETE /api/marketplace/offers/:id
Description: Seller withdraws their offer. Auth required: Bearer JWT (offer owner)
PUT /api/marketplace/offers/:id/status
Description: Direct status mutation (admin override / counter-offer states). This is also the correct way to withdraw an offer programmatically — send { status: 'withdrawn' }.
Auth required: Bearer JWT
Request body: { status: "pending" | "accepted" | "rejected" | "withdrawn" }
POST /api/marketplace/offers/:id/withdraw
Description: Seller withdraws their offer. Sets offer status to withdrawn using sellerOfferService.withdrawOffer(). Only the offer owner may call this.
Auth required: Bearer JWT (offer owner)
Response 200: { success: true, data: { /* updated offer */ } }
Errors: 403 not the offer owner, 404 offer not found.
Note: This endpoint was previously documented as NOT IMPLEMENTED. It was added to
backend/src/services/marketplace/routes.ts(commit3e47713).
POST /api/marketplace/purchase-requests/:id/select-offer
Description: Buyer selects/accepts an offer; this triggers payment intent creation in Payment API and rejects all other offers automatically once payment lands.
Auth required: Bearer JWT (buyer)
Request body: { offerId: string }
Side effects:
- Persists
selectedOfferIdon PurchaseRequest (commit023255f— previously this field was not saved, causing it to be lost). Status moves towardpayment. - Rejects all losing offers (sets their status to
rejected) when payment is confirmed (commit023255f). - Emits
seller-offer-updateto all sellers for the request.
POST /api/marketplace/offers/:id/accept (legacy)
Description: Older synonym retained for backward compatibility with old clients.
DELETE /api/marketplace/offers/:id (controller route)
Description: Controller-pattern delete that also notifies the buyer.
Request Templates
A RequestTemplate is a re-usable "shop product" a seller can publish. Buyers convert templates into actual purchase requests via the shareable link, individually or in bulk (cart checkout).
POST /api/marketplace/request-templates
Description: Create a new template. Auth required: Bearer JWT (seller) Request body:
{
title: string; // 1-200 chars
description: string; // 1-2000
categoryId: string; // MongoId
productLink?: string; // valid URL
size?: string; // <=100
color?: string; // <=100
quantity?: number; // 1-10000
budget?: { min?: number; max?: number; currency: "USD" | "EUR" | "IRR" | "USDT" | "USDC" };
urgency?: "low" | "medium" | "high" | "urgent";
deliveryInfo?: {
deliveryType: "physical" | "online"; // seller-selected; buyer cannot override at checkout
notes?: string;
email?: string; // optional legacy field; empty string is accepted
};
paymentConfig?: {
useShopDefault: boolean; // false = template override, true = shop defaults
allowedChains: number[]; // at least one positive chain id when paymentConfig is sent
allowedTokens: string[]; // at least one non-empty token symbol when paymentConfig is sent
};
maxUsage?: number | null; // 0/null = unlimited
expiresAt?: string | null; // ISO date
images?: string[]; // URLs from [[File API]]
}
Response 201: { data: { template } } with a generated shareableLink.
Validation: If paymentConfig is present, both allowedChains and allowedTokens must be non-empty. The UI now defaults new templates to explicit template rails, so a seller must choose at least one chain and one token before publishing.
GET /api/marketplace/request-templates
Description: Paginated list of the caller's templates.
Auth required: Bearer JWT (seller)
Query params: page, limit (1-100), isActive, categoryId, search
GET /api/marketplace/request-templates/stats
Description: Aggregate counts of the caller's templates (active, expired, usage). Auth required: Bearer JWT (seller)
GET /api/marketplace/request-templates/:id
Description: Full template by id (owner view). Auth required: Bearer JWT (owner)
PUT /api/marketplace/request-templates/:id
Description: Update the template. Same body as create. Auth required: Bearer JWT (owner)
DELETE /api/marketplace/request-templates/:id
Description: Delete a template. Auth required: Bearer JWT (owner)
PATCH /api/marketplace/request-templates/:id/toggle-status
Description: Toggle isActive.
Auth required: Bearer JWT (owner)
GET /api/marketplace/request-templates/public/:shareableLink
Description: Public read of a template via its shareable slug. Used by the shop preview page.
Auth required: No
Errors: 404 link not found or template inactive/expired.
POST /api/marketplace/request-templates/:shareableLink/convert
Description: Buyer converts the template into a real PurchaseRequest.
Auth required: Bearer JWT (buyer)
Request body: Overrides (quantity, address, etc.)
Response 201: { data: { purchaseRequest } }
POST /api/marketplace/request-templates/batch-convert
Description: Convert several templates at once (cart checkout). The seller's template delivery mode is preserved; buyer-supplied checkout details are only overlaid where that mode requires them. Auth required: Bearer JWT (buyer) Request body:
{
items: Array<{
shareableLink: string;
quantity: number; // 1-100
sellerId: string; // MongoId
}>;
status?: "pending" | "pending_payment" | "active";
paymentConfirmed?: boolean;
paymentData?: Record<string, unknown>;
deliveryInfo?: {
email?: string; // copied to generated online requests
billing?: {
name?: string;
phoneNumber?: string;
address?: string;
city?: string;
state?: string;
country?: string;
zipCode?: string;
addressType?: string;
fullAddress?: string; // copied to generated physical requests
};
};
}
Delivery mapping: online templates use deliveryInfo.email; physical templates use deliveryInfo.billing to fill deliveryInfo.address and deliveryInfo.deliveryAddress on the generated PurchaseRequest.
POST /api/marketplace/request-templates/complete-payment
Description: Marks the requests created by batch-convert as paid (called after a successful checkout).
Auth required: Bearer JWT
Request body:
{
requestIds: string[]; // 1+ MongoIds
newStatus?: "pending" | "active" | "processing";
paymentData?: Record<string, unknown>;
}
Shop Settings
Per-seller storefront preferences. Model: ShopSettings.
GET /api/marketplace/shop/settings/:sellerId
Description: Public shop settings for the given seller (used by the shop landing page).
Auth required: No
Response 200: { data: ShopSettings }
GET /api/marketplace/shop/settings
Description: The authenticated seller's own settings. Auth required: Bearer JWT (seller)
PUT /api/marketplace/shop/settings
Description: Update shop settings (banner, bio, policies, allowSellerReviews, allowTemplateReviews, ...).
Auth required: Bearer JWT (seller)
Reviews
Model: Review. Reviews can target a seller or a template. Subject must be seller or template.
GET /api/marketplace/reviews/:subjectType/:subjectId
Description: Published reviews + aggregate stats. Honours allowSellerReviews / allowTemplateReviews from ShopSettings.
Auth required: No
Query params: page (default 1), limit (default 10)
Response 200:
{
"data": [Review, ...],
"pagination": { "page": 1, "limit": 10, "total": 42, "pages": 5 },
"stats": { "count": 42, "avg": 4.6, "one": 1, "two": 0, "three": 3, "four": 10, "five": 28 }
}
Errors: 400 bad subjectType / invalid id, 403 reviews disabled by seller.
GET /api/marketplace/reviews/:subjectType/:subjectId/summary
Description: Stats only (no review list). Auth required: No
POST /api/marketplace/reviews
Description: Submit a review. The server computes isVerifiedBuyer when purchaseRequestId is given and the request is in a terminal state (delivery, delivered, seller_paid, completed).
Auth required: Bearer JWT
Request body:
{
subjectType: "seller" | "template";
subjectId: string;
rating: 1 | 2 | 3 | 4 | 5;
comment?: string;
purchaseRequestId?: string;
}
Errors: 400 validation, 403 reviews disabled, 404 template not found, 409 duplicate review.
Response 201: { data: Review, stats: { ... } }
Payments (legacy under marketplace)
These routes are duplicates of the main Payment API kept under /api/marketplace/payments/* for backward compatibility with the early frontend. Prefer the canonical endpoints.
POST /api/marketplace/payments
GET /api/marketplace/payments
GET /api/marketplace/payments/:paymentId
PATCH /api/marketplace/payments/:paymentId
See Payment API for the canonical descriptions.
Verify Web3 payment (legacy)
POST /api/marketplace/payments/verify
Description: Legacy Web3 verification endpoint that records a transaction and moves the purchase request to processing. Modern flows use POST /api/payment/shkeeper/confirm-transaction instead.
Auth required: Bearer JWT
Request body:
{
purchaseRequestId: string;
sellerOfferId: string;
buyerId: string;
sellerId: string;
amount: number;
currency: "USDT" | string;
paymentHash: string;
paymentMethod?: string;
token?: string;
network?: string;
escrowWalletAddress?: string;
metadata?: Record<string, unknown>;
}
Side effects: Creates a Payment record, updates PurchaseRequest status, emits payment-received to user-<sellerId>.
Real-time events
Most marketplace mutations fan out via global.io to the rooms below — see Socket Events for payloads:
purchase-request-update→request-<id>new-purchase-request→sellersnew-offer→buyer-<buyerId>seller-offer-update→seller-<sellerId>(and global on payment confirm)delivery-code-generated/delivery-confirmed/delivery-update→request-<id>request-cancelled→user-<buyerId>,user-<sellerId>transaction-completed→user-<buyerId>,user-<sellerId>