Testing plans
Quotas apply per organization. Captured webhook events and received emails share the monthly event count. Testing plans are separate from monitoring plans.
- An organization with no testing subscription is on Free. Your monitoring plan does not change these numbers.
- Inboxes and email domains are counted across the whole organization, not per workspace. On Free, an email domain in one workspace blocks a second workspace from getting one. Disabled domains count.
- Monthly events count every captured webhook request and every received or injected email. At the cap, webhook senders and inject get
403and nothing is stored until the next month. Real mail sent over the cap can be accepted by the mail server and then dropped. - The monthly counter updates asynchronously. A burst that arrives at the cap can go slightly over it before refusals start.
- Retention ceiling is the highest
retentionDaysan inbox accepts, and the default. DevHelm does not currently delete captured data on a schedule. See Security and data.
Rate and size limits
Also per inbox:
maxEvents (1 to 100000, default 10000) caps stored events. When the inbox is full, the oldest event is dropped as a new one arrives.
How the limits apply
- Ingest checks the per-IP window first, then resolves the token, then checks the per-inbox and per-workspace windows and the monthly cap. Requests to unknown tokens count against the IP window.
- The wait limit counts calls, not time spent waiting. One 60-second wait costs the same as a 1-second one. Webhook and email waits share the 40 per minute across every workspace in the organization.
- Size limits refuse the whole request. Nothing is stored or clipped to fit.
- Email inject has no testing-specific rate limit. It counts against your organization’s general API rate limit and shares the size limits and the monthly cap.
Rate-limit responses
Every429 carries Retry-After in seconds and the standard error body:
Retry-After seconds, then call again. Retrying a wait with the same receivedAfter is safe: wait does not consume what it returns.
Error reference
Errors share one body shape. Branch oncode; message is for people.
Field validation errors (for example a mock reply status outside 200–599) return
400 with a field-specific code and an errors[] entry naming the field. The 403 quota codes carry the same errors[] entry.
408 that should have matched
A wait returns the oldest item with receivedAt at or after receivedAfter. The CLI and both SDKs default receivedAfter to the client clock when the call starts, so an item that arrived first is missed. Capture the time before you trigger the send and pass it:
408.
Errors in the SDKs and CLI
Every class extends
DevhelmApiError and carries status and code. A quota 403 is a DevhelmAuthError, so check code to tell it apart from a bad key. There is no timeout class: a 408 is a DevhelmApiError with code WAIT_TIMEOUT. Catch DevhelmRateLimitError before DevhelmApiError.
The CLI prints the error class, message, and code, and exits non-zero (11 for a wait timeout):
Next steps
Testing in CI
Fit parallel jobs into the inbox cap and the wait budget.
Security and data
Who can read captured data and how to clean it up.
Wait for a webhook
Wait semantics,
receivedAfter, and negative assertions.Wait for an email
One address per test and repeat sends.