Measured savings across 11 LLMs — Claude Opus 4.7 to Gemini Flash.→ See per-model data
Get free API key →
Engineering

Resend bounce handling, PostHog funnel instrumentation, and an AST fence against billing drift

v1.28.0 closes three infrastructure gaps that had been operating on implicit assumptions: email hard bounces now auto-suppress via a Svix-verified webhook, seven landing CTAs now emit PostHog click events, and a new AST fence in CI prevents any new cost-estimation function from bypassing model_pricing.estimate_cost.

James Hollingsworth(Contributor)Published 8 min~1,094 words

Why this release happened

For the past several weeks gotcontext has been running a tight dogfood loop: we use our own MCP gateway on our own codebase, and the friction surfaces show up quickly. One category of friction that kept appearing was implicit infrastructure — pieces of the platform that worked well enough when no one was watching but had no formal guard against drift.

Three such gaps were overdue for a real fix:

  • Email delivery failures were silently discarded. Hard bounces landed in Resend's dashboard. We had no automated path from "Resend saw a permanent rejection" to "stop sending to that address."
  • We had PostHog wired up and seven landing CTAs we cared about as conversion signals. None of them fired an event.
  • The billing layer had grown to include several cost-estimation code paths. Some called model_pricing.estimate_cost. Others reproduced similar logic inline. We had no CI enforcement to keep those in sync.
  • v1.28.0 closes all three. None of the changes are visible user features. They're the kind of engineering work that shows up as absence of future incidents.

    Track A: Resend bounce webhook

    What shipped: POST /webhooks/resend at api/app/webhooks/resend.py.

    The endpoint receives Svix-signed events from Resend. On email.bounced with bounce_type: permanent, or on email.complained, it sets users.email_opt_out = true for the affected address and halts all subsequent sends to that user.

    Before this, the suppression path was manual: a bounce would accumulate in Resend's suppression list, but our database had no corresponding flag. The next cron job (weekly digest, budget alert, key expiry warning) would attempt to send again, hit the already-suppressed address, and Resend would silently no-op the delivery. No error in our logs, no feedback loop, no way to know which users were unreachable.

    Why Svix matters here. Resend's inbound webhook events carry an svix-signature header. The endpoint verifies it using the same RESEND_WEBHOOK_SECRET pattern we already use for Clerk and Polar — reject anything that doesn't verify. Unverified bounce events from an attacker could otherwise trigger mass opt-outs across arbitrary user IDs.

    The data model. users.email_opt_out already existed (Alembic revision 0009). The v1.28.0 change is the automated write path from the inbound webhook. The 13 send functions in api/app/services/email.py already read this flag before sending; callers were responsible for passing it. Now the flag actually gets set when it should.

    Soft bounces (temporary failures, full inbox) do not trigger suppression. Only permanent rejections and spam complaints do.

    Track B: PostHog landing CTA instrumentation

    What shipped: seven posthog.capture calls across the landing page CTAs.

    The conversion funnel for gotcontext has one main question: does someone who lands on the page sign up? We had PostHog installed. We had CTAs. We had no events connecting the two.

    The seven instrumented sites are the primary conversion actions: hero "Get started free", hero "View docs", pricing page "Start free", pricing page plan upgrade buttons (Free / Pro / Team), and the docs page "Connect your client" call to action. Each fires a landing_cta_clicked event with a cta_id property (e.g. hero_get_started, pricing_pro_upgrade) so the Funnel analysis in PostHog can separate intent signals.

    What we did not instrument. Navigation clicks (header links, footer links) and internal dashboard actions were excluded deliberately. Those are not conversion signals. Adding them would dilute the funnel data and raise the cost of interpreting it.

    The PostHog key (NEXT_PUBLIC_POSTHOG_KEY) must be set on Vercel for events to fire. The guard at apps/web/src/libs/posthog-events.ts no-ops silently when it is not set, so the change is safe in development environments without the key.

    Track C: AST fence against billing drift

    What shipped: api/tests/test_billing_unification_fence.py (with a companion meta-test at test_billing_unification_fence_meta.py covering the walker itself) — a CI gate that asserts every function in the API codebase that computes a dollar-denominated cost value calls model_pricing.estimate_cost rather than reproducing the calculation inline.

    The motivation: over several versions, api/app/routers/v1/ grew to include multiple endpoints that report cost figures to the user (/v1/usage, /v1/usage/by-client, /v1/usage/by-model, /v1/usage/by-cache). Most called the canonical estimator. At least two had inline math that diverged from it when model pricing changed.

    The AST fence works by parsing the Python source of every router file and identifying function definitions that return or assign variables with names containing cost, usd, dollar, or price. For each such function, the fence asserts that the function body (or any function it directly calls within the same module) contains a call to estimate_cost. If a new function introduces a cost computation that bypasses the canonical path, the CI test job fails before the PR can be merged.

    This is the same pattern as api/tests/test_mcp_call_tool_dispatch_fence.py — the 8-test AST fence that caught the v1.22.4 latent-import bug class. AST-level enforcement is the only way to lock these invariants durably; runtime tests don't catch code paths that haven't been executed yet.

    The fence runs in the standard pytest api/tests/ invocation. No new environment variables or external dependencies.

    The architecture document

    Alongside the three tracks, PR #130 ships docs/architecture/SYSTEM-ARCHITECTURE.md (1,453 lines) — a canonical writeup of the full platform architecture. It covers the data flow from Clerk JWT through the FastAPI middleware stack, the MCP gateway session lifecycle, the Alembic migration chain, the Fly.io deploy contract, the Vercel build gates, and the third-party service dependency map.

    The document was validated against four independent graders (the thinktank council pattern: Claude, Codex, Gemini, Droid) before being committed. Prior to this, architecture knowledge was distributed across CLAUDE.md, per-folder CLAUDE.md files, inline code comments, and institutional memory. A new engineer or subagent starting on this codebase had to reconstruct the picture from scattered sources.

    The dogfood angle

    Each of these three tracks came out of operating the product on its own codebase. The Resend gap surfaced when we audited the email send path during a dogfood sweep (F26, documented in docs/plans/2026-05-02-dogfood-fixes-plan.md): we found that the bounce path was manual and decided to fix it. The PostHog gap surfaced when we tried to read the conversion funnel and found it empty. The billing drift gap surfaced when a codex audit flagged inconsistent cost figures across two endpoints.

    We built gotcontext to compress context for other people's codebases. We use it on ours. The 142-tool MCP surface that shipped in v1.23.0 went through a 167-test E2E sweep (infra/e2e/). The architecture document was compressed and compared against live OpenAPI output as a consistency check. The dogfood loop is the primary quality gate.

    If you use gotcontext to work on gotcontext — which you can do by adding the MCP server to your Claude Code config and pointing it at https://api.gotcontext.ai/mcp — the compressed architecture document is available via ingest_context + read_skeleton at a fraction of the raw token cost of reading 1,453 lines directly.

    ``json { "mcpServers": { "gotcontext": { "url": "https://api.gotcontext.ai/mcp", "headers": { "Authorization": "Bearer gc_live_YOUR_KEY" } } } } ``

    Get an API key → · Read the docs →

    Cite this

    Researchers, analysts, or journalists referencing this post can use either format below — both are copyable.

    BibTeXbibtex
    @misc{v128-architecture-refresh-2026,
      title  = {Resend bounce handling, PostHog funnel instrumentation, and an AST fence against billing drift},
      author = {James Hollingsworth},
      year   = {2026},
      month  = {May},
      url    = {https://www.gotcontext.ai/blog/v128-architecture-refresh},
      note   = {gotcontext.ai engineering blog.},
    }
    APAtext
    James Hollingsworth. (2026, May 19). Resend bounce handling, PostHog funnel instrumentation, and an AST fence against billing drift. gotcontext.ai. Retrieved from https://www.gotcontext.ai/blog/v128-architecture-refresh.

    Contribute