webhooks listen is how you observe delivery from a terminal.
127.0.0.1:4318 and serves / by default. Change any of that with --host, --port and --path.
Getting events to your machine
Registering a local endpoint runs into three constraints. Endpoints must be public https URLs, which requires a tunnel. The CLI does not bundle or download one. Run whichever you like, then pass its address with--public-url to tell the listener what it is reachable at.
Registration probes the URL with a challenge, which means the listener has to already be running when you save the endpoint in the dashboard. Start the listener, then go and register it.
Event routing is exclusive. Subscribing a URL to an event type on a domain moves that event off whatever endpoint had it, and there is no redelivery for anything that was in flight. Point this at a test domain.
Watching events arrive
By default each delivery is rendered for a human.--print picks how much:
--filter narrows to the event types you care about, and --max-field shortens anything wider than 512 characters so a long body does not swamp the terminal.
Forwarding into your application
--forward re-posts every accepted delivery to a local URL. Your real handler receives real events while the listener keeps printing them:
--force. A forward gets 10 seconds by default, adjustable with --forward-timeout.
Two flags change the semantics. --forward-sync waits for your handler before acknowledging the delivery to mailkube, which turns a handler failure into a retry. --forward-duplicates also forwards redeliveries, which is how you prove your deduplication on the event id works.
Recording a session
--record appends every accepted delivery to a .jsonl file, one JSON object per line, headers included:
Using a listener as a test
--exit-after stops once that many matching events have arrived, and --exit-timeout gives up with exit 124 if they have not. Together they turn a listener into an assertion:
Replaying synthetic events
Real sends cost quota and move your reputation.webhooks simulate posts a synthetic, correctly signed event to a local endpoint:
--event to see the list of types it can produce. --file sends a payload of your own, from @file or @-. A line out of a --record capture replays straight back through your handler. The target must be loopback unless you pass --force.
Pair it with --local, which runs a listener with no public URL at all, when you want to exercise a rendering or forwarding path without involving the platform.
Verifying a signature offline
webhooks verify performs the same computation a receiver does, against a payload you already have. Nothing is sent and nothing is contacted:
--body is never re-encoded, because a signature covers the exact bytes that were sent and reserializing the JSON would change them. The --id, --ts and --sig values are the X-Webhook-Id, X-Webhook-Ts and X-Webhook-Sig headers from the delivery.
--tolerance is the accepted age of the timestamp, 5 minutes by default. Pass --tolerance 0 to check the signature alone. That is the setting for a capture from yesterday, where the timestamp is legitimately old.
Secrets
Every one of these commands reads the endpoint signing secret from--secret or from MAILKUBE_WEBHOOK_SECRET. It is never stored in the config file: a signing secret is tied to a single endpoint, and the config file stores per-profile settings.
--skip-verify accepts unverified deliveries. It exists for development against a payload you constructed yourself, and using it against real traffic means accepting anything that reaches the port.
Inside a container, the default loopback bind is unreachable from a published port. Run
mailkube webhooks listen --host 0.0.0.0 with -p 4318:4318.Next steps
Webhook events
Every event type, its payload, and the signature scheme.
Scripting
Turning a recorded session into an assertion.