{"openapi":"3.0.3","info":{"title":"ShopDesk API","version":"1.0.0","description":"Sketch generated from lib/apiCatalog.js — verify against server.js when integrating.\n\n## Dashboard overview\nCross-cutting KPIs (orders, revenue, customers, stock, commission, etc.), admin wallets, and distance from the configured store location (miles, km, meters).\n\nWallet: GET /api/admin/wallet/me, GET /api/admin/wallet/me/transactions (ledger; use GET /api/admin/wallet/:userId/transactions for another staff id with wallet.read or Super Admin).\n\nWallet activity: history of credits and debits (Super Admin adjustments). GET /api/admin/wallet/me/transactions (same as GET /api/admin/wallet/:userId/transactions with :userId = me or a numeric staff id).\n\nAdjust: POST /api/admin/wallet/adjust (Super Admin) — writes balance and a ledger row.\n\nAdjust another admin wallet: Super Admin only — credit or debit another staff member’s USD wallet and write a ledger row. POST /api/admin/wallet/adjust with JSON userId, amountUSD, and optional note.\n\nGeo: public GET /api/geo/store; distance GET /api/geo/distance?lat=&lng= or POST /api/geo/distance with JSON lat & lng (or latitude / longitude). Store origin: GET /api/admin/store-geo (platform.read), PUT /api/admin/store-geo (shop_profile_editor: Super Admin or Operations Admin).\n\nDistance from store: 4084.239 mi · 6572.946 km (example). Preview uses rounded strings so decimals match the summary. GET /api/geo/distance returns the same distances as JSON numbers (not strings). Store and client latitude/longitude are JSON numbers in the API response; the admin live JSON preview formats them as rounded strings for display.\n\n## Audit trail\nWho changed commission, created admins, approved vendors, updated orders, adjusted stock, edited catalog overlays, payment methods, and more.\n\nAPI: GET /api/admin/audit-log (audit.read) with optional limit, action, actionPrefix.\n\nExport: GET /api/admin/audit-log/export (audit.export), query format=json or ndjson.\n\n## Stock management\nReal-time inventory for the catalogue. Only numeric stock counts are tracked; the em dash (—) means unlimited / not tracked (no checkout cap, no automatic deduction).\n\nCheckout: Paid storefront orders subtract line quantities from tracked SKUs (never below zero).\n\nThreshold: The number below is only for alerts and KPI cards on this screen — it does not change how customers buy.\n\nPermissions: View summary needs inventory.read; adjust buttons need inventory.adjust (see Roles & permissions).\n\nList / KPIs: GET /api/admin/inventory/summary?threshold= (inventory.read). Adjust: PATCH /api/admin/inventory/products/:id/stock (inventory.adjust) with { \"delta\": number } (tracked stock only), { \"stock\": number }, or { \"stock\": \"—\" } for unlimited.\n\n## Vendor management\nRegister a vendor record (KYC starts as pending).\n\nPOST /api/admin/vendors (vendors.write) registers a vendor. Pending KYC: GET /api/vendor/pending-verification (vendors.read). Approve/reject/suspend: vendors.approve (see Compliance center and endpoint list).\n\n## Assign role by user ID\nSuper Admin only — sets adminRole for an existing staff account (same users as Admin management).\n\nPUT /api/admin/assign-role with JSON userId and adminRole (slug from the role list). Bearer: Super Admin JWT.\n\n## Admin API reference\nLive table: Method, Path, RBAC, Summary — same rows as adminApiEndpoints on GET /api/catalog.\n\n- jwt — any signed-in user\n- any_admin — admin JWT with no extra permission check\n- super_admin — Super Admin only\n- Any other value — permission key, enforced with the same rules as the gateway\n\nIf adminApiEndpoints is missing or empty from GET /api/admin/roles, the matrix is loaded from public GET /api/catalog (same adminApiEndpoints array), then from /admin-api-rbac.json.\n\nRegenerate the static file when routes change:\nnode -e \"const a=require('./lib/apiCatalog');require('fs').writeFileSync('public/admin-api-rbac.json',JSON.stringify({adminApiEndpoints:a.getAdminRbacEndpoints(),rbacLegend:a.RBAC_LABELS},null,2))\""},"paths":{"/api/health":{"get":{"summary":"Service health and storefront path hints","tags":["meta"]}},"/api/catalog":{"get":{"summary":"JSON list of API endpoints","tags":["meta"]}},"/api/meta/audit-action-catalog":{"get":{"summary":"Documented audit action types (same as audit-log payload; public, no auth)","tags":["meta","audit"]}},"/api/openapi.json":{"get":{"summary":"OpenAPI 3.0 sketch (paths + bearer security)","tags":["meta"]}},"/api/commission/current-rate":{"get":{"summary":"Public default commission settings snapshot","tags":["storefront"]}},"/api/geo/store":{"get":{"summary":"Public store label and coordinates for distance (Dashboard overview — Geo).","tags":["storefront","geo"]}},"/api/geo/distance":{"get":{"summary":"Haversine distance from configured store; query lat & lng (Dashboard overview — Geo).","tags":["storefront","geo"]},"post":{"summary":"Haversine distance from configured store; JSON lat & lng or latitude & longitude (Dashboard overview — Geo).","tags":["storefront","geo"]}},"/api/public/products":{"get":{"summary":"Storefront product list","tags":["storefront"]}},"/api/public/products/{id}":{"get":{"summary":"Storefront product by id","tags":["storefront"]}},"/api/store/products":{"get":{"summary":"Alias: product list","tags":["storefront"]}},"/api/store/products/{id}":{"get":{"summary":"Alias: product by id","tags":["storefront"]}},"/shopdesk/products":{"get":{"summary":"Alias: product list","tags":["storefront"]}},"/shopdesk/products/{id}":{"get":{"summary":"Alias: product by id","tags":["storefront"]}},"/api/categories":{"get":{"summary":"Category tree (merged built-in + overlay)","tags":["storefront"]}},"/api/public/categories":{"get":{"summary":"Alias: categories","tags":["storefront"]}},"/api/currencies":{"get":{"summary":"Supported currencies","tags":["storefront"]}},"/api/countries":{"get":{"summary":"Countries for checkout","tags":["storefront"]}},"/api/public/payment-methods":{"get":{"summary":"Enabled payment methods (optional ?country=XX)","tags":["storefront"]}},"/api/public/country-payment-reference":{"get":{"summary":"Informational payment landscape + doc links for a country (?country=XX); not legal advice; see disclaimer in JSON","tags":["storefront"]}},"/api/public/payment-mock/authorize":{"post":{"summary":"Demo-only fake authorization id (body: countryCode, paymentMethodId, optional orderNumber & amountUSD); no real PSP","tags":["storefront"]}},"/api/public/orders":{"post":{"summary":"Place order (validated server-side)","tags":["storefront"]}},"/api/store/orders":{"post":{"summary":"Alias: place order","tags":["storefront"]}},"/shopdesk/orders":{"post":{"summary":"Alias: place order","tags":["storefront"]}},"/api/auth/register":{"post":{"summary":"Register customer account","tags":["auth"]}},"/api/auth/login":{"post":{"summary":"Login; returns Bearer JWT","tags":["auth"]}},"/api/auth/reactivate":{"post":{"summary":"Reactivate cancelled account","tags":["auth"]}},"/api/auth/logout":{"post":{"summary":"Revoke current session (PG)","tags":["auth"],"security":[{"bearerAuth":[]}]}},"/api/admin/access-request":{"post":{"summary":"Submit operator/platform access request for Super Admin review","tags":["admin","access"],"security":[{"bearerAuth":[]}]}},"/api/admin/access-request/{id}/decision":{"post":{"summary":"Approve or reject a pending access request","tags":["admin","access"],"security":[{"bearerAuth":[]}]}},"/api/auth/cancel":{"post":{"summary":"Cancel own customer account","tags":["auth"],"security":[{"bearerAuth":[]}]}},"/api/auth/profile":{"patch":{"summary":"Update profile (e.g. displayName)","tags":["auth"],"security":[{"bearerAuth":[]}]}},"/api/auth/me":{"get":{"summary":"Current user","tags":["auth"],"security":[{"bearerAuth":[]}]}},"/api/auth/orders":{"get":{"summary":"Customer orders for JWT email (payment details redacted)","tags":["auth"],"security":[{"bearerAuth":[]}]}},"/api/auth/orders/{orderNumber}":{"get":{"summary":"Single customer order if email matches","tags":["auth"],"security":[{"bearerAuth":[]}]}},"/api/products":{"get":{"summary":"Product list (any authenticated user)","tags":["products"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Create product","tags":["products"],"security":[{"bearerAuth":[]}]}},"/api/products/stats":{"get":{"summary":"Aggregate product stats","tags":["products"],"security":[{"bearerAuth":[]}]}},"/api/products/{id}":{"get":{"summary":"Product by id","tags":["products"],"security":[{"bearerAuth":[]}]},"put":{"summary":"Replace product","tags":["products"],"security":[{"bearerAuth":[]}]},"patch":{"summary":"Patch product","tags":["products"],"security":[{"bearerAuth":[]}]},"delete":{"summary":"Delete product","tags":["products"],"security":[{"bearerAuth":[]}]}},"/api/orders":{"get":{"summary":"All orders (admin)","tags":["orders"],"security":[{"bearerAuth":[]}]}},"/api/orders/{orderNumber}":{"get":{"summary":"Order detail","tags":["orders"],"security":[{"bearerAuth":[]}]}},"/api/admin/customer-checkout-payments":{"get":{"summary":"Sanitized payment method rows from storefront orders for an email (query email, optional limit)","tags":["orders","admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/orders/{orderNumber}/status":{"patch":{"summary":"Update fulfilment status","tags":["orders"],"security":[{"bearerAuth":[]}]}},"/api/admin/dashboard":{"get":{"summary":"Legacy dashboard JSON (prefer platform-overview; see getCatalog().dashboardOverview)","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/platform-overview":{"get":{"summary":"Cross-cutting KPIs: orders, revenue, customers, stock, commission, etc. (Dashboard overview intro applies.)","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/tax-settings":{"get":{"summary":"VAT/tax shop settings","tags":["admin","tax"],"security":[{"bearerAuth":[]}]},"put":{"summary":"Update tax settings (home country, VAT number, inclusive flag)","tags":["admin","tax"],"security":[{"bearerAuth":[]}]}},"/api/admin/notification-settings":{"get":{"summary":"Admin notification toggles, announcements queue, flash sale","tags":["admin","notifications"],"security":[{"bearerAuth":[]}]},"put":{"summary":"Update notification settings","tags":["admin","notifications"],"security":[{"bearerAuth":[]}]}},"/api/admin/notifications/announce":{"post":{"summary":"Push custom in-app announcement to customers","tags":["admin","notifications"],"security":[{"bearerAuth":[]}]}},"/api/admin/notification-settings/clear-log":{"post":{"summary":"Clear full admin notification log","tags":["admin","notifications"],"security":[{"bearerAuth":[]}]}},"/api/admin/notification-settings/clear-announcements":{"post":{"summary":"Clear announcement rows and queued announcements only","tags":["admin","notifications"],"security":[{"bearerAuth":[]}]}},"/api/admin/notification-settings/clear-pending-approvals":{"post":{"summary":"Clear only pending approval_request rows","tags":["admin","notifications"],"security":[{"bearerAuth":[]}]}},"/api/public/tax/config":{"get":{"summary":"Tax settings + country row (?country=)","tags":["storefront","tax"]}},"/api/public/tax/preview":{"post":{"summary":"Preview order tax (subtotal, shipping, country, categories)","tags":["storefront","tax"]}},"/api/public/notification-context":{"get":{"summary":"Public notification toggles + announcements (each includes replies[] for announcement threads)","tags":["storefront","notifications"]}},"/api/auth/notification-log/{id}/reply":{"post":{"summary":"Shopper reply on an announcement thread (role=customer; id matches announcement / log row)","tags":["auth","notifications","storefront"],"security":[{"bearerAuth":[]}]}},"/api/admin/wallet/me":{"get":{"summary":"Current admin USD wallet balance (Dashboard overview — Wallet: pair with …/wallet/me/transactions for ledger).","tags":["admin","wallet"],"security":[{"bearerAuth":[]}]}},"/api/admin/wallet/me/transactions":{"get":{"summary":"Wallet ledger for the current admin (Dashboard overview — Wallet activity).","tags":["admin","wallet"],"security":[{"bearerAuth":[]}]}},"/api/admin/wallet/{userId}":{"get":{"summary":"Admin wallet balance for userId (self, Super Admin, or wallet.read; Dashboard overview — Wallet).","tags":["admin","wallet"],"security":[{"bearerAuth":[]}]}},"/api/admin/wallet/adjust":{"post":{"summary":"Super Admin — writes balance and a ledger row (body: userId, amountUSD, optional note; Dashboard overview — Adjust).","tags":["admin","wallet"],"security":[{"bearerAuth":[]}]}},"/api/admin/wallet/{userId}/transactions":{"get":{"summary":"Wallet ledger for a staff admin userId; use a numeric :userId with wallet.read or Super Admin (Dashboard overview — Wallet activity).","tags":["admin","wallet"],"security":[{"bearerAuth":[]}]}},"/api/admin/store-geo":{"get":{"summary":"Store origin (label, lat, lng, updatedAt) for distance APIs; platform.read (Dashboard overview — Geo).","tags":["admin","geo"],"security":[{"bearerAuth":[]}]},"put":{"summary":"Set store latitude, longitude, label (Shop profile editor: Super Admin or Operations Admin).","tags":["admin","geo"],"security":[{"bearerAuth":[]}]}},"/api/admin/shop/restamp-products":{"post":{"summary":"Apply current shop name/logo stamp to all products (Shop profile editor role).","tags":["admin","geo"],"security":[{"bearerAuth":[]}]}},"/api/admin/roles":{"get":{"summary":"Role definitions, glossary, permission patterns, admin API matrix","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/permissions/check":{"post":{"summary":"Whether current admin JWT allows a permission key (no separate permission; admin role required)","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/staff":{"get":{"summary":"List admin users","tags":["admin"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Create staff admin","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/list":{"get":{"summary":"Alias: list admins","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/create":{"post":{"summary":"Alias: create staff","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/staff/{id}":{"patch":{"summary":"Update staff (role, 2FA flag, status)","tags":["admin"],"security":[{"bearerAuth":[]}]},"delete":{"summary":"Delete staff","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/staff/{id}/reset-password":{"post":{"summary":"Set staff password","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/assign-role":{"put":{"summary":"Assign adminRole by userId","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/revoke-role":{"delete":{"summary":"Suspend admin by userId","tags":["admin"],"security":[{"bearerAuth":[]}]}},"/api/admin/commission":{"get":{"summary":"Commission settings","tags":["commission"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Update commission settings","tags":["commission"],"security":[{"bearerAuth":[]}]}},"/api/commission/set-rate":{"post":{"summary":"Update rate (legacy body)","tags":["commission"],"security":[{"bearerAuth":[]}]}},"/api/admin/commission/category-rules":{"get":{"summary":"Per-category rate overrides","tags":["commission"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Upsert category rule","tags":["commission"],"security":[{"bearerAuth":[]}]}},"/api/admin/commission/category-rules/{category}":{"delete":{"summary":"Remove category rule","tags":["commission"],"security":[{"bearerAuth":[]}]}},"/api/admin/vendors":{"get":{"summary":"All vendors","tags":["vendors"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Register vendor (optional businessTin)","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/vendor/pending-verification":{"get":{"summary":"Pending KYC vendors","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/admin/vendors/{id}":{"patch":{"summary":"Update vendor profile / TIN / taxVerified","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/vendor/approve":{"post":{"summary":"Approve vendor (body vendorId)","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/vendor/reject":{"post":{"summary":"Reject vendor","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/admin/vendors/{id}/approve":{"post":{"summary":"Approve by path id","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/admin/vendors/{id}/reject":{"post":{"summary":"Reject by path id","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/admin/vendors/{id}/suspend":{"post":{"summary":"Suspend vendor","tags":["vendors"],"security":[{"bearerAuth":[]}]}},"/api/admin/security/login-events":{"get":{"summary":"Recent login attempts","tags":["security"],"security":[{"bearerAuth":[]}]}},"/api/admin/security/sessions":{"get":{"summary":"Active sessions (PG)","tags":["security"],"security":[{"bearerAuth":[]}]}},"/api/admin/security/sessions/{id}":{"delete":{"summary":"Revoke session by jti/id","tags":["security"],"security":[{"bearerAuth":[]}]}},"/api/admin/audit-log":{"get":{"summary":"List audit entries + actorEmail enrichment + action catalog (see getCatalog().auditTrail, auditTrail.actorEmail)","tags":["audit"],"security":[{"bearerAuth":[]}]}},"/api/admin/audit-log/export":{"get":{"summary":"Export audit entries with same actorEmail rules as list (format=json|ndjson; see getCatalog().auditTrail.actorEmail)","tags":["audit"],"security":[{"bearerAuth":[]}]}},"/api/admin/catalog-categories":{"get":{"summary":"Merged categories + overlay keys","tags":["catalog"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Upsert overlay category","tags":["catalog"],"security":[{"bearerAuth":[]}]},"delete":{"summary":"Delete overlay (?name=)","tags":["catalog"],"security":[{"bearerAuth":[]}]}},"/api/admin/payment-methods":{"get":{"summary":"All payment method configs","tags":["payments"],"security":[{"bearerAuth":[]}]},"post":{"summary":"Create method","tags":["payments"],"security":[{"bearerAuth":[]}]}},"/api/admin/payment-methods/{id}":{"put":{"summary":"Replace method","tags":["payments"],"security":[{"bearerAuth":[]}]},"delete":{"summary":"Delete method","tags":["payments"],"security":[{"bearerAuth":[]}]}},"/api/admin/payment-methods/reset-catalog":{"post":{"summary":"Reset to built-in catalogue","tags":["payments"],"security":[{"bearerAuth":[]}]}},"/api/admin/inventory/summary":{"get":{"summary":"Stock KPIs + low-stock list (see getCatalog().stockManagement)","tags":["inventory"],"security":[{"bearerAuth":[]}]}},"/api/admin/inventory/products/{id}/stock":{"patch":{"summary":"Delta (tracked only), absolute number, or stock \"—\" for unlimited","tags":["inventory"],"security":[{"bearerAuth":[]}]}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}}