Configure event sinks: Kafka or HTTP batch
An event sink streams the entire platform event taxonomy — the samemessage.*, call.*, … events your webhooks receive — to one destination you run: a Kafka topic, or an HTTP collector that receives batched JSON arrays. One sink replaces a fleet of single-event webhook endpoints. This guide walks through choosing a transport, configuring it in the dashboard or through the API, and operating it in production.
What event sinks are vs webhooks
Both surfaces are push-based: Orbit delivers events to you rather than you polling for them. The difference is the destination shape:- Webhooks — per-endpoint HTTPS POSTs, one event per request, with an
X-Orbit-SignatureHMAC over each body. You register endpoints on an event list and each becomes a steeply-secured receiver. - Event sinks — a single declared destination per transport (
kafkaorhttp_batch); a subscriber filter such as["message.*"]decides which of the platform’s event taxonomy lands there. The Kafka transport is not an HTTP receiver at all, and the HTTP-batch transport gets a JSON array of events in one POST.
Choose destination — HTTP batch or Kafka
- HTTP batch fits a receiver you can stand up behind HTTPS: a Lambda-flavored endpoint, a log-collecting pipeline, an internal orchestration layer that accepts arrays. Tuning knob is
max_batch_size(1–1000 events per POST body, default 100). - Kafka fits a data plane that already runs brokers: you give Orbit the bootstrap brokers, a topic, and an optional SASL credential; Orbit produces one record per event keyed by your partition field so per-contact ordering holds.
Configure HTTP batch
Configure it in the dashboard under Developer → Event Sinks → HTTP batch, or throughPATCH /api/v1/developer/event-sinks/http_batch:
url must be https and resolve to a public address — private and metadata hosts are rejected, which is also why a PATCH that fails your organization’s ingress proxy can’t be smuggled in. The credentials value becomes the Authorization: Bearer header on every batch POST; it is encrypted at rest and never returned (credentials_configured: true is your proof it stored). Retry behaviour is covered below.
Node.js consumer for HTTP batch (payload-rich envelopes)
id / type / created_at / data envelope the webhook page documents. Ack the whole batch with 2xx — a non-2xx makes the sink retry the batch with exponential backoff, which is why you dedupe on event.id at the receiver.
Configure Kafka
In the dashboard under Developer → Event Sinks → Kafka, or throughPATCH /api/v1/developer/event-sinks/kafka:
brokers— 1–16 bootstrap brokers ashost:port, no scheme, no path.destination.topic— the Kafka topic to produce to; 1–249 chars,[A-Za-z0-9._-], and never.or...destination.partition_key_path— optional. A dot-path into the event envelope used to derive the record’s partition key. Default chain:data.contact_id→data.conversation_id→data.message_id→data.call_id→data.to→ eventid. Co-locating one contact’s events on one partition is the ordering guarantee downstream consumers can rely on.sasl_mechanism+sasl_username+credentials— the SASL handshake. Mechanisms:plain,scram-sha-256,scram-sha-512. The password is the encrypted credential; the username is stored plaintext alongside the brokers. If your brokers are not SASL-protected, omit all three and the sink produces without authentication.
destination.kind must equal the path segment — PATCH /…/kafka with destination.kind: "http_batch" is a 400.
Python consumer (confluent-kafka)
x-orbit-event-type header duplicates the envelope type, so a consumer that branches only on headers can skip parsing the value when you want to filter cheaply.
Delivery semantics — batched, ordered, at-least-once
A sink delivery is at-least-once, same as a webhook:- Batched.
http_batchgroups up tomax_batch_sizeenvelopes per POST; Kafka produces one record per event butlinger-batches them inside the producer. Either way, retries that haven’t yet exhausted their backoff can result in the same event appearing twice — dedupe on the envelopeid. - Partition-ordered. Kafka guarantees ordering only within a partition; that’s why the partition key exists (a single contact’s events are co-located). Ordering across two different contacts is not defined.
http_batchdelivers in flush order per tenant but a retry can interleave — rely oncreated_atfor entity state, not receive order. - Retries. A failed attempt (non-2xx on the HTTP POST, or a producer error on the Kafka send) is retried with exponential backoff, bounded at ~5s maximum delay, 3 attempts per record/batch. Each retry of the same event carries the identical serialized value — the bytes don’t change mid-flight.
Inspect deliveries + DLQ mechanics
Both the dashboard page and aGET expose the sink’s worker-owned run-status fields:
last_run_status as the health signal; consecutive_failures is a leading indicator even before you’d triage last_error. In the dashboard the same fields render as the status chip and health line on Developer → Event Sinks. When retries exhaust on a record or batch, the event is filed to a per-sink dead-letter list and the last_run_status flips to failed — the sink keeps consuming new events, it just stops retrying the poisoned one. You can run your own error-handling on your side based on the delivery outcome (your consumer logs + the sink’s run status are the two surfaces).
Event sink vs webhook — comparison
Pick the one that matches your consumer’s natural shape. A customer receiving webhooks today can keep them while onboarding a Kafka sink in parallel — the event taxonomy, envelope shape, and filter vocabulary are identical.
Production checklist
- Collector
url(http_batch) orbrokers+destination.topic(kafka) set and enabled. -
credentials_configured: trueon a read — the secret round-tripped. -
eventsfilter set to the tightest prefix you actually consume (message.*, not*, unless you genuinely handle all). - Receiver dedupes on the envelope
id— at-least-once re-delivers under retry. - A nightly job reads the sink (
GET /api/v1/developer/event-sinks/{kind}) and alerts onlast_run_status: "failed"or a growingconsecutive_failures. - SASL credentials live in a secrets manager, not in source control.
Next steps
- Event Sinks API — full request/response field reference, credential handling
- Webhook fan-out — how one event fans out to webhooks, the events buffer, and sinks
- Webhooks overview — the per-event taxonomy this sink consumes
- Build a durable webhook consumer — the webhook-side counterpart