Apicurio Registry change events and the outbox pattern

Apicurio Registry emits change events for artifact, version, group, rule, and contract operations. You can consume these events to audit changes and automate workflows in external systems.

This chapter explains the event system architecture, available event types, and how to configure event delivery for your storage variant.

Overview of Apicurio Registry change events

Apicurio Registry generates structured change events as part of its normal operations. Every time the state of the registry changes, an event is emitted with details about what changed.

Events follow the outbox pattern: they are produced as part of the same transaction that modifies the registry state, guaranteeing that events are consistent with the data. The delivery mechanism depends on which storage variant you use:

  • SQL storage (PostgreSQL, MSSQL): Events are written to an outbox database table and consumed using Debezium Change Data Capture (CDC).

  • KafkaSQL storage: Events are published directly to a dedicated Kafka topic.

Typical use cases for registry events include:

Auditing and compliance

Track every change to schemas and API definitions.

CI/CD automation

Trigger pipeline runs when a new artifact version is registered.

Cache invalidation

Notify downstream services when a schema changes.

Event-driven architectures

React to registry state changes in real time.

Event publishing is supported only with PostgreSQL and MSSQL when using SQL storage. MySQL and H2 do not support the outbox table. KafkaSQL supports events natively.

Event delivery uses Kafka, with Debezium for SQL storage and direct publishing for KafkaSQL storage. Apicurio Registry does not currently provide an HTTP webhook subscription API or a webhook delivery service.

Event types reference

Apicurio Registry defines the following event types. Each event includes a JSON payload with details about the change.

Artifact-level changes produce the following event types:

Table 1. Artifact event types
Event type Description

ARTIFACT_CREATED

A new artifact was registered.

ARTIFACT_DELETED

An artifact and all its versions were deleted.

ARTIFACT_METADATA_UPDATED

An artifact’s name, description, or owner was updated.

Artifact version changes produce the following event types:

Table 2. Artifact version event types
Event type Description

ARTIFACT_VERSION_CREATED

A new version of an artifact was created.

ARTIFACT_VERSION_DELETED

An artifact version was deleted.

ARTIFACT_VERSION_METADATA_UPDATED

A version’s metadata (name, description, labels) was updated.

ARTIFACT_VERSION_STATE_CHANGED

A version’s lifecycle state changed (for example, from ENABLED to DEPRECATED).

Group changes produce the following event types:

Table 3. Group event types
Event type Description

GROUP_CREATED

A new group was created.

GROUP_DELETED

A group was deleted.

GROUP_METADATA_UPDATED

A group’s metadata was updated.

Rule changes produce the following event types:

Table 4. Rule event types
Event type Description

GLOBAL_RULE_CONFIGURED

A global compatibility or validity rule was created, updated, or deleted.

GROUP_RULE_CONFIGURED

A group-level rule was created, updated, or deleted.

ARTIFACT_RULE_CONFIGURED

An artifact-level rule was created, updated, or deleted.

Contract operations produce the following event types:

Table 5. Contract event types
Event type Description

CONTRACT_RULESET_CONFIGURED

A global, artifact, or version contract ruleset is set or deleted.

CONTRACT_METADATA_UPDATED

Contract metadata labels are updated.

CONTRACT_STATUS_CHANGED

The contract lifecycle status changes, for example, from DRAFT to STABLE.

Contract events use the same delivery mechanism as the other change events for your storage variant. The internal READY startup signal is not a published change event.

Event payload structure

Each Apicurio Registry change event has a JSON payload with an id and an eventType. Additional fields identify the affected resource and describe the change.

Artifact events include the following identifying fields:

{
  "id": "<uuid>",
  "eventType": "<EVENT_TYPE>",
  "groupId": "<group-id>",
  "artifactId": "<artifact-id>"
}

Additional fields vary by event type:

  • Group events include groupId and omit artifactId.

  • Global rule events omit both groupId and artifactId.

  • Artifact version events include a version field.

  • Artifact version state change events include oldState and newState fields.

  • Metadata update events include name, description, and owner fields where applicable.

  • Rule events include ruleType and rule (the rule configuration) fields.

For artifacts in the default group, the payload omits groupId. Optional metadata fields with no value are also omitted.

Example: ARTIFACT_VERSION_CREATED payload
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "eventType": "ARTIFACT_VERSION_CREATED",
  "groupId": "com.example",
  "artifactId": "my-schema",
  "version": "1.2.0",
  "name": "My Schema",
  "description": "Updated with new fields"
}
Example: ARTIFACT_VERSION_STATE_CHANGED payload
{
  "id": "550e8400-e29b-41d4-a716-446655440001",
  "eventType": "ARTIFACT_VERSION_STATE_CHANGED",
  "groupId": "com.example",
  "artifactId": "my-schema",
  "version": "1.0.0",
  "oldState": "ENABLED",
  "newState": "DEPRECATED"
}

Contract events have the following payload fields in addition to id and eventType:

Table 6. Contract event payload fields
Event type Fields and behavior

CONTRACT_RULESET_CONFIGURED

groupId, artifactId, and action, which is SET or DELETE. Version rulesets also include version. Global rulesets use GLOBAL for both groupId and artifactId and omit version. The payload does not include the ruleset content.

CONTRACT_METADATA_UPDATED

groupId and artifactId identify the affected contract. The payload does not include the updated labels.

CONTRACT_STATUS_CHANGED

groupId, artifactId, fromStatus, and toStatus. If the contract had no previous status, fromStatus is an empty string. The payload does not include the transition effective date.

Example: Version contract ruleset update
{
  "id": "550e8400-e29b-41d4-a716-446655440002",
  "eventType": "CONTRACT_RULESET_CONFIGURED",
  "groupId": "com.example",
  "artifactId": "orders",
  "version": "1.0.0",
  "action": "SET"
}
Example: Contract metadata update
{
  "id": "550e8400-e29b-41d4-a716-446655440003",
  "eventType": "CONTRACT_METADATA_UPDATED",
  "groupId": "com.example",
  "artifactId": "orders"
}
Example: Contract status change
{
  "id": "550e8400-e29b-41d4-a716-446655440004",
  "eventType": "CONTRACT_STATUS_CHANGED",
  "groupId": "com.example",
  "artifactId": "orders",
  "fromStatus": "DRAFT",
  "toStatus": "STABLE"
}

Event system architecture

The event system uses CDI (Contexts and Dependency Injection) events internally to decouple the storage layer from event delivery. When a storage operation modifies state, it fires a CDI event.

A storage-specific event processor observes the CDI event and routes it to the appropriate delivery mechanism. The following diagram shows the event flow from a storage operation to external consumers:

Storage operation (for example, createArtifact)
       |
       v
  Fire CDI event
  (SqlOutboxEvent or KafkaSqlOutboxEvent)
       |
       +---------------------------+
       |                           |
       v                           v
  SQL storage                 KafkaSQL storage
  SqlEventsProcessor          KafkaSqlEventsProcessor
       |                           |
       v                           v
  Insert into outbox table    Publish to Kafka topic
  (PostgreSQL / MSSQL)        (registry-events)
       |                           |
       v                           |
  Debezium CDC connector           |
       |                           |
       +---------------------------+
       |
       v
  External consumers
  (Kafka topic: registry-events)

SQL storage outbox pattern with Debezium

With SQL storage, Apicurio Registry writes change events to an outbox database table that Debezium reads using Change Data Capture (CDC).

With SQL storage, events are written to an outbox database table as part of the same database transaction that performs the registry operation. This guarantees that events are only produced for changes that are actually committed.

The outbox table uses an insert-then-delete pattern: the event row is inserted and then immediately deleted within the same transaction. Debezium CDC captures the insert before the deletion takes effect, ensuring the event is published to Kafka without the outbox table growing indefinitely.

The outbox table schema:

CREATE TABLE outbox (
    id            VARCHAR(128)  NOT NULL PRIMARY KEY,
    aggregatetype VARCHAR(255)  NOT NULL,
    aggregateid   VARCHAR(255)  NOT NULL,
    type          VARCHAR(255)  NOT NULL,
    payload       JSONB         NOT NULL
);

KafkaSQL storage direct Kafka publishing

With KafkaSQL storage, Apicurio Registry publishes change events directly to a dedicated Kafka topic without using the outbox table.

With KafkaSQL storage, events bypass the database entirely and are published directly to a dedicated Kafka topic. The events topic uses a single partition to guarantee total ordering of all events. The event’s aggregate ID is used as the Kafka message key.

Configuring events with SQL storage and Debezium

When using SQL storage with PostgreSQL or MSSQL, you can use Debezium to capture events from the outbox table and publish them to Kafka.

Prerequisites
  • Apicurio Registry is deployed with SQL storage using PostgreSQL or MSSQL.

  • A Kafka cluster is available.

  • Debezium is deployed with the Kafka Connect framework.

Procedure
  1. Enable logical replication on your PostgreSQL database. Set the following in postgresql.conf:

    wal_level = logical
  2. Create a Debezium PostgreSQL connector that monitors the outbox table. Use the following connector configuration as a starting point:

    {
      "name": "registry-outbox-connector",
      "config": {
        "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
        "database.hostname": "<database-host>",
        "database.port": "5432",
        "database.user": "<database-user>",
        "database.password": "<database-password>",
        "database.dbname": "<database-name>",
        "topic.prefix": "registry",
        "schema.include.list": "public",
        "table.include.list": "public.outbox",
        "transforms": "outbox",
        "transforms.outbox.type": "io.debezium.transforms.outbox.EventRouter"
      }
    }
  3. Deploy the connector to your Kafka Connect cluster:

    curl -X POST http://<kafka-connect-host>:8083/connectors \
      -H "Content-Type: application/json" \
      -d @connector-config.json
  4. Verify that events are being published by consuming from the output topic:

    kafka-console-consumer --bootstrap-server <kafka-host>:9092 \
      --topic outbox.event.registry-events --from-beginning

    The Debezium EventRouter transformation reads the outbox table columns and routes events to Kafka topics based on the aggregatetype column.

Configuring events with KafkaSQL storage

When using KafkaSQL storage, events are published directly to a Kafka topic with no additional infrastructure required. The events topic is created automatically when Apicurio Registry starts.

Prerequisites
  • Apicurio Registry is deployed with KafkaSQL storage.

Procedure
  1. Optionally configure the events topic name by setting the following environment variable:

    APICURIO_EVENTS_KAFKA_TOPIC=registry-events

    The default topic name is registry-events.

  2. Optionally configure topic properties such as replication factor:

    APICURIO_EVENTS_KAFKA_TOPIC_REPLICATION_FACTOR=3
  3. Consume events from the configured topic:

    kafka-console-consumer --bootstrap-server <kafka-host>:9092 \
      --topic registry-events --from-beginning

    The events topic is configured with the following defaults:

    • Partitions: 1 (single partition for total ordering guarantee)

    • Cleanup policy: delete

    • Retention: Infinite (-1)

Configuration reference

You configure the Apicurio Registry event system with properties that set the Kafka events topic name and, for KafkaSQL storage only, the replication factor, cleanup policy, and retention time of that topic.

Table 7. Event configuration properties
Property Default Description

apicurio.events.kafka.topic

registry-events

Name of the Kafka topic used for publishing registry change events. Applies to both SQL (via Debezium) and KafkaSQL storage variants.

apicurio.events.kafka.topic.replication.factor

Inherited from journal topic

Replication factor for the events topic (KafkaSQL only). If not set, inherits from the main KafkaSQL journal topic configuration.

apicurio.events.kafka.topic.cleanup.policy

delete

Cleanup policy for the events topic (KafkaSQL only).

apicurio.events.kafka.topic.retention.ms

-1 (infinite)

Retention time in milliseconds for the events topic (KafkaSQL only).

The following table lists event support by storage variant:

Table 8. Event support by storage variant
Storage variant Events supported Delivery mechanism Requirements

SQL (PostgreSQL)

Yes

Outbox table + Debezium CDC

Debezium connector, Kafka

SQL (MSSQL)

Yes

Outbox table + Debezium CDC

Debezium connector, Kafka

SQL (MySQL)

No

 — 

 — 

SQL (H2)

No

 — 

 — 

KafkaSQL

Yes

Direct Kafka publishing

None (built-in)

GitOps

No

 — 

 — 

KubernetesOps

No

 — 

 —