marketing-admin.routes.ts
Mount: /api/v1/admin/marketing (nested inside admin.route.ts)
Staff-only API for sponsors and coupon deals. The router is mounted as a sub-router by admin.route.ts, which already applies authMiddleware + adminMiddleware.
Guards
authMiddleware(logged in)adminMiddleware(super admin ORadminRole)requirePermission(PERMISSIONS.VIEW_STATS)on every route
All marketing reads and writes currently share one permission (
VIEW_STATS). Tighten this with a dedicatedMANAGE_MARKETINGpermission when staff specialisation is needed.
Sponsors
| Method | Path | Body / query | Purpose |
|---|---|---|---|
| GET | /sponsors?page&limit&cursor | — | List sponsors (cursor or page+limit) |
| POST | /sponsors | { name, profileLink, phone?, email? } | Create a sponsor (profile link must be http/https URL) |
| GET | /sponsors/:id | — | Fetch one sponsor |
| PATCH | /sponsors/:id | partial sponsor fields incl. notes | Update sponsor |
Deals
| Method | Path | Body / query | Purpose |
|---|---|---|---|
| GET | /deals?page&limit&cursor | — | All deals with sponsor join |
| GET | /sponsors/:sponsorId/deals?page&limit&cursor | — | Deals for one sponsor |
| POST | /sponsors/:sponsorId/deals | { startDate, type, offerAmount?, finalAmount?, status?, endDate? } | Create deal; if status === 'done', auto-assigns a unique coupon code |
| PATCH | /sponsors/:sponsorId/deals/:dealId | partial: status, amounts, endDate | Update deal; coupon is assigned/cleared as status crosses done |
Deal validation rules
status === 'pending'→endDatemust be omitted.status === 'done' | 'canceled'→endDateis required and must be a parseable date string.type∈ values exported byMARKETING_DEAL_TYPES(marketing-deal.model.ts).status∈MARKETING_DEAL_STATUSES.offerAmountandfinalAmountmay benullor non-negative numbers; the olderdealAmountfield is read for backward compatibility and surfaced in JSON asfinalAmount.
Coupon code lifecycle
- A deal becomes
done→ the service callsassignUniqueDealCouponCode(sponsorName)(seemarketing-deal-coupon.service.ts). - The generated
couponCodeis returned indealToJsonfor the admin UI. - During user onboarding (
PATCH /api/v1/users/me/onboarding), the same coupon code is validated and persisted on the user.
Pagination
All list endpoints support either:
- Page mode (deprecated) —
?page=&limit=returning{ items, total, page, limit }plus aDeprecation: trueresponse header. - Cursor mode (preferred) —
?cursor=&limit=returning{ items, nextCursor, hasMore, limit }via the sharedcursor-pagination.tsutil (sorted bycreatedAt desc, _id desc).
Clients should migrate to cursor mode; page mode remains for compatibility with the current admin UI.
Related
- Models — YouTube & content (lists
marketing-sponsor.model.ts,marketing-deal.model.ts) - Services — Other — coupon code generator
- Route map — full mount list
Last updated on