Sponsors
Self-serve sponsor slot booking with calendar availability and volume discounts.
Launchy ships a complete sponsor system so brands can book sidebar slots without emailing you. All revenue is yours; the template takes no cut.
What a sponsor gets
- A slot in the homepage sidebar (rendered by
SponsorSlotsin components/sponsor-slot.tsx) for the booked date range - Their name, tagline, outbound link, and image
Booking flow
- Visitor goes to
/sponsor - Sees available slots + a date picker for the next
sponsor_max_daysrange - Picks a slot and range
- Fills in brand name, tagline, website URL, image (uploaded to R2 via
/api/sponsor/upload), email - Redirected to Stripe Checkout (via
/api/sponsor/checkout— rate-limited 10/hour per IP) - On
checkout.session.completedwebhook, thesponsorsrow is inserted - Plunk sends
sponsor-confirmed.tsxemail
See app/sponsor/.
Slot configuration
/admin/settings → sponsors controls (exact keys from lib/settings-shared.ts):
| Setting | Purpose |
|---|---|
sponsor_slot_count | Number of sidebar slots (default: 3) |
sponsor_base_price_cents | Full-price cost per day per slot |
sponsor_max_discount | Max discount percentage at sponsor_discount_full_days |
sponsor_discount_full_days | Duration at which the max discount kicks in |
sponsor_max_days | Max booking duration |
Volume discounts
lib/sponsor-pricing.ts computes the price with a linear discount: at sponsor_discount_full_days days or more, the full sponsor_max_discount percentage applies; below that, the discount scales linearly. The /sponsor UI shows the effective per-day price updating live as the sponsor picks a duration.
Availability check
When the sponsor picks a slot + date range, the server checks for any overlapping existing booking on that slot. If found, the UI disables the slot on those dates.
Sidebar rendering
components/sponsor-slot.tsx queries sponsors whose starts_at <= today <= ends_at and renders up to sponsor_slot_count slots, filling unbooked ones with a "This could be you" placeholder linking to /sponsor.
Admin view
/admin/sponsors (see app/admin/sponsors/) shows:
- Active and upcoming sponsor bookings
- Revenue tracking
- Manual slot creation via the admin action in app/admin/sponsors/actions.ts
Refunds are handled manually from the Stripe Dashboard (the code doesn't auto-refund sponsor bookings).
Renewal reminders
There is no renewal-reminder cron shipped. If you want one, add app/api/cron/sponsor-renewal/route.ts that queries sponsors with ends_at in 7 days and sends a prefilled booking link.
Rate limits
sponsor-checkout:{ip}— 10 / hour (on the checkout route)sponsor-upload:{ip}— 10 / hour (on the image upload route)
Both configured via lib/rate-limit.ts.