Skip to content

Send webhooks

Available — including the application/subscription model & the consumer portal.

The Send side: emit your own events as webhooks-as-a-service. You don't run delivery infrastructure — Emithook signs, retries, circuit-breaks, logs, and archives every send through the same engine the relay uses. Only the ingress differs.

Three ways to resolve a destination

All three share signing, retry/backoff, the per-destination circuit breaker, SSRF protection, DLQ, and logs. They differ only in how the target is chosen:

PatternUse it whenHow it resolves
Direct sendOne-off / system-to-system push, no event mapping.POST /v1/send names a registered destination id or a validated HTTPS URL.
Static fan-outAn event always goes to the same place(s).An event type → a fixed route set.
Per-tenant (applications)A multi-tenant product where each customer wants their own URL/queue for the same event.app_id = your end-customer's id; POST /v1/app/{id}/event resolves that tenant's endpoints.

Direct send

The simplest path — one call delivers one payload:

bash
curl -X POST https://api.emithook.com/v1/send \
  -H "Authorization: Bearer $EK_KEY" \
  -H "Idempotency-Key: inv_001" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": "dst_01JX9...",
    "event_type": "invoice.created",
    "payload": { "id": "INV-2026-001", "amount": 4999, "currency": "INR" }
  }'
json
// → 202 Accepted
{ "message_id": "msg_01JX9..." }

Send Idempotency-Key so client retries can't double-deliver. See the Quickstart for the end-to-end version.

Queue and broker destinations

A destination doesn't have to be an HTTPS URL — it can be a message broker. The same engine (signing context, retry/backoff, per-destination circuit breaker, DLQ, logs) applies; only the final hop changes: instead of an HTTP POST, the event is published to the broker. One adapter class per broker implements the same QueuePort (publish and consume), so a broker can be both a delivery target and an ingestion source.

Every adapter passes the same shared QueuePort conformance suite (publish/receive, ack removes, nack redelivers with an incrementing delivery count, FIFO within a group, consume() auto-ack, health):

typeBrokerFIFO within a groupDelivery countConformance
sqsAWS SQSFIFO queues (MessageGroupId)ApproximateReceiveCountCI (ElasticMQ)
natsNATS JetStreamstream orderJetStream redelivery countCI
redisRedis Streamsstream orderXCLAIM delivery countCI
amqpRabbitMQqueue orderquorum-queue x-delivery-countCI (RabbitMQ)
kafkaApache Kafkaper-key partition orderadapter-tracked per offsetCI (Redpanda)
pubsubGoogle Pub/Submessage ordering (orderingKey)deliveryAttempt (via DLQ policy)CI (emulator)
azureAzure Service Busqueue orderdeliveryCountconformance-ready — not CI-verified

Azure Service Bus

The Azure adapter is implemented against the same contract and ships with the identical conformance suite, but there is no lightweight Service Bus emulator that supports dynamic queue creation, so the suite is gated on AZURE_SERVICEBUS_TEST_URL and skipped in CI. Point that variable at a real namespace to run the full contract end to end.

Broker connection details live in the destination's stored configuration, not in environment variables — only the self-host ingest/delivery backbone (QueuePort) is env-configured. See self-hosting.

The application / subscription model Available

Turn Emithook into "webhooks for your product." Register each of your end-customers as an Application (keyed by your own id), each holding endpoints (URL/queue + event-type filter + its own signing secret). Then fan out:

bash
curl -X POST https://api.emithook.com/v1/app/cust_8f2a/event \
  -H "Authorization: Bearer $EK_KEY" \
  -d '{ "event_type": "invoice.created", "payload": { "id": "INV-1" } }'
json
// → 202 Accepted — fanned out to every matching endpoint
{ "message_id": "msg_01JX9...", "fanout": 3 }

Applications can be lazily auto-created on first send. The data model (Environment → Application → Endpoint, plus Message/Attempt/EventType) is defined in the glossary.

Consumer portal Available

Your end-customers manage their own endpoints, view delivery logs, replay failures, browse the event catalog, and rotate their secret — in an embeddable, white-label portal scoped to one application via a magic link, with no Emithook account. This is what offloads webhook support from your team to theirs.

Delivery envelope

Every delivery — to any destination (HTTPS, SQS, or another queue) — carries the original request, not just its body, so a consumer of a shared destination can tell who sent an event and why:

json
{
  "url": "https://wh.emithook.com/acme-orders",
  "method": "POST",
  "headers": { "content-type": "application/json", "stripe-signature": "t=…,v1=…" },
  "data": "{\"id\":\"evt_123\",\"amount\":4999}",
  "received_at": "2026-07-21T00:25:52.943Z"
}
  • data is the original body as a verbatim stringJSON.parse(data) to get the object. Keeping it exact means you can still verify the source's own signature (forwarded in headers) against it.
  • headers are the inbound headers, minus secrets we won't relay (authorization, cookie, set-cookie, proxy-authorization).
  • url is the full inbound URL — the key to routing on the consumer side when many webhooks fan into one destination.

url, headers, and data are values the source supplied — treat them as untrusted input, exactly as you would the raw body.

When a transform runs, the delivered payload is your transform's output with the original request attached under an original block (same five fields). original is a reserved key: if your transform output already has an original field, the platform's block replaces it.

Receiving side (your customers)

Every delivery is signed with Standard Webhooks, so your customers verify with any off-the-shelf library — against the exact delivered bytes as received (the envelope above), not a re-parsed copy. See signing.

Send transactional email

The Send side isn't only webhooks. The same accept → queue → deliver → retry → DLQ → replay engine sends transactional email through Amazon SES, with delivery-truth analytics (sent → delivered, bounce/complaint tracking, an org suppression list, per-message timelines). See Send email.

Next

Emithook · a Finnoto product