# Stop the agent misrouting webhook events to the wrong tenant

## TL;DR
Derive the tenant from the signed webhook payload itself, never from the URL path, query string, or whatever the agent guessed. Verify the signature first, extract the tenant id from inside the verified payload, then route.

## The problem
```text
webhook received on /hooks/acme-corp
payload (verified): tenant_id = "globex-inc"
event applied to: acme-corp workspace
result: globex onboarding marked complete in acme's workspace
```

## Use this skill
Agent? Search Vectle for this error:
```bash
curl -s "https://vectle.com/api/v1/search?q=agent misrouted webhook events wrong tenant multi-tenant onboarding"
```

## Fix it

### Step 1: Verify the signature before touching anything

Validate the webhook signature with the shared secret for the endpoint before parsing or routing. Reject anything that fails verification.

Expected: only genuinely provider-sent events reach your routing logic.

### Step 2: Extract the tenant id from inside the verified payload

Read the tenant identifier from the verified payload body (tenant_id, account_id, or the provider's equivalent). This is the only tenant value you trust.

Expected: the tenant id is known and comes from signed data.

### Step 3: Route strictly on that tenant id

Look up the workspace by the payload tenant id and apply the event there. Ignore tenant hints in the URL path or query params; treat a mismatch between path and payload as a routing error, not a tie to break.

Expected: every event lands in the workspace that matches its signed tenant id.

### Step 4: Log path-vs-payload mismatches as security events

When the URL tenant and the payload tenant disagree, drop the event and alert. That pattern is either a misconfigured sender or someone probing.

Expected: mismatches are visible and noisy instead of silently "resolved" by picking one.

### Step 5: Replay a cross-tenant event in staging

Send a signed event whose payload tenant differs from the URL tenant.

Expected: the event is rejected or routed to the payload tenant, and an alert fires. It never touches the URL tenant's workspace.

## When this applies

- Webhook events update the wrong customer's onboarding state
- The endpoint uses per-tenant URLs or a tenant query param for routing
- Onboarding state (steps, checklists, provisioning) is tenant-scoped

## When it doesn't

- Webhooks fail signature verification entirely (fix the secret first)
- A single-tenant app where routing is not a concept
- Events are delayed or duplicated but land in the right tenant

## Compatibility

Stripe, GitHub, Slack, and Segment webhooks; any multi-tenant SaaS with per-customer onboarding state. The pattern holds for every signed webhook.

## Variant phrasings

### webhook applied to wrong workspace during onboarding

Same fix. The workspace must come from the verified payload, full stop.

### multi-tenant webhook routing mixed up customer events

Audit every place the agent reads tenant from the request instead of the payload. There is usually more than one.

## Why it happens

Per-tenant webhook URLs feel like routing - /hooks/acme-corp must be for acme. But URLs are cheap to get wrong: a provider misconfiguration, a copied endpoint, or an agent that registered one URL for every tenant can all deliver tenant A's event to tenant B's path. The payload is the only signed statement of which tenant the event belongs to, and routing on anything else is a guess dressed up as logic.

## Edge cases

- Providers that do not include a tenant id in the payload: join on a verified account identifier (connected account id, installation id) instead
- Shared secrets across tenants: rotate to per-tenant secrets so a leak cannot forge cross-tenant events
- Batched payloads with mixed tenants: split and route each event individually

## If it still fails

- Capture the raw payload, the signature header, the URL tenant, and the payload tenant before changing anything else.
- Reproduce with one signed test event so you are not debugging a crowd.
- Check the provider status page; delivery outages look exactly like routing bugs.

## Prevention

- Webhook handlers take the tenant id as an explicit verified parameter, never from ambient state.
- Integration tests cover cross-tenant payloads for every webhook endpoint.
- Alert on any payload-vs-path tenant mismatch the day it happens.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_Og3MUp03NUA20GjJMa1RrQ
