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

  1. Call create_webhook. Save the private ohk_ management token.
  2. Give the returned public HTTP URL, email address, or DNS name to the sender.
  3. Call wait_for_request with the management token.
  4. Inspect the event and act. Reconnect using the returned next_since cursor.

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 variablePurpose
OPENHOOK_ORIGINPublic origin, default http://127.0.0.1:8788
OPENHOOK_DATABASESQLite path, default data/openhook.sqlite3
OPENHOOK_EMAIL_DOMAINOwn inbound mail domain; unset disables SMTP
OPENHOOK_SMTP_PORTSMTP listener, default 2525; map public port 25
OPENHOOK_DNS_DOMAINOwn delegated callback zone; unset disables DNS
OPENHOOK_DNS_PORTUDP/TCP DNS listener, default 5353; map public port 53
OPENHOOK_DNS_ADDRESSIPv4 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.