Content Contexts — Events, Deliberation, Education, Projects, Marketplace
Location: apps/api/src/modules/{events,deliberation,education,projects}/ + apps/api/src/modules/economy/marketplace.*
One page for the Wave 3–6 content bounded contexts. All share the pattern: UUID-only cross-context refs, JwtAuthGuard, Zod-validated bodies, cursor-paginated lists with optional interestId filter, removedAt soft-remove for admin moderation.
Events
Aggregate roots: Event, EventRsvp. Modes online | in_person; RSVP statuses going | maybe | declined; the host auto-RSVPs as going. Capacity only constrains new going RSVPs.
| Method | Path | Description |
|---|---|---|
| POST | /events | Create (startsAt must be in the future) |
| GET | /events?interestId=&limit=&cursor= | Upcoming events |
| GET | /events/mine | Hosting or RSVP'd (going/maybe) |
| GET | /events/:id | Detail + up to 8 "going" attendees |
| POST | /events/:id/rsvp | Set my RSVP status |
| POST | /events/:id/cancel | Host cancels |
Emits events.rsvp.created { ... } when someone (not the host) goes — consumed by notifications.
Deliberation
Aggregate roots: Debate (+ arguments + votes), Problem (+ solutions + votes). Debate sides: for | against; one vote per user, changeable. Problems collect solutions; the author resolves with an accepted solution.
| Method | Path | Description |
|---|---|---|
| POST / GET | /debates | Create / list debates (motion 8–200 chars) |
| GET | /debates/:id | Detail with both sides' arguments + tallies |
| POST | /debates/:id/arguments | Add a for/against argument |
| POST | /debates/:id/vote | Vote a side |
| POST | /debates/:id/close | Author closes the debate |
| POST / GET | /problems | Create / list problems |
| GET | /problems/:id | Detail + solutions |
| POST | /problems/:id/solutions | Propose a solution |
| POST | /problems/solutions/:solutionId/vote | Upvote a solution |
| POST | /problems/:id/resolve | Author accepts a solution |
Education
Aggregate roots: Course (status draft → published), Lesson, Flashcard, enrollments. Only published courses are listable/enrollable; drafts visible to the author only.
| Method | Path | Description |
|---|---|---|
| GET | /courses / /courses/mine / /courses/recommended | Published lists / my courses / interest-fit picks |
| POST | /courses | Create draft (depthBand 1–5) |
| GET | /courses/:id | Detail (draft → author only) |
| POST | /courses/:id/lessons | Add a lesson |
| POST | /courses/:id/publish | Publish (sets publishedAt) |
| POST | /courses/:id/enroll | Enroll |
| POST | /courses/:id/lessons/:lessonId/complete | Mark lesson done |
| GET / POST | /courses/lessons/:lessonId/flashcards | List / add flashcards |
| POST | /courses/lessons/:lessonId/flashcards/generate | AI-generate cards |
Emits education.course.published, education.lesson.completed, education.course.completed.
Projects
Aggregate roots: Project (+ members). Statuses: open | active | shipped | archived.
| Method | Path | Description |
|---|---|---|
| GET | /projects / /projects/mine | List / my projects |
| POST | /projects | Create (title, summary, link, interestId) |
| GET | /projects/:id | Detail |
| POST | /projects/:id/join / /projects/:id/leave | Membership |
| POST | /projects/:id | Owner patches status/link/summary |
Marketplace
Aggregate roots: MarketOffering, MarketOrder, MarketLedgerEntry (append-only — never UPDATE). Internal mana credit only (1–10 000 mana), same EmojiWallet as /modules/economy.
Escrow flow: buyer placeOrder → buyer's wallet row is locked FOR UPDATE, mana debited into escrow (order.status = 'escrow', ledger escrow_hold) → provider markDelivered → buyer release (provider paid, ledger release) or provider refund (buyer re-credited, ledger refund). order.status is the lifecycle; the ledger is the audit trail.
| Method | Path | Description |
|---|---|---|
| GET | /market/offerings / /market/offerings/mine | Browse / my offerings |
| GET | /market/offerings/:id | Offering card |
| POST | /market/offerings | Create offering (priceMana 1–10 000) |
| POST | /market/offerings/:id/active | Toggle active |
| POST | /market/offerings/:id/order | Place order (mana → escrow) |
| GET | /market/orders/mine | My orders (as buyer or provider) |
| POST | /market/orders/:id/deliver | Provider marks delivered |
| POST | /market/orders/:id/release | Buyer releases escrow |
| POST | /market/orders/:id/refund | Provider refunds |
Emits marketplace.order.placed, marketplace.order.released.
Unified Admin Moderation
All these contexts (plus videos/audio) share one oversight surface — 8 kinds: events, debates, problems, courses, projects, videos, audio, offerings.
| Method | Path | Description |
|---|---|---|
| GET | /admin/content?kind=&page=&search=&status= | Paginated list per kind |
| POST | /admin/content/:kind/:id/remove | Soft-remove (removedAt), audit-logged |
| POST | /admin/content/:kind/:id/restore | Restore |
See also: /modules/videos, /modules/economy, /modules/discovery.