Webhook Service Setup
Learn how to set up, configure, and run the Reloop webhook service locally.
The Webhook Delivery Service is an ElysiaJS microservice that manages the scheduling, formatting, cryptographical signing, and dispatching of outbound event webhooks to developer callback URLs. It listens for system-wide events and processes delivery retries using BullMQ.
Before configuring the Webhook Service, make sure you've completed the base Local Development Setup (installed Bun, configured the hosts file, and started the Docker container stack).
Quick Start
Dependencies are handled globally at the root directory. You can start the Webhook Service development server instantly:
# Start the dev server with hot reloading
bun be:webhook:dev
cd apps/backend/webhook
bun dev
Once running, the Webhook Service will listen locally on port 8013. You can query it via Caddy at https://local.reloop.sh/api/webhook ๐
Service Information
- Directory Location:
apps/backend/webhook/ - Local Listening Port:
8013(configurable withPORTenv variable) - Unified Access Route: https://local.reloop.sh/api/webhook
- Primary Tech Stack: ElysiaJS, BullMQ, Redis, PostgreSQL, NATS
- Dependencies: Relies on PostgreSQL (to query subscriber callback targets) and Redis (to schedule and process BullMQ delivery jobs).
Environment Configuration
The Webhook Service environment variables are defined in apps/backend/webhook/.env. If you ran bun env:setup at the root, your .env is already configured:
# Database & Cache Configurations
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
# Server Gateway Configuration
PORT=8013
BASE_URL="https://local.reloop.sh"
NODE_ENV=development
NODE_TLS_REJECT_UNAUTHORIZED=0
# Event Bus Broker
NATS_URL=nats://localhost:4222
Reference Table
| Variable | Required | Default Value | Description |
|---|---|---|---|
PG_URL | YES | postgresql://... | PostgreSQL database connection string. |
REDIS_URL | YES | redis://... | Redis connection URL used by BullMQ to schedule delivery jobs. |
PORT | YES | 8013 | Local port number the Elysia app listens to on the host. |
BASE_URL | YES | https://local.reloop.sh | Main application domain (used for cookie validations). |
NATS_URL | YES | nats://localhost:4222 | Connection URL for NATS event bus messaging. |
NODE_TLS_REJECT_UNAUTHORIZED | NO | 0 | Allows self-signed certificates in local development mode. |
NODE_ENV | YES | development | Active environment scope (development or production). |
Development Commands
All workspace operations should be executed from the monorepo root directory using Turborepo filters:
| Command | Action | Scope |
|---|---|---|
bun be:webhook:dev | Start the Webhook dev server with hot reloading | Development |
bun run --filter=webhook build | Compile the Webhook service for production | Production |
bun run --filter=webhook start | Run the compiled production build locally | Production |
bun run --filter=webhook check-types | Verify TypeScript type-safety | Code Quality |
Database Schema & Relationships
The relational subscriber endpoints configuration, event subscriptions, payload archives, and dispatch delivery logs are defined inside packages/db/src/schema/webhook.ts.
Enums
| Enum | Values | Description |
|---|---|---|
webhook_status | active ยท paused ยท disabled ยท failed | Webhook subscription delivery status |
webhook_delivery_status | pending ยท success ยท failed ยท retrying | Specific event dispatch execution status |
webhook Table
Holds user-configured outbound endpoints, secret signing tokens, and execution aggregations.
| Column | Type | Default | Description |
|---|---|---|---|
id | text | wh_{cuid} | Primary Key |
name | varchar(255) | โ | Friendly name label for the endpoint |
url | text | โ | Recipient HTTP target URL destination |
secret | text | โ | Cryptographic HMAC-SHA256 signature signing token |
organizationId | text | โ | Foreign Key โ organization.id |
userId | text | โ | Foreign Key โ user.id |
status | webhook_status | active | Active subscription configuration state |
customHeaders | jsonb | null | Key-value dictionary of custom HTTP headers sent (Record<string, string>) |
rateLimitEnabled | boolean | true | Throttling toggle constraint |
maxRequestsPerMinute | integer | 60 | Request rate volume ceiling |
maxRetries | integer | 3 | Maximum retry attempts for failed dispatches |
successCount | integer | 0 | Cumulative successful deliveries count |
failureCount | integer | 0 | Cumulative failed delivery attempts count |
consecutiveFailures | integer | 0 | Current series of consecutive failures before auto-disabling |
webhook_delivery Table
High-fidelity transactional logs tracking the chronological attempts, network payloads, durations, and response status codes for every event dispatch.
| Column | Type | Default | Description |
|---|---|---|---|
id | text | whde_{cuid} | Primary Key |
webhookId | text | โ | Foreign Key โ webhook.id |
webhookEventId | text | null | Foreign Key โ webhook_event.id |
eventType | text | โ | Semantic event type (e.g. domain.verified) |
status | webhook_delivery_status | pending | Dispatch attempt resolution state |
requestUrl | text | โ | Dispatched network URL |
requestHeaders | jsonb | null | Sent headers including security signature |
requestBody | jsonb | โ | Sent JSON body payload |
responseStatus | integer | null | Received HTTP response status code |
responseBody | text | null | Received body output stream |
attemptNumber | integer | 1 | Increment sequence of current attempt |
durationMs | integer | null | Request completion time (ms) |
Other Webhook Tables
webhook_event_subscription: Link table mapping webhooks to specific event filters.webhook_event: Relational event log storing the canonical payload structure and workspace context representing an asynchronous event trigger.
Interactive API Docs (Swagger)
The Webhook Service publishes dynamic Swagger documentation directly from ElysiaJS. To test endpoints and query API structures:
- Open your browser and navigate to the interactive Swagger playground at https://local.reloop.sh/api/webhook/openapi (or http://localhost:8013/api/webhook/openapi directly).
- Use the interactive Swagger UI playground to run queries and inspect request schemas.
Core Operational Mechanics
- Event Capture: The service captures real-time campaign dispatches, click logs, and delivery notifications by subscribing to core NATS subjects (
kumomta.event.*,tracking.*). - Subscription Matcher: For every captured event, the PostgreSQL database is queried to find matching customer webhook configurations in the
webhook_subscriptiontable. - Queue Scheduling: If an active subscriber matches the trigger event, a delivery payload is constructed and scheduled in BullMQ (
webhook-delivery-queue) in Redis. - Signed Outbound Attempt: A background BullMQ worker pulls the job, signs the payload string using the customer's shared subscription secret (
x-reloop-signatureheader), and dispatches it over HTTP POST. - Robust Retry Loop: If the destination server returns a non-2xx response, the worker registers a fail state, scheduling automated retries using exponential backoff (configured in
queues/webhook.queue.ts).