Public endpoints
POST /api/subs/new
Starts the magic-link flow for Programs or passes through to n8n for Internal.
Query params:
calendar_id=programs|internal(required)
Body (JSON or form-encoded):
name(required)email(required)turnstile_token(optional, Programs only)
Behavior:
- Programs: creates
verify_requestswith 30-minute TTL, enforces 60s cooldown per email, sends Resend email. - Internal: forwards to
N8N_BASE/webhook/subs/newwith{ name, email }.
Responses:
200JSON{ ok: true, message: ... }(Programs)303redirect to/subscribe-programs?sent=1(Programs, when Accept is not JSON)400invalid input or Turnstile failure429cooldown active (Retry-After header)500missing bindings or provider errors
GET /api/subs/verify
Consumes a magic link for Programs and mints a token.
Query params:
calendar_id=programsv=<verification code>
Responses:
200JSON:{ ok, token, calId, expires_at, icsUrl, subscribeUrl, webcalUrl, googleUrl, outlookWeb }303redirect tosubscribeUrl(when Accept is not JSON)403calendar disabled or subscriber disabled404invalid/expired link
GET /cal/programs.ics
Token-gated ICS for Programs.
Query params:
token(required)calendar_id=programs(required)
Behavior:
- Validates token hash, calendar enabled, subscriber active, and expiry.
- Logs access to
calendar_access_log. - Returns
programs.icsfrom R2 with ETag andCache-Control: private, max-age=300.
GET /cal/internal.ics
Proxy to n8n Internal feed.
Query params:
calendar_iddefaults tointernaland is normalized
Behavior:
- Forwards request to
${N8N_BASE}/webhook/rc-cal. - Maps 401/403 to 404 for client compatibility.
- Sets
Content-Dispositionfilename to<calid>.ics.
GET /api/turnstile-site-key
Returns { siteKey } for client-side Turnstile widget initialization.
Admin endpoints (edge-protected)
GET /api/admin/stats
Returns counts for subscribers, tokens, and recent verify activity.
GET /api/admin/subs/list
Returns up to 1000 subscribers for a calendar id. Note: this query expects a DB view or schema with calendar_id and token in the subscriber listing.
POST /api/admin/revoke
Disables a subscriber and revokes tokens for a calendar.
POST /api/admin/enable
Re-enables a subscriber by email.
GET /api/admin/mixpanel-test
Sends a single test event to Mixpanel and returns the response.