SPRINT-7 T5-C — Edge webhook URLs + SMS cron decision¶
Date: 2026-08-30
Status: T5-C COMPLETE. STOP — await Ryan review before T5-D (RadioCheck iOS pointer).
Prerequisite: T5-B Phase 2 PASS (api.last1.app live, public RLS 0-row proof).
Executive summary¶
| Workstream | Result |
|---|---|
| Edge inbound webhooks (Twilio) | Repointed hosted → https://api.last1.app/functions/v1/* |
last1.id radiocheck revoke webhook |
Updated → https://api.last1.app/functions/v1/revoke-from-last1 |
Edge runtime env (TWILIO_*, UNIT_REQUEST_*) |
Updated + functions container recreated |
Monolith webhooks (last1.app/api/*) |
Confirmed unchanged (T-4 hostname-stable cutover) |
| Stripe dashboard | No edge-hosted endpoint found; monolith last1.app/api/stripe/webhook only |
| Sovereign pg_cron URL rewrite | Done → public api.last1.app/functions/v1/* |
| Sovereign SMS crons | Rewritten but INACTIVE (Option B — hold until T-6) |
check-in-proof-events-cron |
Active on public URL (internal proof path, no SMS) |
| Hosted pg_cron (rollback net) | Still active until T-6 teardown (PAT expired — not queried live) |
Scripts: t5-c-update-last1id-webhook.sh, t5-c-update-twilio-webhooks.py, t5-c-update-edge-webhook-env.sh, t5-c-rewrite-crons.sh
1. Webhook inventory¶
1.1 Edge-hosted (was tcthzakppndkykatoefx.supabase.co/functions/v1/*)¶
| Callback | Old URL | New URL | How updated |
|---|---|---|---|
| Twilio inbound (primary) | …/sms-webhook |
https://api.last1.app/functions/v1/sms-webhook |
Twilio API — phone +15203532111, messaging service MG9ee6f9096dfebc0263967c1de0ce4316 |
| Twilio check-in reply | …/sms-checkin |
https://api.last1.app/functions/v1/sms-checkin |
Twilio API — phone +18586660633 |
| last1.id revoke delivery | …/revoke-from-last1 |
https://api.last1.app/functions/v1/revoke-from-last1 |
Postgres UPDATE on last1id_prod.app_clients (app_id=radiocheck) |
| Unit request email links | LAN/internal | https://api.last1.app/functions/v1/unit-request-actions |
Edge .env (UNIT_REQUEST_ACTION_BASE_URL) |
| Twilio status callback | LAN/internal | https://api.last1.app/functions/v1/sms-webhook |
Edge .env (TWILIO_STATUS_CALLBACK_URL) |
Edge stripe-webhook function: deployed and reachable at https://api.last1.app/functions/v1/stripe-webhook (unsigned probe → 400). No Stripe Dashboard endpoint currently registers a *.supabase.co/functions/v1/stripe-webhook URL (Stripe API lists only monolith + other products). RadioCheck subscription events today flow through https://last1.app/api/stripe/webhook (T2-3 rotation, unchanged hostname). No Stripe dashboard action required unless Ryan decides to split RadioCheck Edge billing webhooks later.
1.2 Monolith (last1.app/api/*) — confirm only, not re-touched¶
| Endpoint | Expected behavior | T5-C probe |
|---|---|---|
POST /api/stripe/webhook |
Signature gate | 400 (unsigned) — R940 monolith via CF |
POST /api/webhooks/bundle |
HMAC gate | 401 (unsigned) |
POST /api/webhooks/radiocheck-org-access |
HMAC gate | (same class as bundle — not re-probed) |
T-4 cutover already routes these hostnames to R940. No URL change.
1.3 Ryan-side dashboard vs API-updatable¶
| Item | Owner | T5-C action |
|---|---|---|
| Twilio phone SMS URLs | API (done) | t5-c-update-twilio-webhooks.py |
| Twilio Messaging Service inbound | API (done) | same |
last1.id app_clients.webhook_url |
DB/API (done) | t5-c-update-last1id-webhook.sh |
| Edge function env URLs | VM script (done) | t5-c-update-edge-webhook-env.sh |
| Stripe monolith webhook | Already correct | No change |
Stripe Edge stripe-webhook endpoint |
N/A today | No dashboard row found |
| Deactivate hosted pg_cron SMS jobs | Ryan — Supabase Dashboard | Deferred to T-6 (see §3) |
| Twilio Studio / other numbers | Ryan audit | API scan found 2 numbers + 1 messaging service only |
2. Public webhook smoke (through CF)¶
Log: /tmp/last1app-gate-b/t5c-webhook-smoke-20260830T232703Z.txt
| POST | HTTP | CF path | Notes |
|---|---|---|---|
/functions/v1/sms-webhook (form, no sig) |
403 | cf-ray + server: cloudflare |
Twilio signature gate ✓ |
/functions/v1/sms-checkin (JSON anon) |
200 | CF ✓ | Reachable (check-in TwiML path) |
/functions/v1/stripe-webhook (JSON anon) |
400 | CF ✓ | Missing stripe-signature ✓ |
/functions/v1/revoke-from-last1 (JSON anon) |
401 | CF ✓ | HMAC gate ✓ |
3. SMS cron decision — Option B applied (pending Ryan confirm)¶
Double-fire risk¶
Hosted Supabase (tcthzakppndkykatoefx) pg_cron jobs sms-checkin-cron and org-sms-cron remain active on hosted URLs until T-6 teardown (documented T2-0; live query blocked — Management API PAT returned Unauthorized).
If both hosted and sovereign SMS crons were active at 14:00 UTC, users would double-text.
Recommendation (Option B)¶
| Option | Sovereign SMS crons | Hosted SMS crons | Risk |
|---|---|---|---|
| (a) Activate now | active=true |
Must deactivate hosted NOW | Double-fire if hosted not disabled |
| (b) Hold until T-6 ✓ applied | active=false (URLs rewritten) |
Keep firing (dormant app OK) | No sovereign SMS until T-6 activation |
Applied state on VM 103 (last1app-db):
| jobname | schedule | active | URL target |
|---|---|---|---|
sms-checkin-cron |
0 14 * * * |
false | https://api.last1.app/functions/v1/sms-cron (+ cron Bearer) |
org-sms-cron |
0 14 * * * |
false | https://api.last1.app/functions/v1/org-sms-cron (+ cron Bearer) |
check-in-proof-events-cron |
*/5 * * * * |
true | https://api.last1.app/functions/v1/check-in-proof-events (+ cron Bearer) |
Cron rewrite log: /tmp/last1app-gate-b/t5c-cron-rewrite-20260830T232649Z.log
Ryan decision needed: Confirm Option B (hold sovereign SMS until T-6 + hosted teardown), or reply Option A to activate sovereign SMS now and authorize hosted cron deactivation in Supabase Dashboard.
4. Backups¶
| Artifact | Path |
|---|---|
| last1.id webhook pre-image | /tmp/last1app-gate-b/t5c-radiocheck-webhook-pre-20260830T232615Z.csv |
| Twilio update report | /tmp/last1app-gate-b/t5c-twilio-webhook-update.json |
Edge .env pre-T5-C |
/opt/supabase/docker/.env.pre-t5c-webhook-* on VM 103 |
Rollback last1.id revoke URL:
UPDATE app_clients
SET webhook_url = 'https://tcthzakppndkykatoefx.supabase.co/functions/v1/revoke-from-last1'
WHERE app_id = 'radiocheck';
5. Deferred (T5-D / T-6)¶
| Item | Gate |
|---|---|
RadioCheck iOS EXPO_PUBLIC_SUPABASE_URL → https://api.last1.app |
T5-D |
| Activate sovereign SMS crons + disable hosted SMS crons | T-6 (or Ryan Option A now) |
| Hosted Supabase teardown | T-6 |
Optional: dedicated Stripe endpoint for Edge stripe-webhook |
Product decision |
6. Ryan review checklist¶
- Accept Twilio inbound URLs now hit
api.last1.appthrough CF (live inbound SMS path on sovereign). - Accept last1.id revoke deliveries now target sovereign
revoke-from-last1(HMAC unchanged). - Confirm Option B for SMS crons, or authorize Option A (+ hosted cron disable).
- Acknowledge monolith Stripe/webhooks unchanged at
last1.app/api/*.
STOP. No T5-D iOS work until Ryan replies.