Skip Navigation

Topics & Messages

Producing Messages

Send one message or thousands: partitions, serializers and Schema Registry subjects, nulls and headers, delays, ${…} variables for unique values, templates, and re-producing a message you found.

The Produce Message(s) dialog writes messages to a topic: one message to test a consumer, a copy of one you found while searching, or thousands of varied test messages with counters, random values and dates filled in for each one. You choose the partition, how the key and value are turned into bytes (plain text, numbers, hex or a Schema Registry schema), and any headers, and you can save what you typed as a template for next time.

Warning: producing writes real data that every consumer of the topic will read, and it can't be taken back. Check the cluster's colour and name (on its tab and in the status bar) before you send, especially to a production cluster.

Opening the Produce dialog

Select a topic in the topics list, then either click 1 Produce Message(s) in the topic toolbar, or right-click the topic and choose Produce Message(s). To send a copy of an existing message, right-click it in the message table and choose Re-produce Message (Re-producing an existing message).

The Topics section of the Storefront Dev cluster in a duplicated tab, Storefront Dev (Topics), with the orders topic selected. The topic toolbar at the top right holds, from the left, the Produce Message(s) and Clear Messages buttons, then Edit Settings, Re-create Topic, Delete Topic and Create Topic. The orders topic is highlighted in the topics list on the right, with 60 messages. 1
The topic toolbar, with orders selected in the topics list.
  1. Produce Message(s): opens the dialog for the selected topic.

The dialog doesn't block the rest of KafkIO, and it stays open after each send, so you can change the message and send again, or search the topic to see what arrived. You can open several at once, for the same topic or different ones, on one cluster or several: each dialog keeps the topic it was opened for (shown at its top), whatever you select afterwards. Together with duplicated cluster tabs, that lets you feed one topic while you watch another, for example a live stream of the topic your consumer writes its results to.

The dialog at a glance

The Produce Message(s) dialog for the orders topic. Partition is Auto, and the Key serializer and Value serializer are both String (raw). Below are the tabs Key, Message and Headers; the Message tab is open, with a Send null message check box, a search box, and a JSON order whose orderId is ${counter}, customer is customer-${random.int:1:20}, amount is ${random.int:5:500}, status is NEW and placedAt is ${current.datetime:yyyy-MM-dd'T'HH:mm:ss}. Under the editor is the Template row (Select template..., Load, Delete, Save as template). Then Last message sent at 25 Oct 2026, 08:35:37, with Partition 1, Offset 29, Serialized key size 11, Serialized message size 121 and Timestamp 1792917337064. At the bottom, '${variable}' substitution is ticked next to an Examples link, How many (max 500k) is 10, Delay between messages is 100 (0.10s), with quick links 0ms, 100ms, 1s, 5s, 20s and 60s, and the button reads Produce 10 Messages (in 1.00s). 1 2 3 4 5 6 7 8 9 10 11
Ten varied orders, each with its own number, customer, amount and time, just sent to orders.
  1. Partition: Auto, or a partition number (Choosing a partition).
  2. Key serializer: how the key becomes bytes.
  3. Value serializer: how the message (the value) becomes bytes (Key and value serializers).
  4. The Key, Message and Headers tabs, where you type what to send (Key, value and headers).
  5. Send null message (and Send null key on the Key tab) (Sending true nulls).
  6. Template: load, delete or save templates (Message templates).
  7. Where the last message went (Results and errors). Shown after the first send.
  8. '${variable}' substitution, on by default, and its Examples (Variable substitution).
  9. How many (max 500k): how many messages to send.
  10. Delay between messages, in milliseconds (Producing many messages).
  11. The send button. Its label says how many messages it will send and, with a delay, how long that takes.

Partition and serializers

Choosing a partition

Leave Partition on Auto to let Kafka's producer decide, as your applications' producers usually do: a message with a key goes to the partition its key hashes to, so messages with the same key always land in the same partition, in order. Messages without a key (a null key) are spread across partitions by the producer, a batch at a time, so a quick run of keyless messages often lands in a single partition.

Pick a partition number to send everything there, whatever the key: useful to test how a consumer handles one particular partition, or to put a message next to the ones it relates to. Note that a key normally lands in one partition only, so writing it elsewhere by hand can break the ordering your consumers rely on.

Key and value serializers

A Kafka message is only bytes. The serializers decide how the text you type becomes those bytes, separately for the key and the value. Choose the format your consumers expect: the same one you'd pick as the deserializer to read the topic.

SerializerType thisSends
String (raw)Any text, including JSON or XML The text as UTF-8, exactly as typed. The default for keys, and for values without a schema.
Int (32)A whole number, e.g. 42 or -7 4 bytes, big-endian (what Kafka's IntegerSerializer writes).
Int (64)A whole number 8 bytes, big-endian (Kafka's LongSerializer).
Unsigned Int (32)A number from 0 to 4294967295 4 bytes.
Unsigned Int (64)A number from 0 to 18446744073709551615 8 bytes.
HexHexadecimal, e.g. CA FE 00 01, ca:fe:00:01 or #CAFE0001 Those bytes. Spaces, colons and # are ignored, so you can paste a single-line hex dump (line breaks are not accepted). Use it for binary data you can't type as text.
MessagePackJSON The same data encoded as MessagePack.
Schema RegistryJSON that fits the subject's schema Avro, Protobuf or JSON Schema data in the Confluent wire format (schema ID first), using the subject you pick (below).

The number serializers need a plain number with no spaces or separators; anything else, or a number out of range, fails when you send (Results and errors). Protobuf (raw) and __consumer_offsets, which you may see when reading messages, can only read, so they aren't offered here. Exactly what each serializer accepts is listed in the Serializers & Deserializers reference.

When the dialog opens, both serializers start on String (raw), except that the value starts on Schema Registry when the cluster has a Schema Registry connection and the topic has a schema subject of its own (such as orders-avro-value). Schema Registry appears in the list only when there's at least one subject it could use.

Using a Schema Registry subject

When you choose Schema Registry, a Subject box appears next to it: the subject whose latest schema version is used to encode what you type. Pick one before sending (the send button stays disabled until you do). Type in the box to filter long lists.

The Produce Message(s) dialog for the orders-avro topic, opened by re-producing a message: Partition 1, Key serializer String (raw), Value serializer Schema Registry. The Subject drop-down is open with a Search box and two subjects: orders-avro-value, labelled topic subject and highlighted, and com.storefront.OrderPlaced, labelled record subject. The Message tab below holds the order as JSON: orderId 28, customer customer-4, amount 113.13, status PAID. 1 2
Picking the subject to encode an orders-avro value with.
  1. Schema Registry chosen as the value serializer.
  2. Subject, open: each subject is labelled with how it relates to this topic.

Subjects are listed in this order, so the likeliest ones come first:

  • topic subject: this topic's own subjects, orders-avro-value (or -key for the key) and any orders-avro-RecordName subjects. The first of these is preselected.
  • record subject: subjects named after a record, such as com.storefront.OrderPlaced, which any topic can use.
  • other topic's subject: another topic's topic-RecordName subjects.
  • other subject: anything else that might fit.

Other topics' plain -key and -value subjects aren't offered. How KafkIO tells these naming strategies apart is explained under Subject naming strategies.

Type the message as JSON, whatever the schema type: KafkIO checks it against the schema and encodes it as Avro, Protobuf or JSON Schema data. For Avro, write a field whose type is a union other than plain null with the type as a key, for example "note": {"string": "gift wrap"} (and "note": null for null), which is how KafkIO displays such fields when reading. For Protobuf, when the subject is named after a message type (a record or topic-record subject), that type is used; otherwise, the schema's first message type. KafkIO never registers schemas when producing: register or change them in the Schema Registry section.

Key, value and headers

Type or paste the key on the Key tab and the value on the Message tab (the one shown when the dialog opens). Both are full editors with line numbers, undo and redo, and a search box (Search key..., Search message...) that works like the one for searching within a message. The text is sent exactly as typed, including line breaks and indentation: KafkIO doesn't reformat JSON.

Leave the key empty if your topic doesn't use keys, but note that an empty key isn't the same as no key: see below.

Sending true nulls

An empty key or value is sent as zero bytes, which is a value. Send null key (on the Key tab) and Send null message (on the Message tab) send a real null instead, and grey out the editor. The difference matters to consumers and to Kafka itself:

  • A message with a null value is a tombstone. On a compacted topic it tells Kafka to delete every earlier message with that key: send one with the key filled in to remove that key from the topic.
  • A null key leaves the partition to the producer (see Choosing a partition), while an empty key always hashes to the same partition.
  • Consumers often test for null, not for empty, so it's worth checking they cope with both.

After sending, Serialized key size (or message size) in the result shows -1 for a null and 0 for an empty one.

Headers

Headers carry metadata next to the message: a trace ID, the event type, the producing service. On the Headers tab, click 1 Add header for each one, and fill in its Header key and Header value. Header values are sent as UTF-8 text, and can use variables too.

The Headers tab of the Produce Message(s) dialog, with columns Header key and Header value and three rows: source with the value kafkio-docs, trace-id with the value ${random.uuid}, and correlation-id with its value box greyed out and Null value ticked. Each row has a Null value check box and a remove link; below the rows is an Add header link. 1 2 3
Three headers: plain text, a fresh UUID for each message, and a null.
  1. Add header: adds an empty row.
  2. Null value: sends the header with a null value rather than an empty one.
  3. remove: deletes that row.

Each header key is sent once: if you add the same key twice, only one of the rows is sent (Kafka allows repeated header keys, but this dialog doesn't produce them).

Producing many messages

Set How many (max 500k) to send the same message up to 500,000 times in one go, to load-test a consumer, fill a topic for a demo or check how lag builds up. With variable substitution on, each copy gets its own values; without it, every copy is identical.

Delay between messages waits that many milliseconds between one message and the next, up to 300,000 (5 minutes); the links under it set common values, and the figure beside it shows the delay in seconds. Use it to trickle messages in at a realistic rate, for example one a second while you watch a live stream or a consumer's dashboard. The delay is ignored, and greyed out, when sending a single message. The send button's label shows the total time, e.g. Produce 10 Messages (in 1.00s).

While a batch is being sent, the dialog shows Sending N messages... with a Cancel link. Cancelling (or closing the dialog) stops the batch; the messages already sent stay in the topic. The rest of KafkIO stays usable meanwhile, so you can watch the topic fill up.

All messages in a batch go through one producer connection, and a batch stops at the first message Kafka rejects (Results and errors).

Variable substitution

With '${variable}' substitution ticked (the default), KafkIO replaces ${…} placeholders in the key, the value and header values with a fresh value for every message it sends. That's how one dialog produces a thousand different orders, each with its own number, customer and timestamp, as in the dialog above:

{
  "orderId": ${counter},
  "customer": "customer-${random.int:1:20}",
  "amount": ${random.int:5:500},
  "status": "NEW",
  "placedAt": "${current.datetime:yyyy-MM-dd'T'HH:mm:ss}"
}

A placeholder KafkIO doesn't recognise is left as it is. Untick the option to send text that contains ${ literally, such as a template for another tool.

Counters and random values

VariableGives
${counter}1, 2, 3... (see below)
${random.int}A random whole number from 0 up to 2,147,483,647
${random.int:min}, ${random.int:min:max} A random whole number from min up to, but not including, max, e.g. ${random.int:60:300}
${random.double}, ${random.double:min}, ${random.double:min:max} A random decimal number, likewise. It has many decimal places; use random.int when you want round amounts.
${random.string}Random letters and digits, 1 to 64 characters long
${random.string:min}, ${random.string:min:max} Random letters and digits, from min up to, but not including, max characters long (exactly min when the two are equal); ${random.string:8:8} gives exactly 8
${random.uuid}A random UUID, e.g. efe1ca4c-f714-4b32-b270-76c9adaa37e9

${counter} starts at 1 each time you click the send button, and goes up by one every time it's used, not once per message. The key is filled in first, then the value, then the headers. So with ${counter} only in the value, ten messages get 1 to 10; with ORD-${counter} as the key and "orderId": "ORD-${counter}" in the value, the first message gets key ORD-1 and order ID ORD-2. Use the counter in one place per message when the numbers need to match.

Dates and epoch times

VariableGives (example)
${current.date}Today's date: 2026-10-25
${current.date:format}Today's date in your format: ${current.date:M/d/yyyy} gives 10/25/2026
${current.datetime}The date and time: 2026-10-25T08:36:09.973170400
${current.datetime:format}The date and time in your format: ${current.datetime:dd-MMM-yyyy HH:mm (EEEE)} gives 25-Oct-2026 08:36 (Sunday)
${epoch.seconds}Seconds since 1 January 1970 UTC: 1792917337
${epoch.millis}Milliseconds since then: 1792917337064

Dates and times are your computer's local time, taken as each message is built. Formats use Java's date pattern letters: yyyy year, MM month number, MMM short month name, dd day, EEEE day name, HH hour (00-23), mm minutes, ss seconds, SSS milliseconds. Put literal letters in single quotes, as in yyyy-MM-dd'T'HH:mm:ss. There's no time zone variable: for a UTC timestamp, use ${epoch.millis}.

Every variable, with more examples, is listed in the Substitution Variables reference.

The examples window

Click Examples next to the option to open Variables Substitution Examples: a scratch pad listing every variable, with the result of substituting them below. Edit the top box to try out your own text or date formats, click 1 Test to see the result, and copy what works into your message. 2 Reset brings back the original list.

The Variables Substitution Examples window. Its header reads: You can insert variable placeholders into keys, values or headers to generate counters, dates, random numbers, strings. Experiment below and copy what is required. The Examples box lists counter, counterAgain, string, stringMin, stringMinMax, date, date1 to date4, datetime, datetime1, datetime2 and more, each with its ${...} variable. Below a Reset link, the Example result box shows the substituted values: counter 1, counterAgain 2 and 3, random strings, date 2026-10-25, date1 2026, date2 10/25/2026, date3 Oct/25/2026, date4 25Oct, datetime 2026-10-25T08:36:09.973170400. A Test button sits under the results. 1 2
Every variable on the left, and what it gave this time on the right.
  1. Test: substitutes the variables in the top box again.
  2. Reset: restores the original examples.

Message templates

A template saves the key, message and headers you've typed, so you can send the same kind of message again later without retyping it: a sample order, a test event with all its headers. Templates are shared by every cluster and topic, so one template can feed the same topic on your development, staging and production clusters.

A template holds only the text: the key, the message, and the header keys and values (variables included, as typed). It doesn't keep the partition, the serializers, how many to send, or the null options; a header saved with Null value ticked comes back with an empty value.

Saving a template

Click Save as template and enter a name. The suggested name is the template selected in the list, if any, otherwise my_template_ followed by the date and time. If a template of that name exists, KafkIO asks before overwriting it.

Loading a template

Choose the template in the Select template... list and click Load. Its key, message and headers replace what's in the dialog; anything the template doesn't have (no key, say) is cleared. Check the serializers and partition afterwards, since the template doesn't set them.

Deleting a template

Choose it in the list and click Delete, then confirm.

Templates are plain text files in the message_templates folder of KafkIO's user data folder (see What's left behind for where that is): key.name.txt, value.name.txt and headers.name.txt (one key=value per line). You can copy them to another machine, or keep them in version control with your project.

Re-producing an existing message

To send a message again, as it is or changed, right-click it in the message table and choose Re-produce Message. The dialog opens filled in from that message:

  • its key, value and headers (with the null options ticked where they were null);
  • its partition, rather than Auto: set it back to Auto if you'd rather let the key decide;
  • as serializers, the deserializers the message was read with; for a Schema Registry message, the Subject that matches the message's schema ID.

It's handy for replaying a message a consumer failed on, after fixing the consumer, or for sending a corrected version of a bad one: change the value, then send. With Avro, the value appears as the JSON KafkIO displayed, which is the form it expects back.

Warning: the dialog is filled with the text KafkIO read, not the original bytes. A value cut short by Key/message trim is sent cut short, and binary data read as String (raw) doesn't survive the trip. Before re-producing such messages, search again with a larger trim, or with the Hex deserializer so the dialog uses the Hex serializer and the bytes are copied exactly. Also untick '${variable}' substitution if the message contains ${.

Results and errors

When a send succeeds, the dialog shows Last message sent at the date and time, with details of the last message in the batch:

FieldMeaning
Partition, Offset Where it was written. Search that partition from that offset to go straight to it.
Serialized key size, Serialized message size Bytes after serializing (-1 for a null): a quick check that a serializer did what you expected.
Timestamp The message's timestamp, in milliseconds since 1 January 1970 UTC.

The topic's message count in the topics list is refreshed after each send. To see the messages themselves, search the topic (Newest shows them first), or stream it while you send.

If something goes wrong, an Error sending message(s) dialog says why:

An Exception Details dialog headed Error sending message(s), reading: JsonAvroConversionException: "ORD-28" node has unexpected type, allowed types [NUMBER], actual type STRING. Below are Show Details, Report, View Logs and OK buttons.
A string where the Avro schema wants a number.

The usual causes:

  • The text doesn't fit the serializer: letters for an Int (32), invalid JSON for MessagePack, or a value that doesn't match the schema, as above. Nothing is sent: fix the text and try again.
  • Kafka refuses the message: not authorised to write to the topic (an ACL), a message bigger than the topic's max.message.bytes, a partition without a leader. KafkIO's producer doesn't retry, so you see Kafka's own error straight away rather than a timeout later.
  • The cluster can't be reached: see Troubleshooting Connections.

When producing many messages, the batch stops at the first failure; the messages before it were sent. Click Show Details for the full error, or View Logs to open KafkIO's log.