# Outbound webhook migration playbooks — for AI agents

Machine-readable playbooks for moving outbound webhooks (webhooks a SaaS sends
to its own customers) to the Webhook Relay Outbound Gateway. Each playbook is a
deterministic, step-by-step procedure with exact API calls, checks, a cutover
that requires human approval, rollback, and a "Never do" list.

Human-readable guides: https://webhookrelay.com/docs/outbound/migrate
Product docs: https://webhookrelay.com/docs/outbound

## Pick the playbook

Identify what the codebase uses today, then read that playbook:

| Source | Detect it by | Playbook |
| --- | --- | --- |
| Svix | `svix` package (npm, PyPI, gem), `github.com/svix/svix-webhooks`, `svix.message.create`, `svix-signature` | https://webhookrelay.com/outbound-migration/svix.md |
| Hookdeck Outpost | `@hookdeck/outpost-sdk`, `github.com/hookdeck/outpost/sdks/outpost-go`, `outpost_sdk`, `api.outpost.hookdeck.com`, `x-outpost-signature` | https://webhookrelay.com/outbound-migration/hookdeck-outpost.md |
| Convoy | `convoy.js`, `github.com/frain-dev/convoy-go`, `convoy-python`, `/v1/projects/.../events`, `X-Convoy-Signature` | https://webhookrelay.com/outbound-migration/convoy.md |
| Hook0 | `hook0-client` (npm, crate, PyPI), `app.hook0.com`, `/api/v1/event/`, `X-Hook0-Signature` | https://webhookrelay.com/outbound-migration/hook0.md |
| Homegrown | an outbox/webhook table, a worker or job that POSTs to customer URLs with its own HMAC signing and retries (cron, SQS, Sidekiq, Celery, BullMQ) | https://webhookrelay.com/outbound-migration/diy.md |

## Shared facts

- API base URL `https://my.webhookrelay.com`, HTTP Basic auth with an access token
  key and secret (`RELAY_KEY`, `RELAY_SECRET`). MCP server: `https://my.webhookrelay.com/v1/mcp`.
- The Outbound Gateway is generally available on the Basic, Business and Pro plans
  (not Free). `GET /v1/outbound/access` →
  `{"enabled":true,"limits":{"consumers":...,"endpoints_per_consumer":...,"event_types":...}}`.
  Check it first. `enabled: false` means the account is on Free: the human upgrades
  at https://my.webhookrelay.com/account. Every other `/v1/outbound` route returns `403` until then.
- Plan limits: Basic 100 consumers / 10 endpoints each / 300 event types; Business
  500 / 25 / 500; Pro 1,000 / 100 / 1,000. Platform ceiling (Enterprise): 1,000
  consumers, 100 endpoints per consumer, 1,000 event types, 100 event types per endpoint.
- No bulk import endpoint. Event type and consumer creation are upserts; a
  repeated endpoint URL for a consumer returns `409`.
- **Existing signing secrets can be imported.** `POST /v1/outbound/consumers/{consumer}/endpoints`
  accepts an optional `secret`: 32-100 standard base64 characters (padded or not)
  decoding to at least 24 bytes, `whsec_` prefix optional (Svix's format). URL-safe
  base64, UUIDs and asymmetric `whsk_`/`whpk_` keys are refused with `400`, and the
  error never echoes the secret. Without `secret`, a new `whsec_` secret is
  generated. Read it later with `POST /v1/outbound/endpoints/{id}/secret/reveal`.
  MCP `create_outbound_endpoint` accepts `secret` too. The TypeScript and Go SDKs
  add a `secret` option in their next release; until then use the REST call.
- An imported secret keeps the customer's key. Receivers change nothing only if the
  old scheme already was Standard Webhooks (Svix, Outpost in Standard Webhooks mode).
  Other sources (Convoy, Hook0, Outpost default mode, most homegrown senders) still
  change the signature check.
- Deliveries use Standard Webhooks: `webhook-id`, `webhook-timestamp`,
  `webhook-signature` (`v1,` HMAC-SHA256), plus `webhook-event-type` and
  `webhook-consumer`. Receiver guide: https://webhookrelay.com/docs/outbound/receiving
- Payloads: JSON up to 256 KiB. Publish: `POST /v1/outbound/messages` with an
  `Idempotency-Key` header → `202`.
- Paused endpoints record new messages as `rejected` (skipped) and are not sent
  automatically on resume.
- SDKs: TypeScript `@webhookrelay/sdk` (`relay.outbound.*`), Go
  `github.com/webhookrelay/webhookrelay-go` (`api.*Outbound*`). No Python SDK.

## The procedure every playbook follows

1. Preconditions: credentials, `GET /v1/outbound/access`, limits.
2. Inventory the codebase for the old provider.
3. Export from the old provider (read-only). Read endpoint secrets only inside the
   import process, to pass them to endpoint create; never print or store them.
4. Check the export against plan limits before writing anything.
5. Import event types, consumers and endpoints (with `secret` where it fits the
   format); pause each new endpoint.
6. Dual publish behind a feature flag (old provider still delivers).
7. Verify with a free Webhook Bin (https://webhookrelay.com/webhook-bin.md) and
   https://webhookrelay.com/verify-standard-webhooks-signature, then compare counts.
8. Report and **stop for human approval**.
9. Cutover one customer at a time (resume new endpoints, disable old ones). Customers
   whose receiver changes (scheme, header names or a new secret) switch only after
   the change is live.
10. Rollback is available at every stage.

## Never do (all playbooks)

- Never delete the old provider's endpoints, applications, tenants or projects.
- Never print, log or commit signing secrets or API tokens.
- Never switch production traffic without explicit human approval.
- Never exceed plan limits silently.
- Never let two providers deliver live to the same customer at once.
