Skip to content

Service Integrations

show.fm integrates with multiple third-party services to provide a complete podcasting platform. This section documents the configuration, API patterns, and implementation details for each integration.

Architecture Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                      SERVICE INTEGRATION LAYER                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                    show.fm Application                     │   │
│  │                  (SvelteKit + Hono API Layer)                    │   │
│  └───────────────────────────┬─────────────────────────────────────┘   │
│                              │                                          │
│   ┌──────────────────────────┼──────────────────────────────────────┐  │
│   │                          │                                       │  │
│   ▼                          ▼                          ▼            │  │
│  ┌──────────┐  ┌──────────────────┐  ┌──────────────────────────┐   │  │
│  │Cloudflare│  │     Supabase     │  │     Third-Party APIs      │   │  │
│  │ Services │  │    PostgreSQL    │  │                           │   │  │
│  │          │  │       Auth       │  │  • Google Calendar        │   │  │
│  │ • Pages  │  │       RLS        │  │  • Stripe (Payments)      │   │  │
│  │ • Workers│  │                  │  │  • Resend (Email)         │   │  │
│  │ • R2     │  │                  │  │                           │   │  │
│  │ • Queues │  │                  │  │                           │   │  │
│  │ • KV     │  │                  │  │                           │   │  │
│  └──────────┘  └──────────────────┘  └──────────────────────────┘   │  │
│                                                                       │  │
└───────────────────────────────────────────────────────────────────────┘  │

└──────────────────────────────────────────────────────────────────────────┘

Service Documentation

ServicePurposeStatus
CloudflareInfrastructure - Pages, Workers, R2, Queues, KV, HyperdriveProduction
SupabaseDatabase, authentication, RLS policies, real-timeProduction
Google CalendarOAuth, availability checking, event creationProduction
StripeSubscriptions, checkout, webhooks, billing portalProduction
ResendTransactional email, templates, audience managementProduction
Search SystemFuse.js (client) + PostgreSQL FTS (server)Production

Service Categories

Infrastructure Services

Cloudflare provides the edge computing infrastructure:

  • Pages: Hosts the main SvelteKit application
  • Workers: Runs RSS feed generation, automation execution, scheduled publishing
  • R2: Object storage for audio files and media
  • Queues: Async message processing for automation and cache invalidation
  • KV: Global key-value storage for RSS feed caching
  • Hyperdrive: Connection pooling for Workers → PostgreSQL

Data Services

Supabase provides the data layer:

  • PostgreSQL: Primary database with multi-tenant schema
  • Auth: User authentication with email/password and OAuth
  • RLS Policies: Row-level security for multi-tenancy
  • Admin Client: Service role access for Workers and webhooks

Scheduling & Calendar

Google Calendar powers the guest booking system:

  • OAuth 2.0: Secure calendar access with refresh tokens
  • FreeBusy API: Real-time availability checking across multiple calendars
  • Events API: Automatic calendar event creation with Google Meet links
  • Multi-Account: Support for multiple connected Google accounts

Payments & Billing

Stripe handles all payment processing:

  • Checkout Sessions: Subscription signup flow
  • Customer Portal: Self-service subscription management
  • Webhooks: Real-time subscription state synchronization
  • Tier Management: Price ID → subscription tier mapping

Search & Discovery

Search System provides two-layer search:

  • Client-Side (Fuse.js): Instant fuzzy search for in-memory data (<10K items)
  • Server-Side (PostgreSQL FTS): Full-text search with GIN indexes and trigram matching
  • Command Palette: Global Cmd+K search across the application

Email & Communications

Resend handles all transactional and automation emails:

  • Email Client: Type-safe Resend SDK wrapper with error handling
  • Templates: Pre-built booking flow emails (request, confirm, decline, host notification)
  • Audience Management: Contact creation and topic subscriptions for marketing
  • Automation Integration: Sends emails triggered by the automation engine

Integration Patterns

Environment Configuration

All services use environment variables for configuration:

bash
# Cloudflare (set via wrangler.toml and wrangler secret)
CLOUDFLARE_ACCOUNT_ID=...

# Supabase
PUBLIC_SUPABASE_URL=https://xxx.supabase.co
PUBLIC_SUPABASE_ANON_KEY=...
SUPABASE_SECRET_KEY=...      # Server-only

# Google Calendar
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...     # Server-only
GOOGLE_REDIRECT_URI=...

# Stripe
PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_...
STRIPE_SECRET_KEY=sk_...     # Server-only
STRIPE_WEBHOOK_SECRET=whsec_...

# Resend
RESEND_API_KEY=re_...        # Server-only
RESEND_FROM_EMAIL=[email protected]
RESEND_AUDIENCE_ID=aud_...   # For marketing contacts

API Route Pattern

Service integrations follow the Hono API pattern:

typescript
// src/api/routes/service-name/index.ts
import { Hono } from 'hono';
import type { Env } from '$api/types';

const serviceRoutes = new Hono<Env>().post('/action', async (c) => {
	// Access secrets from environment
	const apiKey = c.env?.SERVICE_API_KEY;

	// Initialize service client
	const client = new ServiceClient(apiKey);

	// Perform action
	const result = await client.doSomething();

	return c.json({ data: result });
});

export { serviceRoutes };

Webhook Pattern

External services call back via webhook endpoints:

typescript
// src/api/routes/webhooks/service.ts
webhookRoutes.post('/service', async (c) => {
	// 1. Verify signature
	const signature = c.req.header('x-signature');
	const body = await c.req.text();

	if (!verifySignature(body, signature, secret)) {
		return c.json({ error: 'Invalid signature' }, 400);
	}

	// 2. Parse event
	const event = JSON.parse(body);

	// 3. Handle event
	await handleEvent(event);

	// 4. Acknowledge (always return 200 to prevent retries)
	return c.json({ received: true });
});

Security Considerations

Token Storage

ServiceToken TypeStorageRefresh
Google CalendarRefresh TokenDatabase (encrypted)On-demand
StripeAPI KeyEnvironment variableN/A
SupabaseService Role KeyEnvironment variableN/A
ResendAPI KeyEnvironment variableN/A

Secret Management

  • Local Development: .env file (gitignored)
  • Production: wrangler secret put for Cloudflare
  • Never commit: API keys, webhook secrets, service role keys

Rate Limiting

ServiceLimitsHandling
Google Calendar1M queries/dayCaching, batch requests
Stripe100 requests/secondWebhook queuing
Supabase500 requests/secondConnection pooling via Hyperdrive
Resend10 requests/secondQueue-based automation sends

Error Handling

All service integrations follow consistent error handling:

typescript
try {
	const result = await serviceClient.action();
	return c.json({ data: result });
} catch (error) {
	// Log with context
	console.error(
		JSON.stringify({
			event: 'service_error',
			service: 'service-name',
			error: error instanceof Error ? error.message : String(error)
		})
	);

	// Return generic error (don't expose internals)
	return c.json({ error: 'Service unavailable' }, 500);
}

Internal documentation - Not for public distribution