Webhooks
Get notified when things happen in the gateway. Webhooks send HTTP POST requests to URLs you configure, useful for alerting, logging, or triggering automation.
Get notified when things happen in the gateway. Webhooks send HTTP POST requests to URLs you configure, useful for alerting, logging, or triggering automation.
The Problem It Solves
You're running browser-gateway in production. A provider starts failing at 3am. Without webhooks, nobody knows until users start complaining. With webhooks, you get a Slack message (or PagerDuty alert, or Discord notification) the moment the gateway puts it on cooldown.
Configuration
Two ways to manage webhooks:
- Dashboard at
/web/webhooks, add, edit, test, and delete webhooks with a form. Preferred for day-to-day changes because updates take effect without a restart. - REST API under
/v1/webhooks, same operations for scripted setups. See the Webhooks REST reference.
For static configs, a webhooks section in gateway.yml still works:
webhooks:
- url: https://hooks.slack.com/services/T00/B00/xxx
events: [provider.cooldown, queue.timeout]Each webhook has two fields: url (where to send the POST) and an optional events list.
Multiple webhooks
You can configure multiple webhook URLs. Each can listen to different events:
webhooks:
# Alert the team when a provider has issues
- url: https://hooks.slack.com/services/T00/B00/xxx
events: [provider.cooldown, queue.timeout]
# Log everything to a monitoring service
- url: https://your-logging-service.com/webhooks/browser-gateway
# No events filter = receives ALL eventsFiltering events
The events field is optional. If you omit it, that webhook receives every event. If you specify it, only those events are sent.
Available Events
| Event | Status | When It Fires |
|---|---|---|
provider.cooldown | firing | A provider failed too many times and entered cooldown (temporarily skipped) |
shutdown.start | firing | The gateway is shutting down (SIGTERM/SIGINT received) |
queue.timeout | firing | A request waited in the queue too long and timed out |
What each event means
provider.cooldown. The gateway tried to connect to a provider and it failed too often (over 50% failure rate). The provider is being skipped for 30 seconds (configurable). This is a warning sign, the provider might be having issues.
shutdown.start. The gateway received a shutdown signal (Ctrl+C or SIGTERM from Docker/systemd). It's stopping gracefully, active sessions are draining, and new connections are being rejected.
queue.timeout. A connection was waiting in the queue for a free provider slot, but the wait exceeded the configured timeoutMs. The client received a 503 error. If you see this often, you need more provider capacity.
The dashboard's Test button sends a one-off payload with "event": "test" to the URL you entered, so you can check the endpoint before saving.
There is no separate event when a provider recovers from cooldown. It returns to routing on its own when the cooldown ends.
Webhook Payload
Every webhook receives a JSON POST body with this structure:
{
"version": "1",
"timestamp": "2026-03-27T14:30:00.000Z",
"event": "provider.cooldown",
"status": "firing",
"source": "browser-gateway",
"data": {
"providerId": "my-cloud-provider",
"cooldownMs": 30000
}
}| Field | Description |
|---|---|
version | Payload version (currently "1") |
timestamp | ISO 8601 timestamp of when the event occurred |
event | Event name (e.g., provider.cooldown) |
status | "firing" for every event the gateway sends today |
source | Always "browser-gateway" |
data | Event-specific details. provider.cooldown sends providerId and cooldownMs; queue.timeout sends waitMs; shutdown.start sends an empty object. |
Verifying Requests
The self-hosted gateway does not sign webhook requests. To make sure a request came from your gateway, use a hard-to-guess URL (for example, one containing a random path segment or a secret query parameter), and keep it out of source control with an environment variable (see below). The dashboard and GET /v1/webhooks mask query parameters named token, apiKey, key, secret and password.
Delivery and Retries
Webhooks are delivered with automatic retries:
- First attempt, immediate
- Retry 1, after 1 second
- Retry 2, after 5 seconds
- Retry 3, after 15 seconds
- Give up, logged as a warning
Each delivery attempt has a 5-second timeout. If your webhook endpoint is slow or down, the gateway won't block, deliveries happen in the background.
Real-World Examples
Slack Notifications
Send provider status alerts to a Slack channel:
webhooks:
- url: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
events: [provider.cooldown, queue.timeout, shutdown.start]Slack incoming webhooks accept any JSON payload. You'll see the raw JSON in the channel. For formatted messages, put a small proxy service in between that transforms the payload into Slack's Block Kit format.
Discord Notifications
Discord webhooks also accept JSON POST:
webhooks:
- url: https://discord.com/api/webhooks/YOUR/WEBHOOKCustom Monitoring
Send all events to your own monitoring endpoint:
webhooks:
- url: https://your-app.com/api/gateway-eventsYour endpoint receives the JSON payload and can store it, graph it, trigger alerts, whatever you need.
Environment Variable for Secrets
Never put webhook URLs with tokens directly in the config file. Use environment variables:
webhooks:
- url: ${SLACK_WEBHOOK_URL}
events: [provider.cooldown, queue.timeout]# .env
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00/B00/xxxTips
- Start with
provider.cooldown: it is the earliest sign that a provider is failing. - Don't page on a single cooldown: cooldowns are short (30 seconds by default) and auto-recover. Repeated cooldowns for the same provider are the real signal.
queue.timeoutmeans you need more capacity: if you're getting these regularly, add more providers or increasemaxConcurrentlimits.shutdown.startis useful for ops: know when the gateway is restarting, especially in Docker/Kubernetes environments.