Browse documentation

Schemas and compatibility

Make payload rules explicit and evolve event and API contracts without silently changing their meaning.

On this page
Before you start
  • A consumer inventory
  • Example payloads
  • A release and deprecation process

A contract change before release

Validate both the new producer and existing consumer expectations before changing the shared boundary.

A contract change before releaseStep 1: Schema author to Contract registry. Propose schema revision. Step 2: Contract registry to Consumer tests. Run known payloads. Step 3: Consumer tests to Release gate. Report compatibility. Step 4: Release gate to Schema author. Approve or revise.Schema authorContract registryConsumer testsRelease gatePropose schema revision1Run known payloads2Report compatibility3Approve or revise4

Scroll across the diagram to see all four systems.

Inspect each exchange

Propose schema revision

Record field constraints and the semantic reason for the change.

Pin the schema dialect

Declare the JSON Schema dialect with $schema and give reusable contracts a stable $id. A schema validator must support the selected dialect; merely loading a document does not establish compatibility. Keep the validator configuration in the release evidence.

Properties describe known members but do not make them required. Use required deliberately, and distinguish an omitted property from an explicit null. If null has meaning, include it in the accepted type rather than allowing accidental coercion.

Contact event payload schemajson
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://api.example.test/schemas/contact-qualified/1",
  "type": "object",
  "required": [
    "contact_id",
    "stage",
    "occurred_at"
  ],
  "properties": {
    "contact_id": {
      "type": "string",
      "minLength": 1
    },
    "stage": {
      "enum": [
        "qualified"
      ]
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "source_revision": {
      "type": "integer",
      "minimum": 0
    }
  },
  "additionalProperties": true
}

Treat meaning as part of compatibility

Adding an optional field is often compatible for tolerant readers, but adding an enum value may break a client with exhaustive branching. Changing a money field from major units to minor units is a breaking change even if the type remains number.

Maintain examples that express boundary cases: zero, empty strings, absent values, explicit null, long identifiers and unknown extension fields. Test the consumer behavior, not just the producer schema.

ChangeTypical riskRelease decision
New optional fieldStrict readers reject unknown members.Check actual reader policy before calling it additive.
New enum valueExhaustive handlers have no branch.Provide an unknown value strategy or a versioned contract.
Required field addedOlder producers cannot satisfy the schema.Coordinate producer migration.
Type or units changedValid parsing may produce incorrect business results.Create an explicit replacement contract.
Field removedConsumers lose an expected input.Measure usage and complete the announced migration.

Separate shape checks from business rules

A JSON Schema can constrain a type, length, range or allowed value. It cannot establish whether a contact belongs to the authenticated tenant, whether a consent record is authoritative or whether a destination write is allowed. Those checks need current business and authorization context.

In Draft 2020 12, format annotation and format assertion are separate vocabularies. Do not assume that format: email or format: date-time enforces validity unless the chosen validator and configuration enable the required assertion behavior.

Version the boundary that actually changes

An API path version, event type version and schema revision answer different questions. A compatible field addition may update the schema without changing the API path. A change in event meaning may need a distinct event type so old consumers do not reinterpret historical records.

Keep event payload schemas retrievable after a producer upgrade. A replay of last month’s event still needs the contract that described it. Record schema identity with the event or provide a deterministic mapping from event type and version.

Make deprecation operationally visible

Publish the replacement, the reason for migration and the behavior that will stop. Inventory consumers through access records or explicit registration. A calendar date alone is not evidence that every critical consumer migrated.

Before retiring a contract, verify active usage, replay requirements, rollback behavior and archived data readers. Preserve a path to understand old records even after the old write interface is removed.

Check a compatibility decision

Explore how required fields, enum handling and semantic changes affect a consumer contract.

POST/api/v1/contracts/validate

Contract inputs

Includes request values and the server validation context defined below.

Expected outcome

HTTP 200

Contract accepted

The payload shape and meaning fit the consumer contract.

{
  "valid": true,
  "compatibility": "accepted",
  "schema_revision": 1
}

Decision path

  1. Check required properties
  2. Check the consumer value policy
  3. Check semantic compatibility
Input schema 4 fields
FieldTypeMeaning
required_field_presentRequiredbooleanWhether the original required field is present.
stageRequiredstringIncoming lifecycle value.
reader_tolerates_unknownRequiredbooleanWhether the consumer has an explicit unknown value branch.
units_changedRequiredbooleanWhether the field meaning changed without a new contract.
Download JSON Schema

Standards and documentation

Explore the protocol specifications and platform guidance for this topic.

Bring the contract into your operating environment.

Discuss an implementation

Image preview