webhookr-relay
v0.1.5
Published
Local CLI and SDK for polling Webhookr relay events and forwarding them to localhost.
Readme
webhookr-relay
Local CLI and SDK for Webhookr — relay webhooks to your local dev server in real time, no tunnels needed.
Quick start
npm install -g webhookr-relay
webhookr init # authenticate + pick your project
webhookr relay # start forwarding webhooks to localhostThe interactive setup writes a profile-based .webhookr.json, so one checkout can keep separate relay targets for local, staging, or team-specific environments.
Commands
| Command | Description |
|---------|-------------|
| webhookr init | Interactive setup wizard — authenticates via browser, picks a project/channel, detects your framework, asks how to route locally, and saves a profile-based .webhookr.json config |
| webhookr relay | Start relaying webhooks to your local server (reads .webhookr.json, optionally with --profile) |
| webhookr login | Re-authenticate with Webhookr via browser |
| webhookr logout | Revoke the saved CLI token and remove local authentication |
| webhookr status | Show current config and connection status |
Saved config format
webhookr init now writes a canonical versioned config:
{
"version": 2,
"activeProfile": "my-project-frederik-macbook",
"profiles": [
{
"name": "my-project-frederik-macbook",
"serverUrl": "https://webhookr.test",
"project": "my-project",
"relays": [
{
"channel": "stripe",
"apiKey": "whrk_...",
"targetUrl": "http://127.0.0.1:8000/webhooks",
"targetStrategy": "preserve-path"
}
]
}
]
}Older flat .webhookr.json files are still read and normalized automatically.
Manual relay (without init)
If you prefer flags over the interactive wizard:
webhookr relay \
--server https://webhookr.co \
--api-key whrk_... \
--target http://localhost:8000/webhooks \
--target-strategy preserve-pathAll relay flags
| Flag | Description | Default |
|------|-------------|---------|
| --profile <name> | Use a saved profile from .webhookr.json | active profile |
| --server <url> | Webhookr server URL | — |
| --api-key <key> | Your API key | — |
| --channel <slug> | Restrict polling to a single channel | all channels visible to the key |
| --target <url> | Local URL or base URL to forward webhooks to | — |
| --target-strategy <fixed\|preserve-path> | Keep the target fixed, or append the original request path and query to the target base URL | fixed |
| --interval-ms <ms> | Poll interval in milliseconds | 2000 |
| --retry-delay-ms <ms> | Initial retry delay for poll, ack, and local-forward failures | interval-ms |
| --max-retry-delay-ms <ms> | Maximum retry delay before exponential backoff stops growing | retry-delay-ms × 5 |
| --batch-size <n> | Events per poll, max 100 | 20 |
| --timeout-ms <ms> | Local forward timeout | 10000 |
| --header <name:value> | Static header added to forwarded requests (repeatable) | — |
| --verbose | Enable debug logging | false |
If you pass --server, --api-key, and --target, the CLI runs in direct single-relay mode and bypasses .webhookr.json. If you omit those flags, it reads the saved config and starts every relay in the selected profile.
SDK usage
You can also use webhookr-relay as a library:
import { createRelayClient } from 'webhookr-relay';
const relay = createRelayClient({
serverUrl: 'https://webhookr.co',
apiKey: 'whrk_...',
targetUrl: 'http://localhost:3000/webhooks',
targetStrategy: 'preserve-path',
});
await relay.start();
// Later, to gracefully shut down:
await relay.stop();Options
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| serverUrl | string | Webhookr server URL | required |
| apiKey | string | Your API key | required |
| targetUrl | string | Local URL to forward to | required |
| targetStrategy | 'fixed' \| 'preserve-path' | Reuse the target as-is, or append the original request path and query to it | 'fixed' |
| channel | string | Optional channel slug filter for polling | unset |
| pollIntervalMs | number | Poll interval in ms | 2000 |
| retryDelayMs | number | Initial retry delay for poll, ack, and local-forward failures | pollIntervalMs |
| maxRetryDelayMs | number | Maximum retry delay before exponential backoff stops growing | retryDelayMs × 5 |
| batchSize | number | Events per poll (max 100) | 20 |
| timeoutMs | number | Forward timeout in ms | 10000 |
| staticHeaders | Record<string, string> | Headers added to forwarded requests | {} |
| logger | RelayLogger | Custom logger implementation | noop |
How it works
- Your webhook provider (Stripe, GitHub, etc.) sends events to your Webhookr ingest URL
- Webhookr stores the events
- The CLI polls for new events and forwards them to your local server
- Events are acknowledged after successful local delivery
When targetStrategy is preserve-path, the local forwarded URL is rebuilt from:
- the configured
targetUrlpath as the base - the original webhook
request_path - the original
query_string, appended to any existing target query params
Forwarded requests also include relay metadata headers such as:
x-webhookr-event-idx-webhookr-delivery-attemptx-webhookr-received-atx-webhookr-original-pathx-webhookr-original-queryx-webhookr-source-ipwhen available
x-webhookr-event-id stays stable across retries and reconnects, so local handlers can use it as an idempotency key. x-webhookr-delivery-attempt increments when the same event is retried within the current relay process.
Requirements
- Node.js 20+
Releasing
The npm publish workflow is tag-driven. A normal branch push does not publish the CLI.
To release a new version:
- bump
packages/webhookr-relay-client/package.json - push the commit to your branch or
main - create and push a matching tag in the form
relay-vX.Y.Z
Example:
git tag relay-v0.1.3
git push origin relay-v0.1.3The GitHub Actions publish-relay workflow verifies that the relay-vX.Y.Z tag exactly matches the package version before it runs npm publish.
License
MIT
