Skip to content

SDK ingest — who Herald accepts events from

Herald rejects SDK events whose actor email isn’t on the product’s allowed-senders list. The list is on by default — when a product is created, every member of the owning organization is added automatically. Stranger events 422; your team’s events flow through.

Each ingest route reads one or more email fields off the payload and gates them against the product’s allowed-senders list before validation runs.

RouteField checked
POST /v1/ingest/feedbackpayload.author.email
POST /v1/ingest/feedback:batchpayload.items[*].author.email
POST /v1/ingest/conversationpayload.participants[*].email
POST /v1/ingest/sales-callpayload.participants[*].email
POST /v1/ingest/revenuepayload.customer.email

Within a batch, addresses are de-duplicated before checking — sending the same author 1,000 times costs one allowlist lookup, not 1,000.

A non-allowed actor returns a structured 422 with the offending address:

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{ "error": "sender_not_allowed", "address": "alice@bloomflow.example" }

The first miss in a batch is the deal-breaker — the rest of the items aren’t checked. Fix the address (or add it to the allowlist) and retry.

Open the dashboard → Settings → Allowed senders. Add either:

  • An addressalice@bloomflow.example. Exact match, lowercased.
  • A domainbloomflow.example. Covers every address at that domain.

Changes take effect immediately. The next event from that address goes through.

Under a region-level Cloudflare incident the gate can’t reach the product’s Durable Object. In that case the route returns 503:

HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{ "error": "service_unavailable" }

Retry with backoff. Herald never fails open — a 503 means the check didn’t run, not that the event was accepted. The check is per-product and runs on every event, so under sustained load you’ll see 503s on a small fraction of requests during an incident, not on all of them.

The same gate runs on the email forward path: anything sent to fwd+{product_id}@inbox.withherald.co from an address that isn’t on the list bounces with a Herald-voice reply and never touches R2. Default-deny is the load-bearing security guarantee that lets Herald promise one product → one Durable Object → no cross-tenant leakage. Allowing the SDK to push events for arbitrary addresses would make the email gate a Maginot Line.

If a customer needs Herald to track their own users’ events, the customer’s domain goes on the allowlist — not every random address that happens to ship in a payload.