Skip to main content
Create webhook testing inboxes, wait for the requests your code sends to them, and read or download what was captured. For how inboxes work, see Webhook inboxes.
An inbox receives requests so you can assert on them. devhelm webhooks manages the opposite direction: endpoints DevHelm sends events to. See webhooks.

Setup

The testing API needs an API key and a workspace ID. Export the key, look up the workspace, then export its ID:
The CLI and Python SDK read both variables. The TypeScript SDK reads DEVHELM_WORKSPACE_ID and takes the key in its constructor. With curl, send the workspace as a header:
--api-token and --api-url override DEVHELM_API_TOKEN and DEVHELM_API_URL per command. There is no workspace flag. Without DEVHELM_WORKSPACE_ID the CLI sends workspace 1 and fails with 404 Workspace doesn't exist. Every command takes -o table|json|yaml (default table) and -v. JSON and YAML print the bare object, without the API’s data envelope. Inbox and event IDs are UUIDs; the CLI rejects other values before calling the API.

Commands

inboxes list

List inboxes, newest first. The CLI prints the first page of 50. The table shows ID, NAME, STATUS, and URL.
CLI

inboxes get

CLI
The JSON includes httpUrl, publicToken, status, httpResponse (the mock reply), cors, retentionDays, maxEvents, and lastEventAt.

inboxes create

Create an inbox. Table output prints only the capture URL; use -o json to get the ID as well.
CLI
CLI
CLI
Reply header names must match [A-Za-z][A-Za-z0-9-]{0,63}. Credential and hop-by-hop names such as Set-Cookie, Authorization, and Content-Length are refused. X-Event-Id and X-Inbox-Id are always added. See Simulate a failing receiver. At the plan’s inbox cap, create returns 403 INBOUND_WEBHOOK_INBOX_LIMIT_REACHED. The cap counts every inbox in the organization.

inboxes update

CLI
update has no --response-status, --response-body, --response-content-type, or --response-delay-ms. Change those through the SDK or the API. A PATCH merges httpResponse field by field, except headers, which it replaces:
API

inboxes delete

Delete the inbox and every event on it. The capture URL returns 404 at once. A new inbox gets a new URL; tokens cannot be rotated.
CLI
Pass --yes (-y) in scripts and CI. Without it the CLI prompts, and a non-interactive run is refused.

inboxes activity

Request counts in 24 hourly UTC buckets. The table shows the total per inbox; JSON has the buckets.
CLI
Counts include requests that were later deleted, and the current partial hour.

inboxes wait

Block until a matching request has been captured, then print it. JSON and YAML include the full body.
CLI
Always pass --received-after with a time taken before you trigger the send. The default is the local clock when wait starts, so a request that arrived first is skipped and the wait times out.
  • Wait returns the oldest match. It does not consume it: the same call returns the same request again.
  • For the next request, pass the previous receivedAt plus 1 ms. Passing receivedAt unchanged returns the same request, because the match is inclusive.
  • With no match before the timeout, the CLI prints No matching inbound item arrived before timeout (inbox <id>) and exits with code 11. A disabled inbox times out the same way.
  • All waits in the organization share 40 requests per minute, across inboxes, email, and every workspace. Over the limit returns 429.
See Wait for a webhook in a test for test patterns and negative assertions.

inboxes events list

List captured requests, newest first. The table shows ID, METHOD, PATH, and RECEIVED.
CLI
The CLI prints the rows of one page and not the next cursor. Page through with the SDK or GET /api/v1/webhook/inboxes/<inbox-id>/events. Events are stored asynchronously, one to a few seconds after the sender gets its reply. A list right after a send can miss the newest requests; use wait instead.

inboxes events get

Get one request with its headers, query, and body.
CLI
  • Header names are stored lowercased. Values are stored as sent, including Authorization.
  • body is UTF-8 text clipped at 256 KB; bodyTruncated says whether it was cut. Binary bytes decode lossily.
  • sha256 is the hash of the exact body bytes.

inboxes events raw

Download the exact body bytes. The signed link lasts 60 seconds; the CLI downloads it at once. Compare the file with the event’s sha256, and verify signatures against these bytes, not against body:
CLI
See Verify webhook signatures.

inboxes events clear

Delete every captured request on the inbox. The inbox and its URL stay.
CLI
The CLI cannot delete a single event. Use event.delete() in the SDKs, or the API:
API

Common errors

Every code and limit is in Limits, quotas, and errors.

Next steps

Webhook inboxes

What an inbox captures and how ingest works.

Wait for a webhook in a test

Vitest and pytest patterns built on wait.

Testing in CI

One inbox per job, secrets, cleanup, and the wait budget.

email commands

The CLI for testing mailboxes.