Skip to main content
In CockroachDB, the changefeed envelope is the structure of each emitted to the . Changefeeds package events, triggered by an update to a row in a , into messages according to the configured envelope. By default, changefeed messages use the wrapped envelope, which includes the primary key of the changed row and a top-level field indicating the value of the row after the change event.​ You can use changefeed options to customize message contents in order to integrate with your downstream requirements. For example, the previous state of the row, the origin cluster’s metadata, or the schema of the event payload in the message. Envelope configuration also allows you to prioritize change event metadata versus changefeed throughput. The possible envelope fields support use cases such as: This page covers:
  • Use case examples.
  • Reference lists:
    • The options to configure the envelope.
    • The supported envelope fields.
You can also specify the format of changefeed messages, such as Avro. For more details, refer to .

Use cases

The use case examples in the following sections emit to a Kafka sink. Review the Options table for option sink support. Each example uses the following table schema:
The values that the envelope option accepts are compatible with different , and the structure of the message will vary depending on the sink and .

Audit changes in data

You can include both the previous and updated states of a row in the message envelope to support use cases like auditing or applying change-based logic in downstream systems. Use the diff option with to include the previous state of the row:
The diff option adds the "before" field to the envelope containing the state of the row before the change:
For an insert into the table, the "before" field contains null:

Route events based on operation type

This feature is in and subject to change. To share feedback and/or issues, contact Support.
You may want to route change events in a table based on the operation type (insert, update, delete), which can be useful for correctly applying or handling changes in a downstream system or replication pipeline. Use the envelope=enriched option with to include the "op" field in the envelope:
The "op" field can contain:
  • "c" for .
  • "u" for .
  • "d" for .
The "ts_ns" timestamp, included in the envelope when you specify only the envelope=enriched option, is the time the message was processed by the changefeed job. If you require timestamps to order messages based on the change event’s commit time, then you must specify envelope=enriched, enriched_properties=source, updated when you create the changefeed, which will include "ts_hlc" and "ts_ns" in the "source" field.

Add envelope schema fields

This feature is in and subject to change. To share feedback and/or issues, contact Support.
Adding the schema of the event payload to the message envelope allows you to:
  • Handle schema changes in downstream processing systems that require field types.
  • Detect and adapt to changes in the table schema over time.
  • Correctly parse and cast the data to deserialize into a different format.
  • Automatically generate or synchronize schemas in downstream systems.
  • Verify critical fields are present and set up alerts based on this.
It is important to consider that adding the schema of the event payload can increase the size of each message significantly, which can have an impact on changefeed throughput. Use the envelope=enriched, enriched_properties=schema options with to include the "schema" top-level field and the schema fields and types:

Preserve the origin of data

This feature is in and subject to change. To share feedback and/or issues, contact Support.
When you have multiple changefeeds running from your cluster, or your CockroachDB cluster is part of a multi-architecture system, it is useful to have metadata on the origin of changefeed data in the message envelope. Use the envelope=enriched, enriched_properties=source options with to include the "source" top-level field that contains metadata for the origin cluster and the changefeed job:

Enable full-fidelity message envelopes

This feature is in and subject to change. To share feedback and/or issues, contact Support.
A full-fidelity changefeed message envelope ensures complete information about every change event in the —including the before and after state of a row, timestamps, and rich metadata like source and schema information. This type of configured envelope allows downstream consumers to replay, audit, debug, or analyze changes with no loss of context. It is important to consider that a full-fidelity envelope increases the size of each message significantly, which can have an impact on changefeed throughput. Use the mvcc_timestamp, envelope=enriched, enriched_properties='source,schema', diff, updated, and key_in_value (for Kafka) options with to create a full-fidelity envelope:

Option reference

For a full list of options that modify the message envelope, refer to the following table:
OptionDescriptionSink support
diffInclude a "before" field in each message, showing the state of the row before the change. Supported with wrapped or enriched envelopes.All
enriched\_properties (Preview)(Only applicable when envelope=enriched is set) Specify the type of metadata included in the message payload. Values: source, schema.Kafka, Pub/Sub, webhook, sinkless
envelope=bareEmit an envelope without the "after" wrapper. The row’s column data is at the top level of the message. Metadata that would typically be separate will be under a "\_\_crdb\_\_" field. Provides a more compact structure to the envelope. bare is the default envelope when using . When bare is used with the Avro format, "record" will replace the "after" keyword.All
envelope=enriched (Preview)Extend the envelope with additional metadata fields. With enriched\_properties, includes a "source" field and/or a "schema" field with extra context. Supported in JSON, Avro, and protobuf .Kafka, Pub/Sub, webhook, sinkless
envelope=key\_onlySend only the primary key of the changed row and no value payload, which is more efficient if only the key of the changed row is needed. Not compatible with the updated option.Kafka, sinkless
envelope=rowEmit the row data without any additional metadata field in the envelope. Not supported in Avro format or with the diff option.Kafka, sinkless
envelope=wrapped (default)Produce changefeed messages in a wrapped structure with metadata and row data. wrapped includes an "after" field, and optionally a "before" field if diff is used. Note: Envelopes contain a primary key when your changefeed is emitting to a sink that does not have a message key as part of its protocol. By default, messages emitting to Kafka sinks do not have the primary key field. Use the key\_in\_value option to include a primary key array field in messages emitted to .All
full\_table\_nameUse the (database.schema.table) in topics, subjects, schemas, and record output instead of the default table name. Including the full table name prevents unintended behavior when the same table name is present in multiple databases.All
key\_in\_valueAdd a primary key array to the emitted message in Kafka sinks. This makes the primary key of a deleted row recoverable in sinks where each message has a value, but not a key. key\_in\_value is on by default in cloud storage and webhook sinks. To only emit the primary key of the changed row in Kafka sinks, use envelope=key\_only.Kafka
mvcc\_timestampEmit the timestamp for each change event. The message envelope contains the MVCC timestamp of the changed row, even during the changefeed’s initial scan. Provides a precise database commit timestamp, which is useful for debugging or strict ordering.All
resolvedEmit resolved timestamps in a format depending on the connected sink. Note: The resolved timestamp is emitted as a separate message, and has its own envelope containing a resolved key and a timestamp value as a string. For more details on the resolved options, refer to .All
updatedAdd an "updated" timestamp field to each message, showing the commit time of the change. When the changefeed runs an initial scan or a , the "updated" field will reflect the time of the scan or backfill, not the MVCC timestamp. If you use updated with envelope=enriched, you must also specify enriched\_properties=source, and then the "updated" field will be replaced with "ts\_ns" and "ts\_hlc" in the "source" fields. Note:envelope=enriched with the updated option will not produce a change event commit timestamp in the message—to include the timestamp, use updated with envelope=enriched, enriched\_properties=source, updated.All

envelope option examples

wrapped

wrapped is the default envelope structure for changefeed messages. This envelope contains an array of the primary key (or the key as part of the message metadata), a top-level field for the type of message, and the current state of the row (or NULL for ). By default, messages emitted to Kafka sinks do not have the primary key array field. If you would like messages emitted to Kafka sinks to contain a primary key array field, you can use the option. Refer to the following message outputs for examples of this. Cloud storage sink:
Kafka sink: Default when envelope=wrapped or envelope is not specified:
Kafka sink message with key_in_value provided:

enriched

This feature is in and subject to change. To share feedback and/or issues, contact Support.
enriched introduces additional metadata to the envelope, which is further configurable with the enriched_properties option. This envelope option is supported for , , , and sinkless changefeeds. To add the operation type and the processing timestamp of the change event to the envelope, use envelope=enriched:
To order messages when using envelope=enriched, you must also use enriched_properties='source' with the updated option in order to include the "ts_hlc" and "ts_ns" commit timestamps in the "source" field:
To add the origin of the change data and the schema of the payload, use the envelope=enriched and enriched_properties='source,schema':

bare

bare removes the after key from the changefeed message and stores any metadata in a crdb field. When used with format, record will replace the after key. Cloud storage sink:
use message format by default. The bare message envelope places the output of the SELECT clause at the top level of the message instead of under an "after" key. When there is additional information that the changefeed is sending, such as or timestamps, the messages will include a crdb field containing this information. In CDC queries: A changefeed containing a SELECT clause without any additional options:
A changefeed containing a SELECT clause with the option specified:

key_only

key_only emits only the key and no value, which is faster if you only need to know the key of the changed row. This envelope option is only supported for or sinkless changefeeds. Kafka sink:
It is necessary to set up a Kafka consumer to display the key because the key is part of the metadata in Kafka messages, rather than in its own field. When you start a Kafka consumer, you can use --property print.key=true to have the key print in the changefeed message.
Sinkless changefeeds:

row

row emits the row without any additional metadata fields in the message. This envelope option is only supported for or sinkless changefeeds. row does not support format—if you are using avro, refer to the bare envelope option. Kafka sink:

Field reference

CockroachDB provides multiple changefeed envelopes, each supported by different and use cases. For the changefeed options to enable each field, refer to the following descriptions:

"payload"

The top-level "payload" field is present in envelopes for changefeeds emitting to a sink and contains all of the change event data. The messages emit as a batch with a "payload" wrapper around the change event data and a "length" field for the number of messages in the batch:

"payload" with enriched envelope

When the envelope=enriched, enriched_properties=schema envelope options are specified, the envelope will include a payload field that wraps the entire message except for the "schema" fields. As a result, when you emit messages to a webhook sink with envelope=enriched, enriched_properties=schema, you will receive messages with two "payload" fields, similar to the following structure:

"after"

The state of the row after the change. This contains the column names and values after an or . For operations, "after" will be NULL. In the default wrapped envelope, every message for an INSERT or UPDATE has an "after" field with the new data. In a row envelope, the whole message is the state of the row without the "after" wrapper, and in the key_only envelope there is no "after" field because only the key is sent.

"before"

The state of the row before the change. This field appears only if the diff option is enabled on a wrapped (or enriched) envelope. For updates, "before" is the previous values of the row before the update. For deletes, "before" is the last state of the row. For inserts, "before" will be NULL (the row had no prior state). This field is useful for auditing changes or computing differences. (Not applicable to envelopes like row or key_only, which do not support the "before" or "after" fields.)

"key"

  • For non- enriched envelopes: An array composed of the row’s PRIMARY KEY field(s) (e.g., [1] for JSON or {"id":{"long":1}} for Avro). For Kafka sinks, the primary key array field is by default off. If you would like messages emitted to Kafka sinks to contain a primary key array, you can use the option.
  • For enriched envelopes: The primary key of the row as an object, e.g, {"id": 1}.

"updated"

A timestamp indicating when the change was committed. This field appears if the updated option is enabled. It is formatted as a string timestamp. The updated timestamp corresponds to the transaction commit time for that change. If the changefeed was started with a (at a specific past timestamp), the updated times will align with the MVCC timestamps of each row version.

"op"

The type of change operation. "c" for INSERT, "u" for UPDATE, "d" for DELETE. This field emits if envelope=enriched is enabled. If you’re using to , we recommend using the envelope=enriched option instead for this metadata.

"source"

Metadata about the source of the change event. This is included when using envelope=enriched with enriched_properties='source' (or 'source,schema'). The "source" field includes the following fields about the cluster running the changefeed:
  • "changefeed_sink": The type receiving the changefeed (e.g., "kafka", "sinkless buffer" ).
  • "cluster_id": The .
  • "cluster_name": The name, .
  • "database_name": The name of the database.
  • "db_version": The CockroachDB version.
  • "job_id": The changefeed’s ID.
  • "mvcc_timestamp": The of the change, as a string. This is CockroachDB’s timestamp for the version (a decimal number representing logical time). It is included if the mvcc_timestamp option is set. Unlike "updated", which might be omitted for initial scans, "mvcc_timestamp" is always present on each message when enabled, including backfill events. This field is mainly used for low-level debugging or when exact internal timestamps are needed. ( "mvcc_timestamp" will be a top-level field when the changefeed is not using the enriched envelope.)
  • "node_id": The ID of the node that emitted the changefeed message.
  • "node_name": The name of the node that emitted the changefeed message.
  • "origin": The identifier for the changefeed’s origin, always cockroachdb.
  • "primary_keys": An array of column name(s) for the changed row.
  • "schema_name": The schema name of the changed table.
  • "source_node_locality": The of the node that emitted the changefeed messages, e.g., "cloud=gce,region=us-east1,zone=us-east1-b".
  • "table_name": The name of the table that changed.
  • "ts_hlc": A timestamp indicating when the change was committed. Included instead of "updated" when the enriched_properties=source and updated options are included at changefeed creation.
  • "ts_ns": A timestamp indicating when the change was committed. Included instead of "updated" when the enriched_properties=source and updated options are included at changefeed creation.

"ts_ns"

The processing time of the event by the changefeed job. This field emits if envelope=enriched is enabled. When you’re comparing changes for ordering, it is important to ignore this top-level [ts_ns field]. Instead, if you require timestamps to order messages based on the change event’s commit time, then you must specify envelope=enriched, enriched_properties=source, updated when you create the changefeed, which will include "ts_hlc" and "ts_ns" in the "source" field.

table:

The name of the table that generated the change. This field appears in certain contexts:
  • In sinkless changefeeds (those created with or CREATE CHANGEFEED... WITH sinkless ), the output includes a "table" field for each row change, because a single sinkless feed could cover multiple tables.
  • In the enriched envelope, the is typically part of the "source" field (as "database_name", "schema_name", "table_name" sub-fields), rather than a separate top-level "table" field.
When a changefeed targets a table with multiple column families, the family name is appended to the table name as part of the topic. Refer to for guidance.

__crdb__

A field used by the bare envelope to carry CockroachDB metadata that would otherwise be top level. When envelope=bare, the message’s top level is the row data, so CockroachDB inserts any needed metadata (primary key, topic, timestamps, etc.) under a nested "__crdb__" object. For example:
Here "__crdb__": {"key": [101]} holds the primary key for the row, while the rest of the object are the row’s columns at the top level. Other metadata like "updated" timestamps or resolved timestamps would also appear inside __crdb__, if present. This field is specific to the bare envelope or other cases (like custom with SELECT) where metadata needs to be attached without interfering with the selected row data.

"schema"

The schema and type of each payload field. This is included when using envelope=enriched with enriched_properties='schema'. The "schema" field provides information needed to interpret the data. The following are present for each schema present in the envelope, depending on the configured options:
  • "field": The name of field.
  • "type": The type of the field. If "type" is an array, then the "items" field defines the data type of the array elements.
  • "optional": This is a boolean field that defines whether a field may be absent in the "payload" section of the envelope depending on the configuration.
  • "name": The name of the described schema.

See more

  • For more details on the file naming format for cloud storage sinks, refer to .