Guide · 7 min read
Webhook receiver
Receive signed webhooks on localhost to trigger Automation workflows.
- Product stage
- Pre-release development
- Last reviewed
- 2026-07-23
- Successful result
- The change is visible in the active project.
What this accomplishes
Receive signed webhooks on localhost to trigger Automation workflows.
The webhook receiver is the local HTTP server that receives webhooks from external systems. The
receiver is bound to 127.0.0.1; the receiver is not a public endpoint. The receiver uses
HMAC-SHA256 to authenticate every request; the receiver injects secrets at execution time. The
receiver is the right shape for users who want to receive webhooks without exposing a public
endpoint.
This page is the webhook receiver. The blog post on the receiver is at webhook security on loopback; the local scheduler is in local scheduler.
Current availability
This page documents behaviour verified in Miton development builds. Miton is still pre-release and is not publicly downloadable; invited beta users will receive a signed desktop build with automatic updates.
Before you start
- A Miton development build, an active project and Automation open.
- A connected model plus permission for every file, network or scheduled action in the workflow.
The loopback binding
The receiver is bound to 127.0.0.1. The receiver is not accessible from another machine on the
network; the receiver is not accessible from the public internet.
The receiver’s default port is 7733. The user can change the port in Settings under “Webhook →
Port” (default: 7733). The user cannot change the bind address; the loopback is the security
model.
A user who needs a public webhook endpoint can use a reverse proxy. The user runs a reverse proxy on the user’s machine; the reverse proxy accepts public traffic; the reverse proxy forwards to the loopback receiver. The reverse proxy is the user’s; the user is responsible for the reverse proxy’s security.
The HMAC-SHA256 authentication
Every webhook request is authenticated with HMAC-SHA256. The external system computes an HMAC-SHA256
of the request body using a shared secret; the external system sends the HMAC in the
X-Miton-Signature header. The receiver computes the same HMAC using the same shared secret; the
receiver compares the two HMACs.
If the HMACs match, the request is authenticated; the receiver processes the request. If the HMACs
do not match, the request is rejected with a 401 Unauthorized. The receiver also rejects requests
with a missing X-Miton-Signature header.
The receiver supports two HMAC variants:
X-Miton-Signature: sha256=<hex>— the standard variant. The HMAC is computed over the raw request body.X-Miton-Signature: sha256=<hex>,t=<unix-timestamp>— the timestamped variant. The HMAC is computed over<timestamp>.<body>. The timestamp is used for replay protection (see below).
The per-workflow secret
The shared secret is per-workflow. The user generates a secret when the user creates the webhook-receiving workflow; the user copies the secret to the external system. The secret is stored in the user’s secret store (encrypted at rest with the OS keychain); the secret is never written to the workflow definition or the run history.
The secret is shown to the user once when the user creates the workflow. The user can re-show the secret from the workflow’s settings; the user can also rotate the secret (see below).
The secret rotation
The user can rotate the secret. The user opens the workflow’s settings; the user clicks “Rotate secret”; the system generates a new secret; the user copies the new secret to the external system.
The secret rotation can be immediate or graceful:
- Immediate — the old secret is invalid immediately. The user must update the external system before the next webhook arrives.
- Graceful — both the old and the new secret are accepted for a configurable grace period (default: 24 hours). The user can update the external system during the grace period; the user does not lose any webhooks during the rotation.
The default is immediate. The user can configure the rotation to be graceful in the workflow’s settings.
The replay protection
The receiver has a replay protection mechanism. The receiver tracks the timestamp of the last request from each external system; the receiver rejects any request with a timestamp older than the configured replay window.
The replay protection is per-external- system. The user configures the external system’s identity (e.g. the GitHub webhook ID, the Stripe webhook ID). The receiver uses the identity to track the last request timestamp.
The replay window is configurable. The default is 5 minutes. The user can disable the replay protection; the user can also configure a longer window (15 minutes, 1 hour).
The replay protection is enabled by default when the external system uses the timestamped HMAC variant. The replay protection is not available with the non-timestamped variant (the receiver cannot know when the request was signed).
The payload format
The receiver accepts any payload format. The payload is passed to the workflow as a JSON object. The receiver does not transform the payload; the receiver passes the payload as-is to the workflow.
The workflow can parse the payload with the JSON parser. The workflow can also pass the payload to a sub-agent for natural- language processing.
The receiver also supports binary payloads. The binary payload is base64-encoded and passed to the workflow as a string. The workflow can decode the base64 string with the standard base64 decoder.
The routing model
The receiver routes the webhook to the workflow based on the path. The path is
/webhook/<workflowId>. The path is per workflow; the user can have many webhook-receiving
workflows, each with a different path.
The receiver also supports wildcard routing. A user can configure a workflow to receive webhooks
from any path under /webhook/. The wildcard routing is useful for users who want to receive
webhooks from a system that has a fixed path (e.g. a webhook provider that always posts to
/webhook/).
What the webhook receiver does not cover
The webhook receiver does not cover:
- Public webhook endpoints — the receiver is on the user’s machine; the receiver is not a public endpoint. The user can use a reverse proxy to expose the receiver to the public internet; the reverse proxy is the user’s responsibility.
- WebSocket support — the receiver is HTTP only. The receiver does not support WebSocket. The user can use a third-party tool for WebSocket.
- Custom authentication schemes — the receiver is HMAC-SHA256 only. The receiver does not support OAuth, API keys, or other authentication schemes. The user can use a third-party tool for other authentication schemes.
The webhook receiver focuses on the common case (an external system posting a payload to a local endpoint). The less common cases are covered by the user’s existing tools.
What success looks like
The webhook receiver is on the user’s machine. The receiver is on 127.0.0.1; the receiver is not a
public endpoint. The user does not have to expose the receiver to the internet; the user does not
have to set up a tunnel; the user does not have to configure a firewall rule.
Use the HMAC for authentication. Every external system has a shared secret; the receiver uses the shared secret to authenticate every request. Rotate the secret regularly.
Use the secret rotation for secrets. Reference secrets with the secret store; the receiver injects the secret at execution time; the secret is never in the workflow definition, the run history, or the log.
Use the replay protection for the external system. The receiver tracks the timestamp of the last request; the receiver rejects stale requests. The default replay window is 5 minutes; the user can configure the window per external system.
Common failures and recovery
- Inspect the failed run and the exact node output before replaying it.
- Confirm credentials, permissions and test fixtures belong to the active project.
- Use dry-run or step replay before starting the complete workflow again.
Related next action
Open the Automation overview to connect this task to the rest of the workflow.