Events
What SolidHooks stores for every inbound request, the event statuses, and how to list, filter and replay events.
An event is a stored inbound webhook: the raw body, the request headers and the parsed payload. Events are immutable. Once written they do not change, so an event is the record of what arrived.
Event ids start with evt_. Each event belongs to the source that received it.
How it works
The pipeline is: provider, ingest URL, event stored, connections matched, deliveries created. The event is stored first. Whether it is then delivered depends on its status.
An event carries:
- the raw body exactly as received, plus the parsed payload
- the request headers and any query parameters on the ingest URL
- the resolved
eventType, read from the source'seventTypeFieldat ingest time - the
idempotencyKey, if the source computes one - a
statusand, for rejected events, arejectionReason verifiedWith, the verifier that ran at ingest, kept so editing the source later never relabels history
If a body is not valid UTF-8, isBinary is true, raw is a lossy rendering, and rawBase64 holds the exact bytes.
Statuses
| Status | Meaning |
|---|---|
received | Matched at least one connection and was dispatched. |
filtered | Connections exist, but their event-type filters excluded it. |
unmatched | The source has no active connections. |
rejected | Failed signature verification. Carries a rejectionReason and is never delivered. |
duplicate, pending | Legacy values. Ingest no longer writes either. |
Rejected events
A request that fails verification gets a deliberately opaque 401 with the same message whatever the cause, so the response cannot be used to probe signatures. The request is still stored as a rejected event, so you can see what arrived and why it was turned away.
rejectionReason is one of:
| Reason | Meaning |
|---|---|
signature_mismatch | The signature did not match. |
missing_signature_header | The signature header was absent. |
missing_id_header | Standard Webhooks only: the webhook-id header was absent. |
missing_timestamp_header | The timestamp header was absent. |
malformed_signature_header | The signature header could not be parsed. |
malformed_timestamp | The timestamp could not be parsed. |
timestamp_out_of_tolerance | The timestamp fell outside the replay window. |
verifier_misconfigured | The source's verifier is misconfigured. |
unsupported_verifier | The verifier type is not supported. |
Rejected events are never deduplicated, never delivered, and cannot be replayed.
Rejected events are a sample
Rejected events are rate-limited per source. Under a flood of bad requests the log is a representative sample, not a complete ledger of every 401.
Deduplication
When a source has idempotencyKeys, a repeat of an earlier request returns {"ok":true,"duplicate":true} instead of storing a second event. Duplicate requests are never counted against your quota. See Idempotency.
List and filter events
curl -s "$API/v1/events?status=rejected&limit=20" \
-H "authorization: Bearer $SDHK_KEY"| Parameter | Meaning |
|---|---|
sourceId | Only events from this source. |
status | One of pending, received, filtered, duplicate, rejected, unmatched. |
from, to | Events created on or after from and on or before to. |
limit | Page size, 1 to 100. Defaults to 20. |
cursor | Cursor from the previous page. |
search | Search term, up to 255 characters. |
sortBy, sortDir | Sort field, and asc or desc. |
Related endpoints:
GET /v1/events/{id}returns one event with its deliveries.GET /v1/events/{id}/timelinereturns the event's whole journey, ingest phases then each delivery's attempts. Start here when a webhook seems to have gone missing.GET /v1/events/has-anytells a new, empty project from an empty filter.
All of these need read access to events. Project-scoped endpoints take an optional X-Project-Id header.
Retention
Events are kept for your plan's retentionDays. Older events and their deliveries are purged nightly and can no longer be replayed.
Currently, we keep events for 3 days on Hobby, 7 on Launch, 30 on Scale and 90 on Enterprise. You can get your plan retention days by using GET /v1/billing/plan.
Replay
POST /v1/events/{id}/replay re-runs an event through its currently active connections. It creates new deliveries, which are billed. See Replay.
Live stream
GET /v1/events/stream tails new events as Server-Sent Events, so you can react without polling. See Streams.
Next steps
- Sources — where events come from
- Deliveries — what happens after an event is stored
- Idempotency — how duplicates are detected
- Replay — send an event again
- Streams — tail events live