Openhook
Self-hostable webhook, email, and DNS inboxes that let AI agents capture callbacks, inspect payloads, and forward events to local handlers.
Hosted MCP Server
npx add-mcp 'https://openhook.dev/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
Openhook
An event inbox for AI agents. Create a public URL, wait for a callback, inspect what arrived, and continue the task.
Openhook operates its own HTTP, SMTP, and authoritative DNS capture servers. Events live in its SQLite database. No external webhook service is involved.
Try it or host it
Create a free inbox without an account, or connect your agent. Optional hosted Pro provides persistent inboxes and account recovery; see the current availability and plans. Self-hosting remains MIT licensed with no Openhook account or subscription.
Practical recipes: local forwarding, GitHub push callbacks, and Stripe test callbacks.
First callback in under a minute
With Python 3 installed, clone the repository and run the disposable example:
git clone https://github.com/openhook-dev/openhook-inbox.git
cd openhook-inbox
python3 examples/first_callback.py
It creates a free inbox on openhook.dev, sends a real callback, verifies the
original bytes, then deletes its inbox and payload. The private token stays in
memory. No account, API key, or Python dependencies are needed for this example.
Minimal operational activity metadata remains after deletion.
Install the agent skill
For Codex, Claude Code, and other compatible Agent Skills clients:
npx skills add openhook-dev/openhook-inbox --skill openhook
The skill setup and permissions explain MCP connection, REST fallback, local forwarding, and handling private inbox tokens. Installing the skill does not install or start the server.
Run it
From the checkout, install uv and start your own server:
uv sync
uv run openhook --http
Open http://127.0.0.1:8788/app. The website, capture API, and Streamable HTTP MCP endpoint share one service and one durable store.
Connect an MCP client to http://127.0.0.1:8788/mcp, or use
uv run openhook for stdio. A local stdio process shares the same database
with a running HTTP process when both use the same OPENHOOK_DATABASE.
The HTTP process must be running to receive HTTP callbacks. Check the same loop
against your server with python3 examples/first_callback.py --url http://127.0.0.1:8788.
External providers need a publicly reachable origin; the localhost example only
proves your local server works.
GitHub releases provide
versioned source and Python distributions when available. The openhook-mcp
package is not currently published on PyPI; use the source checkout or a release
artifact rather than assuming a package-index install works.
Agent workflow
- Call
create_webhook. Save the privateohk_management token. - Give the returned public HTTP URL, email address, or DNS name to the sender.
- Call
wait_for_requestwith the management token. - Inspect the event and act. Reconnect using the returned
next_sincecursor.
Public capture addresses cannot read or delete events. Rotate a compromised
management token with rotate_webhook_token. Rotation preserves public addresses.
Anonymous inboxes expire within seven days. Each accepts up to 1000 retained events. When full, capture returns a capacity error instead of evicting accepted events. Individual events are limited to 1 MB. Email and DNS addresses are returned only when their listeners are configured.
Native tools
29 tools cover inbox creation, response configuration, token rotation, event inspection, literal search, notes, JSON/CSV export, exact-byte download, HTTP/email waits, test events, link/code extraction, callback checking, delayed responses, provider subscriptions, deletion, and activity history. See the generated tool reference.
Durable activity history
Every accepted HTTP, email, and DNS receipt and successful local mutation writes
an activity record in the same transaction as its state change. Duplicate source
deliveries also receive records. History includes notes, response settings,
delayed responses, token rotation, subscriptions, deletion, and expiry.
Read it with get_webhook_activity or POST /api/call with action: activity,
the private token, a since cursor, and a bounded limit.
Minimal activity metadata remains after payload deletion and expiry. It excludes private tokens, provider credentials, bodies, notes, and signing secrets. Existing events are marked as imported; their previous mutation history cannot be reconstructed. The application appends records; this is not a cryptographic tamper-proof log. Rejected captures are not stored as accepted events.
Set verification_secret through configure_webhook to require SHA256 HMAC
on raw HTTP bodies. Openhook accepts X-Openhook-Signature: sha256=… and
GitHub's X-Hub-Signature-256. Invalid signatures are rejected before capture.
Idempotency-Key and X-GitHub-Delivery deduplicate retries within an inbox.
Incoming URLs with ?openhook_wait=1 wait up to 20 seconds for an agent response.
Start respond_to_next_request before sending such a request.
Configuration
| Environment variable | Purpose |
|---|---|
OPENHOOK_ORIGIN | Public origin, default http://127.0.0.1:8788 |
OPENHOOK_DATABASE | SQLite path, default data/openhook.sqlite3 |
OPENHOOK_EMAIL_DOMAIN | Own inbound mail domain; unset disables SMTP |
OPENHOOK_SMTP_PORT | SMTP listener, default 2525; map public port 25 |
OPENHOOK_DNS_DOMAIN | Own delegated callback zone; unset disables DNS |
OPENHOOK_DNS_PORT | UDP/TCP DNS listener, default 5353; map public port 53 |
OPENHOOK_DNS_ADDRESS | IPv4 address returned for DNS A lookups |
SMTP rejects mail for unknown inboxes and never relays mail. DNS answers only for the configured zone and does not provide recursion.
Deploy
Use the Dockerfile and compose.dokploy.yaml in a separate Dokploy project.
Attach the Openhook domain to service openhook, port 8788. Its named data
volume survives releases. See deployment notes.
Provider registration and local delivery
Call register_github_webhook, register_stripe_webhook, or
register_linear_webhook to create a native inbox and subscribe at the source.
Provider API credentials are used transiently and never saved. Raw signatures
are checked before capture; Stripe and Linear timestamps are checked for replay.
Call unregister_webhook before the inbox expires to remove the source subscription.
For local delivery, set OPENHOOK_TOKEN to your private inbox token, then run:
uv run openhook-listen --forward http://127.0.0.1:8080/webhook
# Or wake an already configured local OpenClaw:
uv run openhook-listen --openclaw
OpenClaw delivery also requires OPENCLAW_HOOKS_TOKEN. With no destination,
the listener prints events as JSON. It connects outward over HTTPS, saves a
cursor after successful delivery, and retries failed deliveries. Delivery is
at least once: recipients should deduplicate by event ID. Each destination has
its own cursor; run one listener per destination and token. Managed SSH tunnel
provisioning is not included.
Verify
The website uses a centered text interface and the self-hosted Paper Mono
v1.000 variable font and the approved Openhook logo. Public pages and live tool
schemas are rendered in HTML before JavaScript runs. Agent entry points include
/agents.md, /skill.md, /llms.txt, /llms-full.txt, /docs.md, and
/openapi.json. Its webhook demo creates an inbox, sends a real callback,
and reads the captured event. Refresh asset versions after editing web/:
bun run build
uv run --extra dev pytest
node --check web/assets/app.js
Tests exercise native HTTP capture, private-token isolation, signature checks, idempotency, persistence, expiry, MCP, and real local SMTP and UDP/TCP DNS.
MIT licensed. Required third-party notices are in THIRD_PARTY_NOTICES.md.
Hosted development and contributing
Accounts and billing are disabled by default. The optional hosted edition adds GitHub identity, revocable agent keys, owned inboxes, and verified paid-invoice entitlements. See hosted operations for configuration, limits, test-mode requirements, and launch dependencies. No real merchant or paid launch is implied by the presence of this code.
See contributing and security reporting.