Docs / Strand / connectors/kafka
Apache Kafka Connector
Produce messages to and consume messages from Apache Kafka topics in your Strand workflows.
Prerequisites#
You need Kafka cluster connection details to use this connector. You can connect to a managed Kafka service or a cluster you run yourself.
Your brokers must be reachable from the internet. Strand runs in the cloud
and can only connect to publicly reachable addresses — not 10.x,
172.16–31.x,192.168.xor127.x.
A cluster on an internal network needs a public listener or a tunnel; see
Connectors.
Note that the advertised listener matters as much as the bootstrap address: a
broker that advertises a private hostname will fail after the initial connect.
Managed Kafka (Confluent Cloud, AWS MSK, Aiven, etc.)#
- Sign in to your Kafka provider's dashboard
- Locate your cluster and find the connection details:
- Bootstrap Servers (e.g.,
pkc-abc123.us-east-1.aws.confluent.cloud:9092) - Security Protocol (typically
SASL_SSLfor managed services) - SASL Mechanism (typically
PLAINfor Confluent Cloud,SCRAM-SHA-256orSCRAM-SHA-512for AWS MSK) - SASL Username (API key or cluster username)
- SASL Password (API secret or cluster password)
Required Permissions#
If your Kafka cluster uses ACLs, the SASL user must have the following permissions:
| Operation | Required ACL Permissions |
|---|---|
| Produce | WRITE on topic, DESCRIBE on topic |
| Consume | READ on topic, READ on consumer group, DESCRIBE on topic |
Tip: In Confluent Cloud, configure ACLs under Cluster settings > ACLs. For self-hosted clusters, use kafka-acls.sh. Scope permissions to specific topic and consumer group names.
Self-Hosted Kafka#
- Note the Bootstrap Servers of your Kafka cluster (comma-separated list of
host:port) - Determine the security configuration:
- PLAINTEXT: No authentication or encryption
- SSL: Encryption only
- SASL_PLAINTEXT: Authentication without encryption
- SASL_SSL: Authentication with encryption (recommended)
Tip: Use a dedicated set of credentials for your Strand workflows. For managed services, create an API key scoped to only the topics your workflows need.
Connector Setup#
Create a Kafka connector from the Connectors page.
Configuration Fields#
| Field | Required | Description |
|---|---|---|
| Name | Yes | Friendly name (e.g., "Production Kafka") |
| Bootstrap Servers | Yes | Comma-separated list of broker addresses (e.g., broker1:9092,broker2:9092) |
| Security Protocol | No | Connection security: PLAINTEXT, SSL, SASL_PLAINTEXT, or SASL_SSL (default: PLAINTEXT; use SASL_SSL for managed clusters) |
| SASL Mechanism | No | Authentication mechanism: PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512 (required when using SASL) |
| SASL Username | No | SASL authentication username or API key (encrypted at rest) |
| SASL Password | No | SASL authentication password or API secret (encrypted at rest) |
| Default Topic | No | Default Kafka topic name (can be overridden at node level) |
| Consumer Group | No | Consumer group ID for consume operations (default: strand-consumer) |
| Timeout | No | Connection and operation timeout in seconds (default: 30) |
| Description | No | Optional description for reference |
Operations#
Produce#
Publish a message to a Kafka topic.
| Field | Required | Description |
|---|---|---|
| Topic | Yes | The Kafka topic to produce to (e.g., order-events) |
| Message | Yes | Message value (string or JSON) |
| Key | No | Message key for partitioning (messages with the same key go to the same partition) |
| Partition | No | Specific partition number to produce to (overrides key-based partitioning) |
| Headers (JSON) | No | JSON object of key-value pairs to include as message headers |
Example: Produce an order event:
- Topic:
order-events - Key:
{{ payload.customer_id }} - Message:
{
"event_type": "order_placed",
"order_id": "{{ payload.order_id }}",
"customer_id": "{{ payload.customer_id }}",
"items": {{ payload.items }},
"total": {{ payload.total }},
"timestamp": "{{ payload.timestamp }}"
}
- Headers:
{
"source": "strand-workflow",
"correlation-id": "{{ payload.trace_id }}"
}
Example: Produce a notification with a specific partition:
- Topic:
notifications - Partition:
0 - Message:
{"type": "alert", "severity": "{{ payload.severity }}", "message": "{{ payload.message }}"}
Consume#
Consume messages from a Kafka topic. Returns a batch of messages up to the specified limit.
| Field | Required | Description |
|---|---|---|
| Topic | Yes | The Kafka topic to consume from |
| Group ID | Yes | Consumer group ID (e.g., strand-workflow-consumers) |
| Timeout | No | Maximum time in seconds to wait for messages (default: 5) |
| Max Messages | No | Maximum number of messages to return (default: 10) |
A consumer with no committed offset always starts from the earliest available message; this is fixed and not configurable per node.
Example: Consume from an events topic:
- Topic:
order-events - Group ID:
strand-order-processor - Timeout:
5 - Max Messages:
20
Output#
Produce Output#
{
"success": true,
"status": "sent",
"data": {
"topic": "order-events",
"partition": 3,
"offset": 1542,
"key": "CUST-001",
"timestamp": "2026-02-18T14:30:00Z"
},
"service": "kafka",
"operation": "produce"
}
Key fields for subsequent nodes:
{{ steps.node_a1b2c3d4.output_payload.data.topic }}: The topic the message was produced to{{ steps.node_a1b2c3d4.output_payload.data.partition }}: The partition the message was written to{{ steps.node_a1b2c3d4.output_payload.data.offset }}: The offset of the produced message
Consume Output#
{
"success": true,
"status": "completed",
"data": {
"messages": [
{
"topic": "order-events",
"partition": 3,
"offset": 1540,
"key": "CUST-001",
"value": "{\"event_type\":\"order_placed\",\"order_id\":\"ORD-001\",\"total\":149.99}",
"timestamp": "2026-02-18T14:28:00Z",
"headers": {"source": "strand-workflow"}
},
{
"topic": "order-events",
"partition": 3,
"offset": 1541,
"key": "CUST-002",
"value": "{\"event_type\":\"order_placed\",\"order_id\":\"ORD-002\",\"total\":89.50}",
"timestamp": "2026-02-18T14:29:00Z",
"headers": {"source": "strand-workflow"}
}
],
"count": 2,
"group_id": "strand-order-processor"
},
"service": "kafka",
"operation": "consume"
}
Key fields for subsequent nodes:
{{ steps.node_a1b2c3d4.output_payload.data.messages }}: Array of consumed messages{{ steps.node_a1b2c3d4.output_payload.data.count }}: Number of messages consumed{{ steps.node_a1b2c3d4.output_payload.data.messages[0].value }}: Value of the first message
Errors#
| Error | Meaning |
|---|---|
| Kafka bootstrap_servers is required | Bootstrap servers not configured in connector. |
| Topic is required | No topic specified for the operation. |
| Message is required | No message provided for a Produce operation. |
| Group ID is required | No consumer group ID specified for a Consume operation. |
| SASL username and password are required | SASL authentication is configured but credentials are missing. |
| NoBrokersAvailable | Could not connect to any bootstrap server. Check the address and network access. |
| TopicAuthorizationFailedError | The credentials lack permission to access the specified topic. |
| UnknownTopicOrPartitionError | The specified topic does not exist on the cluster. |
| SaslAuthenticationException | SASL authentication failed. Check username, password, and mechanism. |
| GroupAuthorizationFailedError | The credentials lack permission to use the specified consumer group. |
| MessageSizeTooLargeError | The message exceeds the broker's maximum message size (default: 1 MB). |
| KafkaTimeoutError | The operation timed out. Increase the timeout or check broker availability. |
Example Workflow#
- Create Connector with your Kafka bootstrap servers and SASL credentials
- Produce an event when a workflow triggers:
- Topic:
workflow-events - Key:
{{ payload.workflow_id }} - Message:
{"event": "started", "workflow_id": "{{ payload.workflow_id }}", "input": {{ payload.input }}}
- Process the event in downstream workflow nodes
- Produce a completion event:
- Topic:
workflow-events - Key:
{{ payload.workflow_id }} - Message:
{"event": "completed", "workflow_id": "{{ payload.workflow_id }}", "result": "{{ payload.result }}"} - Headers:
{"correlation-id": "{{ payload.trace_id }}"}
Limitations#
- Long-running consumers: The Consume operation performs a one-time poll and returns. It does not maintain a persistent consumer connection. For continuous consumption, use a scheduled workflow or an external consumer that triggers Strand via webhook.
- Exactly-once semantics: Exactly-once delivery is not guaranteed. Messages are produced with at-least-once semantics. Design your consumers to handle duplicates.
- Schema Registry: Avro, Protobuf, and JSON Schema validation via Confluent Schema Registry is not currently supported. Messages are sent and received as plain strings or JSON.
- Message size: Individual messages are limited to the broker's
max.message.bytessetting (default: 1 MB). - Topic management: Creating, deleting, or configuring topics must be done through your Kafka provider or CLI tools.
- Offset management: Consumer offsets are committed automatically after consumption. Manual offset management is not supported.
- Multi-topic: Each operation targets a single topic. Use multiple workflow nodes to interact with different topics.
Tendrl