Skip to main content

Vercel Protection Bypass

Audience: Henry (or any team Owner with Vercel dashboard access) Time required: < 5 minutes

Symptom​

Inngest Cloud cannot sync against a Vercel deployment that sits behind deployment protection, or you want browser access to preview deployments without an SSO login. Provisioning the bypass secret requires the Vercel dashboard — it cannot be done from this environment.

Diagnosis​

1. Why a human is needed​

The Vercel API token available to automation in this environment returns 403 forbidden on the bypass-secret endpoint (PATCH /v1/projects/{id}/protection-bypass) and on two related account-level endpoints (GET /v5/user/tokens, GET /v1/integrations/configurations), while every other project/env read-write call on the same token succeeds. This is a token-scope restriction, not a role or plan limitation — confirmed Owner role, Pro team plan. Only the Vercel dashboard (or a differently-scoped token) can complete this step.

2. Check whether you need this at all​

Use the custom domain. https://launch.lynkr.cc/api/inngest is the only sync URL that works.

ssoProtection.deploymentType is all_except_custom_domains, and only a custom domain is exempt. The project's assigned .vercel.app domain is NOT — it is gated like any hash URL.

Measured 2026-08-14:

URLResult
GET https://launch.lynkr.cc/api/health200
PUT https://launch.lynkr.cc/api/inngest200 {"message":"Successfully registered","modified":true}
GET https://launcpadv2-scaled.vercel.app/api/inngest302 → vercel.com/sso-api?...
GET https://launcpadv2-scaled.vercel.app/302 → vercel.com/sso-api?...
GET https://creative-ingest.vercel.app307 → scaled-launchpad-v2.vercel.app

APP_BASE_URL is already https://launch.lynkr.cc, and src/app/api/inngest/route.ts pins serveHost to it, so a correctly registered app advertises the custom domain regardless of which host served the registration request.

Practical implication: for day-to-day operation the bypass secret below is not required, provided Inngest Cloud syncs against launch.lynkr.cc. Provision it only if you additionally want ordinary browser access to preview deployments without an SSO login, or as defence-in-depth in case ssoProtection.deploymentType is ever tightened to all.

Correction to a previous revision

An earlier version of this runbook stated that https://launcpadv2-scaled.vercel.app/api/inngest was "reachable from the public internet without any bypass secret", inferring that the project's assigned .vercel.app domain counted as a custom domain for the purposes of all_except_custom_domains. That was wrong — it does not, and the host now returns a 302 to the Vercel SSO gate. Anything relying on that claim (notably an Inngest sync URL pointed at the .vercel.app host) is broken.

The same revision reported a blocking Inngest error — The function 'Write ClickUp Custom Field' has higher concurrency limits (8) than your plan limit of 5. That is resolved. Every function in src/inngest/* now declares concurrency: 5 or lower (clickup-functions.ts:34, creative-check.ts:49, functions.ts:55, marketing-functions.ts:933), and the PUT above registers cleanly.

Fix​

Steps to generate and wire the bypass secret​

  1. Open the project settings. Go to: https://vercel.com/scaled/launcpadv2/settings/deployment-protection (Dashboard path: scaled team → launcpadv2 project — note the typo in the project slug; the GitHub repo is creative-ingest but the Vercel project is launcpadv2 — → Settings tab → Deployment Protection in the left sidebar.)

  2. Find "Protection Bypass for Automation." Scroll to the section titled Protection Bypass for Automation (below the Vercel Authentication / Password Protection / Trusted IPs sections).

  3. Generate the secret. Click Add Bypass Rule (or Generate Secret — label varies slightly by dashboard version). Optionally add a note, e.g. Inngest Cloud webhook delivery. Vercel generates and displays a secret value once — copy it now.

  4. Store it as a Vercel env var. Go to Settings → Environment Variables (same project). Click Add New:

    • Name: INNGEST_SERVE_ORIGIN_BYPASS_SECRET
    • Value: the secret copied in step 3
    • Environments: check Production and Preview (leave Development unchecked unless local dev also needs it) Click Save.

    Note: Vercel also automatically exposes the current bypass secret to every deployment as the system env var VERCEL_AUTOMATION_BYPASS_SECRET — you don't need to reference it in code either way, since Vercel intercepts the header/query param before Next.js routing runs.

  5. Redeploy so the new env var is baked into the build. Dashboard → Deployments tab → click the ⋯ menu on the latest Production deployment → Redeploy. (Not strictly required for the bypass secret itself to work — Vercel's edge checks the secret against its own stored value, not the env var — but redeploy anyway so INNGEST_SERVE_ORIGIN_BYPASS_SECRET is available to any app code that references it later.)

  6. Tell Inngest Cloud about the secret (only needed if syncing against a hash/preview URL — skip if already using the stable .vercel.app domain as confirmed reachable above):

    • If the Vercel↔Inngest integration is installed: Inngest dashboard → Settings → Integrations → Vercel → select this project → paste the secret into the "Deployment Protection Key" field → Save.
    • If not installed: Inngest dashboard → Apps → select the creative-ingest app → Edit → change the sync URL to: https://launch.lynkr.cc/api/inngest?x-vercel-protection-bypass=<secret>&x-vercel-set-bypass-cookie=samesitenone (the custom domain — do NOT use launcpadv2-scaled.vercel.app, it is SSO-gated) → Save → click Resync.
  7. Verify:

    • From the dashboard's Deployment Protection page, confirm the new bypass rule shows status "Active."
    • In Inngest dashboard → Apps → creative-ingest, confirm the app shows "Synced" (green) rather than an error/unreachable state.
    • Trigger a test check (POST /api/admin/checks/trigger on the live deployment, or via the app's admin UI) and confirm a new event shows up in the Inngest dashboard's Runs tab, executing (not stuck "queued").
    • Confirm a new row lands in creative_check_results in ClickHouse sourced from the live Vercel deployment.

A separate, unrelated Inngest error you may see​

PUT /api/inngest can independently return:

{"message":"The function 'Write ClickUp Custom Field' has higher concurrency
limits (8) than your plan limit of 5","modified":false}

This means the Write ClickUp Custom Field Inngest function is configured with concurrency: 8 (or similar) in code, but the connected Inngest Cloud plan only allows 5. Either lower the function's concurrency setting in code (outside this runbook's scope — belongs to whoever owns src/inngest/*) or upgrade the Inngest plan. This does not block the protection-bypass work above; it's a separate sync failure worth flagging to the team.

Prevent​

To rotate the secret later: repeat steps 1–3 to generate a new one, update the env var in step 4, update Inngest's stored key in step 6, then return to the Deployment Protection page and revoke the old bypass rule so it can no longer be used.