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: SASL_SSL) |
| 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) |
| 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