Portfolio URL shortener — Twilio v1 (free under 1k/mo), CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker if volume justifies

DARE.CO.UK · PARKED SKETCH · 2026-05-18

Mirrored from ~/.claude/.../memory/project_portfolio_url_shortener_parked.md. This is a design sketch parked for future build — read for context, not as a current deliverable.

2026-05-17 sketch, pivoted + pricing-verified 2026-05-17 evening. v1 is Twilio Engagement Suite (Link Shortening + Click Tracking), free for first 1k messages/month — branded short domain (e.g. t.dare.co.uk) is $0/mo at Dan’s volume. CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker + KV build is parked for if/when volume exceeds free tier. Resume condition — first SMS integration goes live. Verified pricing 2026-05-17 against Twilio docs.


Goal: shorten meeting-invite URLs like time.dare.co.uk/2026-05-17/#t=20&c=london (52 chars) so they cost one SMS segment instead of two and look less phishing-y in the message body. Same approach lifts to dogwood booking-confirm SMS, audrey delivery-tracking SMS, etc.

Trigger for unparking: first SMS integration goes live. Today (2026-05-17) all SMS surfaces are parked: - audrey Phase C (booking SMS reminders) — project_booking_product_evolution.md - voip.ms AI Agent for audrey support — project_voipms_ai_agent_audrey_support_parked.md - dogwood + dare equivalents not started


Pivot — 2026-05-17 evening

The original sketch went straight to a CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker + KV build because it fit the toolkit’s “render local · push static · serve at edge” rhythm. Dan flagged the omission: “did you look for any reliable API service?” — and on review, Twilio’s native feature shoulders the whole thing at $0/mo for our volume.

The decision tree is downstream of the SMS-provider choice, which is itself parked. But: picking Twilio collapses the shortener question entirely, so the recommendation chain is now:

  1. Pick Twilio as the SMS provider.
  2. Enable Messaging Services → Link Shortening + Click Tracking (the “Engagement Suite” SKU). Zero shortener build. Twilio rewrites long URLs to short URLs inline at send time and reports click events.
  3. Use a branded short domain (t.dare.co.uk per brand, or one portfolio shortener). Per verified Twilio pricing 2026-05-17, the branded-domain feature is NOT a flat $15/mo as the original draft of this sketch claimed — it’s part of Engagement Suite, billed as $0.015/message above the first 1,000 free per month. For Dan’s volume (booking confirms, meeting invites, owner reminders — all measured in tens-to-hundreds/month per brand), the branded domain is effectively $0/mo.
  4. Only build the CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker if (a) sustained volume exceeds ~1k messages/month and the resulting $0.015/msg surcharge feels meaningful, or (b) you want analytics/observability inside the CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Analytics Engine spine instead of Twilio’s dashboard, or (c) you switch to a non-Twilio SMS provider (voip.ms, MessageBird) that doesn’t bundle shortening.

For all three portfolio brands’ current shape, v1 = Twilio config, not code.

Verified pricing (2026-05-17)

Item Cost
Base SMS outbound (US) $0.0083/segment
Engagement Suite (Link Shortening + Click Tracking) surcharge $0.015/message above first 1,000/month free
Branded short domain (e.g. t.dare.co.uk) $0 per-domain fee — included in Engagement Suite
TLS certificate for branded domain $0 (Let’s Encrypt) + ~10 min/90-day renewal labour

Bitly comparison (also verified 2026-05-17)

For a sense of what “managed shortener pricing” typically looks like: - Bitly Free: bit.ly domain only, ~10 branded links/mo - Bitly Core: $10/mo — analytics + redirects, no custom domain - Bitly Growth: $29/mo = $348/yr — first tier that includes a custom branded domain - Bitly Premium: $999/mo

Twilio’s “$0/mo within free tier” beats Bitly Growth by $348/yr for our use case while giving us the same branded-domain UX (t.dare.co.uk/Ab3xZ7). Bitly is “expensive” for branded short links specifically — Twilio’s model bundles it with the SMS send so the marginal cost is zero up to 1k/mo.


What you get: - Send long URL via Twilio API; Twilio rewrites it inline before delivery to a short URL on the domain you’ve configured. - Click events POST’d to a webhook you set on the Messaging Service. - Per-recipient unique short URLs (so click events tie back to the recipient). - No KV, no Worker, no slug-collision handling, no TTL management — Twilio owns all of it.

Setup difficulty: moderate (~30 min one-time). Not trivial like Bitly (which manages TLS for you), but not a build either. The TLS step is the only gotcha.

Per-brand vs portfolio-shared Messaging Service: - One-per-brand if billing / sender numbers / branding need to be separate (likely — audrey’s booking confirms shouldn’t be commingled with dare meeting invites). - The shortener feature is enabled per-service, so the choice doesn’t affect short-link plumbing.

Setup steps (branded domain t.dare.co.uk)

  1. Twilio Console → Messaging → Services → create a Messaging Service per brand (or one shared).
  2. Service → Sender Pool: add the sending number.
  3. Service → Integration: enable Link Shortening + Click Tracking (this is the Engagement Suite SKU).
  4. Service → Integration → Status Callback: set webhook URL for click events (lands on a CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker on e.g. clicks.dare.co.uk, ~30 LOC).
  5. Twilio Admin Center → Organizations → create an Organization.
  6. Register domain (t.dare.co.uk) → Twilio gives you a verification record.
  7. Add CNAME in Cloudflare DNS: t.dare.co.uklsct.ashburn.us1.twilio.com (or whichever region Twilio assigns).
  8. Generate TLS certificate for t.dare.co.ukthis is the gotcha. Bitly hides this; Twilio makes you BYO.
    • Easiest path: certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cf.ini -d t.dare.co.uk (uses CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF API to satisfy DNS-01 challenge — no need to expose port 80 on a real server).
    • Cert + key go to /etc/letsencrypt/live/t.dare.co.uk/ (or wherever certbot’s configured).
  9. Upload cert + key to Twilio via the Admin Center.
  10. Store SID + auth token in 1Password as Twilio portfolio messaging/auth_token.

The recurring chore: TLS renewal. Let’s Encrypt certs expire every 90 days. Automation options: - Manual: a ~/bin/twilio_cert_renew.sh script that runs certbot, uploads new cert via Twilio API, sends a Pushover notification on completion. Run from launchd monthly. ~20 LOC. - Acceptable interim: calendar reminder 80 days out; manual 10-min renewal.

Cost envelope (US, verified 2026-05-17): - Base SMS outbound: $0.0083/segment. - Engagement Suite surcharge: $0.015/message — first 1,000/month free. - Branded domain: $0/mo per-domain fee. - TLS cert (Let’s Encrypt): $0 + 90-day renewal labour. - Click events to webhook: $0 (you pay only for CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker cycles if you process them).

For Dan’s expected volume (probably <500 messages/month across all brands combined in year 1), total Engagement Suite cost = $0/month. Compare Bitly Growth: $29/mo = $348/yr to get the same t.brand.tld/<slug> UX.

Integration into dare_time_page.py: - No code change for v1. The page generates the long time.dare.co.uk/... URL as today. When SMS goes live, the sending pipeline (separate script, TBD when audrey Phase C / dare invites unpark) calls Twilio’s Messages.create with the long URL; Twilio shortens at send time. - The long URL stays canonical on the page + ICS file. Short URL is an SMS-delivery-time artefact, not baked into static output.

v1 lite (skip branded domain entirely): if the 30-min setup + TLS-renewal chore isn’t worth it for v1, default to Twilio’s twil.io/<slug> (or wapp.io for WhatsApp). Zero setup, zero ongoing chore, $0/mo. Recipients see a Twilio-managed domain rather than t.dare.co.uk — fine for known-recipient transactional messages (booking confirms, meeting reminders), less ideal if the message looks even slightly cold-outbound. Upgrade to branded later if recipients show signs of distrust.


v2 (parked) — CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker + KV (build only if volume / branded-domain cost justifies)

Original sketch retained below for the day it earns its build cost.

Trigger to unpark v2: - Sustained >1k messages/month (Engagement Suite surcharge kicks in at $0.015/msg above free tier). At 5k msg/mo that’s ~$60/mo = $720/yr — buys ~1 day of CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker dev + zero ongoing surcharge. - OR: analytics / observability needs that Twilio’s dashboard doesn’t satisfy (cross-brand click-funnel analysis, custom UTM injection, etc.). - OR: a portfolio brand wants a non-Twilio SMS provider (voip.ms, MessageBird, etc.) that doesn’t bundle shortening. - OR: the TLS-renewal chore for branded domains across N brands stops being worth it (at which point a CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Worker on t.dare.co.uk with CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF-managed TLS is a one-time setup that never needs renewal labour again).

Stack proposal (toolkit-aligned):

API: - POST / {url, ttl_seconds} with X-Shortener-Auth: <secret> → 200 {slug, short_url}. Auth via Worker secret (1Password item Cloudflare portfolio Shortener auth when created). - GET /:slug → 302 to stored URL (302 not 301 so we can rotate / revoke without browser caching). - DELETE /:slug with auth → revoke before TTL. Useful if a meeting cancels.

Trail / observability: log redirects to CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Analytics Engine (free tier) — slug, timestamp, country, UA.

Naming convention (per feedback_cf_token_naming_1password_sync.md): - CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Pages/Worker project: portfolio-shortener - KV namespace: portfolio_shortener_kv - API token: Cloudflare portfolio Shortener auth (1Password)

Open design questions if/when v2 unparks: - Hit analytics or fire-and-forget? (~20 LOC if yes.) - Customisable slugs (t.dare.co.uk/meet-london) vs random? Adds custom_slug param + collision handling. - One-shot vs reusable: single-recipient invite URLs (one-shot) or shared meeting URLs (reusable)? Affects whether to invalidate on first click.


Lesson pinned alongside

The omission — going straight to “build it on the toolkit” without an “evaluate managed services first” sweep — is the kind of mistake worth codifying as a sibling. See feedback_evaluate_managed_services_before_build.md (pinned 2026-05-17 evening alongside this pivot) for the general principle: cheap third-party API shoulders the load until volume justifies the build cost; “fits the toolkit” is not a sufficient reason to build when a $0 managed feature exists.

Sibling memories

Source: parked_sketch_portfolio_url_shortener_2026-05-17.md · Rendered 2026-05-18 12:53