Apache Kafka

Datafly Signal publishes first-party events to Apache Kafka topics for real-time stream processing, event sourcing, and integration with downstream consumers and microservices.

Why route Apache Kafka through Datafly Signal

  • The data lands in infrastructure you own. Signal writes to your Apache Kafka directly from your own deployment. Datafly operates no intermediary and never holds a copy of your event data.
  • Client IP and user agent are recorded as event context. Both come from the original visitor request rather than from your server, so geography and device analysis done in the warehouse reflects the actual visitor.
  • Presets for eight verticals. General (the default: page, identify and the key cross-vertical events), Retail, Insurance, Travel, Media & Streaming, Publishing, B2B / SaaS and Automotive, each writing one named field per documented event property. Email, phone and names are written only as SHA-256 digests unless you deliberately change the installed blueprint.
  • One consent decision governs every destination. Apache Kafka is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.

Prerequisites

Before configuring Apache Kafka in Signal, you need a Kafka cluster with a topic and optional SASL credentials for authentication.

Set Up a Kafka Cluster

You have several options:

Option A: Self-Hosted Kafka

  1. Install Apache Kafka using the official quickstart.
  2. Start ZooKeeper (or use KRaft mode for ZooKeeper-less setups).
  3. Start one or more Kafka brokers.
  4. Note the Bootstrap servers addresses (e.g. broker1:9092,broker2:9092).

Option B: Managed Kafka

Use a managed Kafka service such as Amazon MSK, Aiven, Redpanda, or Strimzi on Kubernetes. Follow the provider’s setup guide and note the bootstrap servers.

Create a Topic

Create the topic that Signal will produce to:

kafka-topics.sh --create \
  --topic datafly-events \
  --partitions 6 \
  --replication-factor 3 \
  --bootstrap-server broker1:9092

Or if using a managed service, create the topic through the provider’s console.

Choose the number of partitions based on expected throughput and consumer parallelism. 6 partitions is a good starting point for moderate workloads. Increasing partitions later requires careful planning.

Configure SASL Authentication (If Required)

If your Kafka cluster requires authentication:

  1. Create SASL credentials for Signal. The exact process depends on your Kafka deployment:
    • SASL/PLAIN: Create a username and password in the JAAS configuration.
    • SASL/SCRAM: Use kafka-configs.sh to create SCRAM credentials.
    • SASL/OAUTHBEARER: Configure an OAuth provider and obtain client credentials.
  2. Note the Security protocol (SASL_SSL for encrypted + authenticated connections, SASL_PLAINTEXT for authenticated without encryption).
  3. Note the SASL mechanism (e.g. PLAIN, SCRAM-SHA-256, SCRAM-SHA-512).

Example SCRAM credential creation:

kafka-configs.sh --alter \
  --add-config 'SCRAM-SHA-256=[password=your_password]' \
  --entity-type users \
  --entity-name datafly-signal \
  --bootstrap-server broker1:9092
⚠️

Always use SASL_SSL in production to encrypt data in transit. SASL_PLAINTEXT transmits credentials in cleartext and should only be used in development environments.

Verify Network Access

Ensure your Signal deployment can reach the Kafka bootstrap servers on the configured port (default 9092 for plaintext, 9093 for SSL). Check firewall rules, security groups, and DNS resolution.

Configuration

FieldTypeRequiredDescription
bootstrap_serversstringYesComma-separated broker addresses in host:port form. Also accepts brokers.
topicstringYesThe topic to produce to. It must already exist: Signal does not create topics.
security_protocolselectYesPLAINTEXT, SSL, SASL_PLAINTEXT or SASL_SSL. SSL and SASL_SSL turn on TLS 1.2+.
sasl_mechanismselectNoPLAIN, SCRAM-SHA-256 or SCRAM-SHA-512. Leave empty for no SASL. With SASL_PLAINTEXT or SASL_SSL, choose one: an empty mechanism connects without authentication.
sasl_usernamestringNoRequired when a SASL mechanism is set.
sasl_passwordsecretNoRequired when a SASL mechanism is set.

Signal Setup

Quick Setup

  1. Navigate to Integrations in the sidebar.
  2. Open the Integration Library tab.
  3. Find Apache Kafka or filter by Streaming.
  4. Click Install, select a variant if available, and fill in the required fields.
  5. Click Install Integration to create the integration with a ready-to-use default blueprint.

API Setup

curl -X POST http://localhost:8084/v1/admin/integration-catalog/kafka/install \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Apache Kafka",
    "variant": "default",
    "config": {
      "bootstrap_servers": "broker1:9092,broker2:9092",
      "topic": "datafly-events",
      "security_protocol": "SASL_SSL",
      "sasl_mechanism": "SCRAM-SHA-256",
      "sasl_username": "datafly-signal",
      "sasl_password": "your_password"
    },
    "delivery_mode": "server_side"
  }'

What Signal writes

Each event is one message whose value is the Signal event as JSON:

FieldContents
event, typeEvent name (purchase, page, …) and type (track, page, identify, …).
anonymous_id, user_id, device_idVisitor identifiers (user_id and device_id only when set).
message_id, event_idSignal’s message ID (use it to deduplicate) and the client event ID.
timestamp, sent_at, received_atRFC 3339 UTC times: event, client send, Signal receipt.
org_id, pipeline_id, vendor_typeWhere the event came from and which destination type wrote it.
contextThe collected context as sent: ip, user_agent, locale, page, campaign, consent, geo, device, library, and any partner attributes. Written in full; the blueprint does not filter it (#1414).
click_ids, collected_idsAd click IDs and ID-provider values, when present.
propertiesOnly the fields the blueprint maps (below).

Each event is one Kafka message:

  • Key: user_id, or anonymous_id when there is no user ID. The writer’s hash balancer maps a key to one partition, so one visitor’s events stay in order on one partition (an anonymous visitor’s events move to the user ID’s partition once they log in).
  • Headers: datafly_message_id, datafly_event_type, datafly_event_name.
  • Timestamp: the event time.
  • Producer settings: acks=all, Snappy compression, batches of up to 100 messages or 50 ms per partition.
  • Delivery: at least once. A retry after a timeout can write a message twice; deduplicate on message_id.

Only what the blueprint maps is written. The properties object holds the fields your installed blueprint maps and nothing else (the row contract): the global block under @root (copies of the envelope fields, UTM parameters, the analytics and marketing consent flags, and email_sha256, phone_sha256, first_name_sha256, last_name_sha256, name_sha256), and the event section’s fields at the top level (transaction_id, value, currency, items, …). Identify traits and client properties the blueprint does not map are never written. An event with no section in the blueprint still arrives, with the @root block only: add a section for any other event whose properties you need.

A purchase from an identified visitor, as written by the default (General) preset. The raw email, phone and name in the visitor’s identify traits are not in it; only their digests are. Empty context objects are trimmed here for brevity.

{
  "anonymous_id": "anon-s1-7f3a",
  "user_id": "crm-4711",
  "event": "purchase",
  "type": "track",
  "properties": {
    "@root": {
      "anonymous_id": "anon-s1-7f3a",
      "email_sha256": "86e0b9e56c17cc4d12387e1949b85053fbe73bc3ce5a1188713a9d300cc6133d",
      "event_id": "evt-s1-2",
      "event_name": "purchase",
      "event_timestamp": "2026-09-23T09:30:02.000Z",
      "event_type": "track",
      "first_name_sha256": "81f8f6dde88365f3928796ec7aa53f72820b06db8664f5fe76a7eb13e24546a2",
      "ip_address": "203.0.113.9",
      "last_name_sha256": "4f887a0887ca68593c8acd4e83e0b6bc5f42cd5aff7d3a159041c84230ed1cf6",
      "locale": "en-GB",
      "message_id": "msg-s1-kafka-2",
      "name_sha256": "ed37d99b1445238af3386f81a77a2caf3fffc0cd610be39b4f3ba53943dc66bf",
      "page_url": "https://shop.example/checkout",
      "phone_sha256": "a8acc3a90a7b4e4dc65e93db9240ed26523050ef754d63b75b5161de76781436",
      "user_agent": "Mozilla/5.0 (Macintosh)",
      "user_id": "crm-4711",
      "utm_medium": "cpc",
      "utm_source": "google"
    },
    "coupon": "AUTUMN10",
    "currency": "GBP",
    "items": [{ "item_id": "SKU-1", "item_name": "Trail shoe", "price": 80, "quantity": 1 }],
    "shipping": 4.5,
    "tax": 14.08,
    "transaction_id": "SIG-ORDER-1001",
    "value": 84.5
  },
  "context": {
    "ip": "203.0.113.9",
    "user_agent": "Mozilla/5.0 (Macintosh)",
    "locale": "en-GB",
    "page": { "url": "https://shop.example/checkout", "title": "Checkout", "path": "/checkout" },
    "campaign": { "source": "google", "medium": "cpc" }
  },
  "timestamp": "2026-09-23T09:30:02Z",
  "sent_at": "2026-09-23T09:30:02Z",
  "message_id": "msg-s1-kafka-2",
  "org_id": "org-s1",
  "pipeline_id": "pl-s1",
  "received_at": "2026-09-23T09:30:02Z",
  "event_id": "evt-s1-2",
  "vendor_type": "kafka"
}

The message layout on this page was produced by Signal’s Kafka writer against an Apache Kafka 3.9 broker on 2026-09-23 (batch S1 review), not written from memory.

Stored event context

Alongside the fields your blueprint maps (where email, phone, names and address are stored as normalised SHA-256 by default), each message carries the event’s collected context exactly as Signal received it. The blueprint does not filter or hash the context: this is deliberate (decision 2026-09-23), so the context is complete for attribution, fraud and debugging queries, and it is listed here so you can decide what to keep.

StoredWhat it contains
Visitor requestIP address, user agent, Accept-Language, locale and timezone
PageURL including its query string, path, title and referrer
CampaignUTM source, medium, campaign, term and content
ConsentThe visitor’s consent state
LocationGeoIP country, region and city, and the network (ASN)
DeviceScreen size; for app events the OS and version, manufacturer, model, advertising ID and IDFV when the app collected them
Vendor IDsCookie and click identifiers in vendor_ids (for example the _fbp browser ID, gclid, ttclid, the GA client ID)
Session and librarySession ID, number and engagement time; the collector’s name and version
Partner attributesProfile attributes from partner enrichment (partner_attrs)

Each message is the event JSON: context, click_ids and collected_ids, and the anonymous_id, user_id, device_id, message_id and event_id identifiers, sit beside the mapped properties.

The IP address, device and advertising IDs, and cookie and click identifiers are personal data under UK and EU GDPR. To keep any of them out of storage, strip, mask or hash the field for every integration with the organisation data layer, apply URL redaction to page URLs, or remove it in the installed blueprint; and set the storage’s retention to match your privacy notice. See Personal data by integration.

The integration’s consent category in Signal decides which events reach Apache Kafka; events that fail it are never written. Every message also carries the visitor’s consent state (context.consent, and consent_analytics / consent_marketing in @root) so downstream consumers can apply their own rules.

Testing

  1. Enable the integration in Signal and trigger a test event on your website.
  2. Consume messages from the topic to verify:
kafka-console-consumer.sh \
  --topic datafly-events \
  --from-beginning \
  --max-messages 10 \
  --bootstrap-server broker1:9092
  1. Inspect the message values to verify the event data.
  2. In Signal, check the Live Events view to confirm delivery status shows as successful.

Troubleshooting

ProblemSolution
Events not appearing in the topicVerify the bootstrap servers and topic name are correct.
Connection timeoutEnsure Signal can reach the Kafka brokers. Check DNS, firewalls, and security groups. Verify the port matches the security protocol.
SASL authentication failedThe SASL username or password is incorrect. Verify the credentials and SASL mechanism.
TopicAuthorizationExceptionThe SASL user lacks produce permission on the topic. Configure ACLs with kafka-acls.sh.
UnknownTopicOrPartitionExceptionThe topic does not exist. Signal does not create topics: create it (for example kafka-topics.sh --create) with the partition count you need.
MessageSizeTooLargeThe event payload exceeds message.max.bytes on the broker or topic. Increase the limit or reduce payload size.
SSL handshake failureIf using SSL or SASL_SSL, ensure the broker’s SSL certificate is trusted. Check the CA certificate configuration.
Wrong security protocolThe security protocol must match the broker’s listener configuration. PLAINTEXT for port 9092, SSL for 9093 is a common pattern but varies by deployment.

Visit Apache Kafka documentation for full producer configuration reference, ACL setup, and security configuration.

See also