Hookroute is a focused, self-hosted webhook gateway. It captures inbound requests, evaluates project-owned routes, and delivers each matching event independently to HTTP, Discord, immediate email, or a scheduled email digest.
It is deliberately smaller than a general workflow engine and can run on ordinary PHP/MySQL hosting with one cron entry—no Redis, Docker, or resident process is required.
![]() |
![]() |
| Explicit routes Connect a source and destination, filter events, and shape the outgoing payload. |
Inspectable events Review the captured request and every independent delivery attempt. |
- Multiple users, shared projects, invitations, and owner/admin/member roles
- Ownership transfer and invitation-only registration after the first account
- Secret source URLs plus optional HMAC-SHA256 verification
- Redacted raw request capture and idempotency-key handling
- Fan-out with payload filters and per-route templates
- HTTPS webhooks with static headers and optional outbound HMAC signing
- Discord delivery with mentions disabled by default
- Immediate email and daily email digest windows
- Database-backed retries with exponential backoff
- Delivery response excerpts, errors, and one-click replay
- Per-project event retention
- SQLite for a small installation; MySQL for shared hosting
For production:
- PHP 8.4 or newer with the common Laravel extensions, including cURL, mbstring, OpenSSL, PDO, tokenizer, XML, and ZIP
- MySQL 8+ or SQLite
- Composer 2
- A working local sendmail transport or an SMTP account for invitations, immediate email destinations, and digests
- A cron facility that can run every minute
- A web server whose document root can point to the project's
public/directory
Node.js is only needed while building frontend assets. The generated public/build/ directory can be uploaded to a host without Node.js.
Local development additionally requires Docker Desktop or another Docker Compose compatible runtime. The included Laravel Sail environment provides PHP 8.4, MySQL 8.0, Node.js, and Mailpit.
git clone https://github.com/tbuck-software/hookroute.git
cd hookroute
composer install
cp .env.example .env
sail up -d
sail artisan key:generate
sail artisan migrate --seed
sail npm install
sail npm run buildThe application is available at http://localhost; Mailpit is available at http://localhost:8025.
The development seed creates demo@hookroute.test with password password and a populated example project.
The public landing page includes the product model, feature overview, fixed-price setup offer, contact details, imprint, and privacy information. The canonical repository URL enables its GitHub calls to action and can be overridden for a fork:
HOOKROUTE_REPOSITORY_URL=https://github.com/tbuck-software/hookrouteThe product images on the landing page and in this README are generated from the seeded demo project with Playwright. Install its Chromium build once, keep Sail running, and capture all three views with one command:
npm run screenshots:install
sail up -d
npm run screenshotsThe capture script recreates only the demo@hookroute.test account and its production-systems project. Other local users and projects are not changed. Set HOOKROUTE_SCREENSHOT_BASE_URL when the application is available at a URL other than APP_URL.
Run the scheduler during development in a second terminal so database-queued deliveries and digests are processed:
sail artisan schedule:workThis repository includes Sail's laravel.test, MySQL, and Mailpit services. If your shell alias points to vendor/bin/sail, all commands above work as written. Use sail down to stop the environment and sail down -v only when the local MySQL volume should also be deleted.
Start from a tagged release rather than an arbitrary commit from main. Replace v0.1.0 with the release being installed:
git clone https://github.com/tbuck-software/hookroute.git
cd hookroute
git checkout v0.1.0
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generateConfigure .env before running the database migration:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://webhook.example.com
SESSION_SECURE_COOKIE=true
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=hookroute
DB_USERNAME=hookroute
DB_PASSWORD=change-me
QUEUE_CONNECTION=database
SESSION_DRIVER=database
CACHE_STORE=database
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=hookroute@example.com
MAIL_PASSWORD=change-me
MAIL_FROM_ADDRESS=hookroute@example.comThen finish the installation:
php artisan migrate --force
npm ci
npm run build
php artisan optimizeIf Node.js is unavailable on the server, run npm ci && npm run build for the exact same release locally and upload the generated public/build/ directory.
- Point the domain document root at the project's
public/directory. - Make
storage/andbootstrap/cache/writable by PHP. - Keep
QUEUE_CONNECTION=database,SESSION_DRIVER=database, andCACHE_STORE=database. - Add one cron entry:
* * * * * cd /absolute/path/to/hookroute && php artisan schedule:run >> /dev/null 2>&1That scheduler creates due digests, drains the database delivery queue for up to 50 seconds, and prunes expired event history.
If DB_QUEUE_RETRY_AFTER is customized, keep it greater than both processing leases and job timeouts. The shipped 90-second queue retry window is paired with 30/60-second job timeouts and 45/75-second delivery/digest leases.
The first account can register without an invitation. Afterwards, new accounts require a valid project invitation unless HOOKROUTE_ALLOW_PUBLIC_REGISTRATION=true is set deliberately.
Hookroute uses semantic version tags and GitHub Releases. There is deliberately no automatic updater or background version check: an update can contain database migrations and should be installed consciously after reading its release notes.
Before updating:
- Read the release notes and note any version-specific instructions.
- Back up the database and
.envfile. - Pause the scheduler cron entry so no delivery worker starts during the update.
- Ensure the working tree contains no local source modifications.
Replace v0.1.1 with the target release and run:
php artisan down --retry=60
git fetch --tags origin
git checkout v0.1.1
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan migrate --force
php artisan optimize
php artisan upIf frontend assets are built elsewhere, upload the new public/build/ directory before running php artisan up. Resume the scheduler afterwards and send a test event through one route.
Never replace .env or run php artisan key:generate during an update. Source and destination secrets are encrypted with the existing APP_KEY; changing it without a migration plan makes those values unreadable. Database downgrades are not supported, so restore the backup when rolling back across migrations.
The optional fixed-price setup service covers initial installation and 30 days of setup-related support. It does not include ongoing maintenance or automatic updates.
Release changes are recorded in CHANGELOG.md. Subscribe to the repository's GitHub Releases to be notified about new versions.
Create a source in the UI and send a request to its generated URL:
curl -X POST 'https://webhook.example.com/hooks/{source}/{secret}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-1842-created' \
-d '{"event":"order.created","order":{"id":1842}}'When source HMAC verification is enabled, sign the exact raw request body with the configured secret and send the hex digest as either sha256={digest} or {digest} in the configured header.
Templates can reference the following roots:
payload.*— parsed inbound JSON or form fieldsevent.id,event.received_atsource.id,source.nameproject.name,project.slug
Example HTTP JSON template:
{
"event_id": "{{ event.id }}",
"order_id": {{ payload.order.id }},
"source": "{{ source.name }}"
}The passthrough mode instead forwards the captured raw body and its original content type.
- Source and destination secrets are encrypted at rest with
APP_KEY. - Read-only members cannot retrieve source URLs or destination credentials.
- Sensitive inbound headers are discarded rather than persisted.
- Outbound URLs must use HTTPS, resolve only to public IP space, and are pinned to the validated address for the connection unless
HOOKROUTE_ALLOW_PRIVATE_DESTINATIONS=trueis explicitly configured. - Redirects are disabled for outbound delivery and Discord mentions are suppressed.
- Destination response downloads and daily digest sizes are capped to protect workers from unbounded memory use.
- Rotate
APP_KEYonly with a migration plan: encrypted configuration depends on it.
For intentional private-network delivery, enable the private-destination setting only on a trusted installation whose project administrators are allowed to reach that network.
sail artisan test
sail npm run build
sail npm run lintThe test suite covers authorization, invitation acceptance, ingress authentication, idempotency, filtering, templating, HTTP signing, email, digest windows, retention, and network-address rejection.
MIT


