feat: sponsor outreach email system with CRM pipeline #316

Open
opened 2026-04-03 23:28:53 +00:00 by forgejo_admin · 0 comments

Type

Feature

Lineage

Standalone — discovered from Marcus's manual sponsor email campaign (GroupMe stakeholders chat, 2026-04-03). Maps to WS-S14 on project-westside-agency and sponsor-agent (pathway #8).

Repo

forgejo_admin/basketball-api

User Story

As an admin,
I want to send branded sponsorship outreach emails to local businesses and track their response status,
So that I can build partnerships and funding for the program without manually copy-pasting emails.

Context

Marcus has been manually copy-pasting 4 email template variants to 50+ local businesses (restaurants, banks, construction companies, tire shops, etc.) asking for sponsorships. He has a tiered sponsorship model ($500-$5,000+) and categorizes businesses by type (food, financial, retail, automotive, construction, fitness, dental, grocery).

The platform already has MJML email infrastructure (templates, gmail-sdk, email logging, load_email_template) but it only targets internal contacts (parents/players). This feature extends the email system to external business contacts with a proper CRM-like pipeline.

Design spec: docs/superpowers/specs/2026-04-03-sponsor-outreach-system-design.md (in pal-e-platform)

File Targets

Files the agent should modify or create:

  • src/basketball_api/models.py — Sponsor model, SponsorStatus/SponsorCategory enums, EmailType addition
  • src/basketball_api/services/sponsor_service.py — new: CRUD, blast logic, category pitch defaults, rate-limited sending
  • src/basketball_api/routes/sponsors.py — new: REST endpoints (CRUD + blast + seed)
  • src/basketball_api/app.py — register sponsor router
  • alembic/versions/xxx_add_sponsors.py — migration: sponsors table + EmailType enum update
  • tests/test_sponsors.py — new: model, CRUD, blast endpoint tests

Files the agent should NOT touch:

  • src/basketball_api/services/email.py — existing email service; sponsor_service.py imports from here but doesn't modify it
  • src/basketball_api/routes/admin.py — sponsor routes are their own router

Acceptance Criteria

  • Sponsor model with: business_name, email, category, status pipeline (prospect→contacted→responded→negotiating→committed→declined), custom_pitch override, last_contacted_at
  • CRUD endpoints: GET /sponsors (filterable), POST /sponsors, PATCH /sponsors/{id}, DELETE /sponsors/{id}
  • POST /sponsors/seed — bulk import from JSON payload
  • POST /sponsors/blast — sends emails filtered by category/status, with test_email safety valve
  • Blast updates sponsor status to "contacted" and sets last_contacted_at
  • Rate limiting: 3-5 second delay between sends
  • Each send logged in email_log with EmailType.sponsor_outreach
  • Category pitch defaults derived from Marcus's actual templates (food, financial, retail, automotive, construction, fitness, dental, grocery)
  • custom_pitch on Sponsor overrides category default when set
  • include_tiers flag on blast request toggles sponsorship tier pricing block

Test Expectations

  • Unit: Sponsor model CRUD operations
  • Unit: category pitch resolution with custom_pitch override
  • Integration: blast endpoint with test_email (sends 1, no status update)
  • Integration: blast endpoint without test_email (sends N, updates statuses)
  • Integration: seed endpoint bulk import
  • Integration: filtering by category and status
  • Run command: pytest tests/test_sponsors.py -v

Constraints

  • Follow existing route patterns (see routes/admin.py, routes/jersey.py for style)
  • Use existing load_email_template() for template rendering
  • Use existing get_gmail_client() for sending
  • Rate limiting via time.sleep() between sends (no async required)
  • Tenant-scoped: all queries filter by tenant_id
  • Migration must handle EmailType enum extension (ALTER TYPE)

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • project-westside-agency — sponsor-agent is pathway #8
  • project-westside-basketball — parent project
  • arch-email — email system architecture
  • sop-email-send — email approval workflow
### Type Feature ### Lineage Standalone — discovered from Marcus's manual sponsor email campaign (GroupMe stakeholders chat, 2026-04-03). Maps to WS-S14 on project-westside-agency and sponsor-agent (pathway #8). ### Repo `forgejo_admin/basketball-api` ### User Story As an admin, I want to send branded sponsorship outreach emails to local businesses and track their response status, So that I can build partnerships and funding for the program without manually copy-pasting emails. ### Context Marcus has been manually copy-pasting 4 email template variants to 50+ local businesses (restaurants, banks, construction companies, tire shops, etc.) asking for sponsorships. He has a tiered sponsorship model ($500-$5,000+) and categorizes businesses by type (food, financial, retail, automotive, construction, fitness, dental, grocery). The platform already has MJML email infrastructure (templates, gmail-sdk, email logging, load_email_template) but it only targets internal contacts (parents/players). This feature extends the email system to external business contacts with a proper CRM-like pipeline. Design spec: `docs/superpowers/specs/2026-04-03-sponsor-outreach-system-design.md` (in pal-e-platform) ### File Targets Files the agent should modify or create: - `src/basketball_api/models.py` — Sponsor model, SponsorStatus/SponsorCategory enums, EmailType addition - `src/basketball_api/services/sponsor_service.py` — new: CRUD, blast logic, category pitch defaults, rate-limited sending - `src/basketball_api/routes/sponsors.py` — new: REST endpoints (CRUD + blast + seed) - `src/basketball_api/app.py` — register sponsor router - `alembic/versions/xxx_add_sponsors.py` — migration: sponsors table + EmailType enum update - `tests/test_sponsors.py` — new: model, CRUD, blast endpoint tests Files the agent should NOT touch: - `src/basketball_api/services/email.py` — existing email service; sponsor_service.py imports from here but doesn't modify it - `src/basketball_api/routes/admin.py` — sponsor routes are their own router ### Acceptance Criteria - [ ] Sponsor model with: business_name, email, category, status pipeline (prospect→contacted→responded→negotiating→committed→declined), custom_pitch override, last_contacted_at - [ ] CRUD endpoints: GET /sponsors (filterable), POST /sponsors, PATCH /sponsors/{id}, DELETE /sponsors/{id} - [ ] POST /sponsors/seed — bulk import from JSON payload - [ ] POST /sponsors/blast — sends emails filtered by category/status, with test_email safety valve - [ ] Blast updates sponsor status to "contacted" and sets last_contacted_at - [ ] Rate limiting: 3-5 second delay between sends - [ ] Each send logged in email_log with EmailType.sponsor_outreach - [ ] Category pitch defaults derived from Marcus's actual templates (food, financial, retail, automotive, construction, fitness, dental, grocery) - [ ] custom_pitch on Sponsor overrides category default when set - [ ] include_tiers flag on blast request toggles sponsorship tier pricing block ### Test Expectations - [ ] Unit: Sponsor model CRUD operations - [ ] Unit: category pitch resolution with custom_pitch override - [ ] Integration: blast endpoint with test_email (sends 1, no status update) - [ ] Integration: blast endpoint without test_email (sends N, updates statuses) - [ ] Integration: seed endpoint bulk import - [ ] Integration: filtering by category and status - Run command: `pytest tests/test_sponsors.py -v` ### Constraints - Follow existing route patterns (see routes/admin.py, routes/jersey.py for style) - Use existing load_email_template() for template rendering - Use existing get_gmail_client() for sending - Rate limiting via time.sleep() between sends (no async required) - Tenant-scoped: all queries filter by tenant_id - Migration must handle EmailType enum extension (ALTER TYPE) ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `project-westside-agency` — sponsor-agent is pathway #8 - `project-westside-basketball` — parent project - `arch-email` — email system architecture - `sop-email-send` — email approval workflow
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/basketball-api#316
No description provided.