Glossary & data model
The precise vocabulary of Emithook — the terms the API, CLI and MCP server all use. If you are an agent building context before driving Emithook, read this page first: every other surface assumes these definitions.
Availability legend
Emithook's v1 engine is feature-complete — the terms and endpoints below are shipped and callable. The few surfaces still on the roadmap are tagged so you don't call something that doesn't exist yet:
- Available Shipped — callable today via the API, CLI and MCP server.
- Roadmap Not yet shipped — Python/Go SDKs, the Terraform provider, a second AWS region, and optional runtime CF→SQS failover. See the roadmap.
Roadmap surfaces return not_implemented. Treat the badge as the source of truth for what you can call today.
The object hierarchy
Everything is scoped to an organization and resolves down to a single delivery attempt:
Organization ← billing, members, region, API keys
└─ Environment ← stage/region isolation (e.g. live, test)
├─ Endpoint ← an INBOUND entry you hand to a provider (relay)
│ └─ Route ← filter/transform + the destinations to fan out to
├─ Application ← a SEND customer's end-customer (multi-tenant fan-out)
│ └─ Endpoint ← that app's subscribed URL/queue + signing secret
├─ Destination ← a reusable delivery target (the central registry)
└─ Domain ← a custom hostname / email domain you've verified
↓ every event flows through ↓
Message / Event → Attempt(s) → delivered | retrying | dlqA subtlety worth internalizing: "Endpoint" means two different things depending on direction. On the relay side an Endpoint is inbound (a URL a provider posts to). In the Send application model an Endpoint is outbound (a subscriber URL belonging to an Application). They are distinct objects with distinct IDs; the docs always say "inbound endpoint" or "application endpoint" when context is ambiguous.
Core terms
Directions & products
Relay (receive) — the inbound product. A provider (Shopify, Stripe, Meta…) sends to a URL/queue/email address Emithook owns; Emithook acks fast, verifies, and routes to your systems.
Send (emit) Available — the outbound product (webhooks-as-a-service). You call the Send API with a payload and a destination and Emithook delivers it. Same delivery engine as relay; only the ingress differs.
Ingress (how an event enters)
Dedicated ingest domain — both webhooks (<ingest-domain>/<slug>) and email (<alias>@in.<ingest-domain>) live on a separate registrable domain from the marketing/console site, for a hard cookie boundary off the auth domain plus reputation/abuse isolation. <ingest-domain> is a placeholder: the real name is an operator choice and the edge reads it from config (it is domain-agnostic).
HTTPS edge Available — a provider POSTs to <ingest-domain>/<slug> (path-based) on the dedicated ingest domain. The immortal edge acks and durably buffers in <100 ms and never rejects on the accept path, so an accepted event is never dropped; signature verification runs later, in the processing plane, as a verdict (see Inbound verification below).
Queue ingestion Available — you produce messages to your own broker (SQS/SNS, Pub/Sub, Azure SB/Event Hubs, Kafka, AMQP, NATS, Redis Streams) and Emithook consumes them. Each message becomes an event. Removes per-HTTP-request cost for high-volume senders.
Inbound email / MX engine Available — mail to <alias>@in.<ingest-domain> is parsed (headers, bodies, attachments) into an event after SPF/DKIM/DMARC checks. Email becomes a webhook.
Send API Available — POST /v1/send. The fourth ingress: a direct API call.
Routing & delivery
Destination Available — where a delivered event goes: an HTTPS URL, a queue, or the Pull API. The unit of the central registry: defined once, referenced by id from many endpoints/routes. Editing it updates every reference. ID prefix dst_.
Central destination registry — the single source of truth for outbound targets. A registry entry is {id, name, adapter type, connection config, credential ref, delivery policy, signing, owner, version}. No destination is configured inline per route.
Destination validation — a destination is gated through three checks before it can receive traffic, and cannot activate until all pass: (1) schema — config matches the adapter's JSON schema; (2) credentials — the secret resolves and authenticates; (3) connectivity — a live test publish/reachability probe succeeds. Re-run hourly for drift; states valid | stale | invalid.
Route Available — attaches to an inbound endpoint: an optional filter (header/path/JSON-path match) and an optional sandboxed JS transformation, plus the list of destination ids to fan out to.
Fan-out — one inbound event delivered to N destinations, each signed, retried and logged independently.
Adapter — the pluggable driver for one broker/storage backend. A queue adapter implements a common interface (connect/auth, publish-with-confirm, backpressure, health probe) and declares capabilities (ordering, transactions, max message size, dedup). The same adapter serves both outbound destinations and inbound queue ingestion. All broker adapters are Available.
Managed identity grant — Emithook publishes to your queue using its own identity; you apply a copy-paste grant on your side (SQS queue policy, gcloud … add-iam-policy-binding, Kafka ACL, NATS authorization, etc.) rather than sharing long-lived keys. The connectivity check fails with a permission error until the grant is applied.
Delivery semantics
At-least-once — every acked event is delivered one or more times. Consumers must dedupe on webhook-id.
Retry schedule — exponential backoff with full jitter. Default: 8 attempts over ~28 h — immediate, 5s, 5m, 30m, 2h, 5h, 10h, 10h. Per-endpoint configurable (attempts + schedule). Success = a 2xx within a 15 s timeout.
DLQ (dead-letter queue) — where an event lands after the final failed attempt. DLQ events are visible as "failed — replayable" and can be redriven singly or in bulk.
Circuit breaker — per-destination. Opens after N consecutive hard failures (5xx/timeout), parks events instead of burning attempts, probes half-open, auto-closes; the owner is notified on open/close. A 410 Gone disables a destination immediately; Retry-After and 429/502/503/504 are honored.
Idempotency — on the Send API, an Idempotency-Key header (UUID, POST only) collapses duplicate sends within a ~12–24 h window to one delivery. End-to-end, webhook-id = the ingest event id and is stable across retries.
Claim-check — bodies over 128 KB are offloaded to object storage (S3/R2) and a pointer is enqueued; the router rehydrates the body. Hard cap 10 MB.
SSRF gate — every outbound HTTP delivery is guarded: HTTPS + standard ports only, encoded-IP literals and @ userinfo rejected, DNS resolved then the resolved IP validated against a private/metadata denylist (169.254.169.254, 127.0.0.0/8, 10/8, 172.16/12, 192.168/16, fc00::/7, …) and the connection pinned to that IP (defeats DNS-rebinding), redirects never followed (3xx = failure), internal headers stripped.
Signing & verification
Standard Webhooks — the on-the-wire spec Emithook signs with, verbatim. Receivers verify with any off-the-shelf library.
Signature headers — webhook-id, webhook-timestamp (Unix seconds), webhook-signature. The signed content is exactly {id}.{timestamp}.{body} (raw bytes sent, not re-serialized) → HMAC-SHA256 → base64 → v1,<sig>. Optional Ed25519 variant v1a.
Signing secret — per-destination, format whsec_<base64> (24–64 bytes). Rotation signs with current + previous keys (space-delimited in the header) for a 24 h overlap, so in-flight messages keep verifying.
Inbound verification (preset) — runs in the processing plane (not at the edge — the edge accepts and buffers first), per inbound endpoint: Shopify X-Shopify-Hmac-Sha256, Stripe Stripe-Signature (300 s tolerance), Slack v0= (5-min replay window), Meta X-Hub-Signature-256, generic HMAC → a verdict (mirroring the SES email model). A verified event is routed; a bad signature is quarantined (durable, inspectable, never delivered — not a 401, not silently dropped); an unknown endpoint is dropped (with a metric, no per-event row); an inactive/paused endpoint is parked (durable, replayable).
Provider preset — a bundle of {signature scheme + expected synchronous response (e.g. Meta hub.challenge echo, Slack url_verification) + sane retry defaults} selected when you create an inbound endpoint. ~6 at launch, expanding toward ~30.
Send model entities Available
Environment — region/stage isolation (e.g. live, test). The top container under an org.
Application — the Send customer's end-customer, addressable by the customer's own id via a uid (so they integrate statelessly). Holds endpoints, event-type subscriptions, and per-app signing. Can be auto-created on first send. ID prefix app_.
Application endpoint — a URL/queue belonging to an Application, with an event-type filter and its own signing secret.
Message — one event: an event-type, content, and an optional eventId. ID prefix msg_.
Attempt — one delivery try for a message to one destination: request, response, timing, attempt number, status.
EventType — a schema-bearing identifier (e.g. invoice.created) that powers the event catalog customers subscribe to.
Three delivery patterns — (1) static fan-out: one event → fixed destinations; (2) tenant-scoped routing: app_id = tenant id, POST /v1/app/{id}/event resolves that tenant's endpoints; (3) direct send: POST /v1/send names the destination explicitly.
Logs, archive & replay
Event / attempt log Available — every ingest record and every delivery attempt, queryable by tenant/endpoint/status/time/event-id. Bodies stored inline (MongoDB), default 30-day TTL, per-domain retention control (7/30/90 d, or "metadata only").
Hourly archive Available — a scheduled job rolls each endpoint's records for the clock hour into a gzipped NDJSON file (…/<tenant>/<endpoint>/YYYY/MM/DD/HH.jsonl.gz, .done marker per hour), keyed by attempt-time; join on eventId to reassemble an event and its attempts. Written before TTL prunes the hot store, so it is the durable backup of record. Default 13-month retention.
Replay Available — re-deliver an event: single, bulk-by-filter, or from the archive for events past the hot window. Replays are flagged (webhook-replayed: true) so consumers can distinguish them.
Pull API Available — for consumers behind a firewall who can't expose an HTTPS URL: poll GET /v1/pull/<endpoint>?cursor=…, cursor-based, batch up to 100, explicit ack advances the cursor. Availability window = the domain's retention setting.
Identity, access & tooling
Organization — the top-level tenant: members, roles, billing, region, API keys, all scoped data. A user can belong to multiple orgs with a different role in each. ID prefix org_.
Role — Admin, Developer, Viewer, per org.
API key — scoped (read, write, admin), format ek_live_… / ek_test_…. Every API/CLI/MCP call is gated by key scope. ID/prefix ek_.
Management API Available — the one REST/JSON surface (api.emithook.com) that everything else is a thin client over. The console, CLI and MCP server have no private backdoor.
CLI Available — @emithook/cli (emithook …): the full operational + query surface over the management API. See the CLI reference.
MCP server Available — exposes the management API as Model Context Protocol tools so agents can query and operate Emithook. Read tools are safe by default; write tools require a write-scoped key / explicit confirmation. Includes the email inbox tools. See the MCP reference.
Consumer portal Available — an embeddable, white-label view a Send customer embeds for their end-customers (magic-link/JWT scoped to one Application, no Emithook account): manage endpoints, view logs, replay, browse the event catalog, rotate the secret, read the email inbox.
Agent inbox Available — the LLM-readable side of the MX engine: give an agent its own address (agent-42@in.<ingest-domain>) and it reads/acts on its mail through MCP tools (list_emails, get_email, search_emails, summarize_thread).
ID prefixes
Resource ids are prefixed and (mostly) ULID-based, so they're time-sortable and self-describing:
| Prefix | Resource |
|---|---|
org_ | Organization |
ek_ | API key (ek_live_…, ek_test_…) |
ep_ | Inbound endpoint |
dst_ | Destination (registry entry) |
app_ | Application (Send model) |
msg_ | Message (a sent/relayed event) |
evt_ | Event (ingest record) |
whsec_ | Signing secret |
See also
- Core concepts — the same ideas in prose, with examples.
- API conventions — IDs, errors, pagination, scopes, idempotency.
- API reference · CLI · MCP server