Task: Direct Messaging Backend — Conversations, Cursor Messages, Read State (Hard)
Overview
No messaging layer exists. Deliver a production-shaped DM stub API: unique conversations, participant authz, cursor pagination, read receipts, rate limits, and abuse guards — enough for the hardened frontend messages UI.
Scope (must all ship)
Entities
Conversation: fanId, creatorId, unique pair, lastMessageAt, optional lastMessagePreview
Message: conversationId, senderId, body, createdAt, readAt, optional clientId for idempotent send
Endpoints
POST /api/v1/conversations — fan starts (or get-or-create) with creator
GET /api/v1/conversations/me?cursor&limit — inbox ordered by lastMessageAt
GET /api/v1/conversations/:id/messages?cursor&limit
POST /api/v1/conversations/:id/messages — participant; supports Idempotency-Key/clientId
POST /api/v1/conversations/:id/read — mark messages read up to timestamp/id
Rules
- Non-participant →
403
- Duplicate pair reuses conversation
- Body 1..2000 chars; strip/reject empty
- Optional: only fans may initiate; creators reply (document)
- Rate limit sends per user (in-memory/token bucket stub OK)
Pagination
- Messages oldest→newest within page; cursor stable
- Inbox pagination
Safety
- Block soft-deleted users from new messages
- Do not leak other conversations via sequential ids (404 vs 403 policy documented)
Testing (≥14)
- Get-or-create uniqueness
- Two-way exchange
- Non-participant forbidden
- Validation errors
- Cursor pages
- Read receipt updates
- Idempotent send
- Rate limit trip (unit)
- Unauth 401
Deliverables
MessagingModule, migrations, Swagger, docs/messaging.md, ≥14 tests
Acceptance Criteria
Out of scope
- WebSockets/SSE push (polling contract only)
- Group chats / attachments (stub flag OK)
Task: Direct Messaging Backend — Conversations, Cursor Messages, Read State (Hard)
Overview
No messaging layer exists. Deliver a production-shaped DM stub API: unique conversations, participant authz, cursor pagination, read receipts, rate limits, and abuse guards — enough for the hardened frontend messages UI.
Scope (must all ship)
Entities
Conversation: fanId, creatorId, unique pair,lastMessageAt, optionallastMessagePreviewMessage: conversationId, senderId, body,createdAt,readAt, optionalclientIdfor idempotent sendEndpoints
POST /api/v1/conversations— fan starts (or get-or-create) with creatorGET /api/v1/conversations/me?cursor&limit— inbox ordered bylastMessageAtGET /api/v1/conversations/:id/messages?cursor&limitPOST /api/v1/conversations/:id/messages— participant; supportsIdempotency-Key/clientIdPOST /api/v1/conversations/:id/read— mark messages read up to timestamp/idRules
403Pagination
Safety
Testing (≥14)
Deliverables
MessagingModule, migrations, Swagger,docs/messaging.md, ≥14 testsAcceptance Criteria
Out of scope