> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# CDP event schemas console: enforce a JSON Schema per event type with off, warn, and strict modes

> Register a JSON Schema for each event type at Integrations → CDP → Event schemas, roll it out in warn mode while you watch violations surface, then flip to strict to reject invalid payloads with a 422 at the ingest boundary.

# CDP event schemas console

An event schema is a per-event-type validation gate on inbound CDP events. You register a JSON Schema for the event type, choose an enforcement mode, and every inbound `track`, `page`, `screen`, and `batch` payload whose event type matches a row is validated against it. This guide covers the console at **Integrations → CDP → Event schemas**: the mode semantics, the editor workflow, the supported JSON Schema subset, and a warn-first rollout pattern. The events your [tracking plan](/guides/cdp-tracking-plan) declares tell you *what* producers should send; event schemas are the enforcement engine that decides what happens when they disagree.

Everything below also works from the API (`POST /api/v1/cdp/schemas` and siblings), so CI can drive it the same way your tracking plan does.

## 1. Open the console

Go to **Integrations → CDP** in the dashboard. The two linked route tiles at the top of the hub include **Event schemas** (next to Tracking plan) — click it to open the schemas page at **Integrations → CDP → Event schemas**.

The page lists every registered schema with its mode badge and last-updated timestamp, a search box over event type and description, and a mode filter. Two badges you will read constantly:

* **Strict** (red) — invalid payloads for this event type are rejected.
* **Warn** (amber) — invalid payloads are accepted, and the violation is surfaced for review.
* **Off** (muted) — no validation; payloads pass through verbatim.

Delete is behind a confirmation dialog that warns the event type reverts to passthrough.

Editing requires the owner, admin, or developer role — the same restriction as the tracking plan, because a mode flip changes whether customer events are accepted.

## 2. Enforcement modes: off, warn, strict

The mode on a row decides what the ingest boundary does when a payload fails the schema:

* **Off — accept verbatim.** The payload is stored as-is with no validation. Use `off` to stage a schema while you are still drafting it, or to park a row you want to keep without enforcing. Event types with no row at all behave the same: passthrough, always.
* **Warn — accept, and surface the drift.** The payload is stored as usual, and the validation failure is recorded as a violation against that event type. Nothing is rejected, so well-behaved producers keep delivering while you measure how far the bad producers drift.
* **Strict — reject with 422.** The payload is rejected at ingest with HTTP 422. Nothing untyped enters your event store, so downstream segments and models only see clean data.

Unmatched event types are accepted regardless of mode — strictness only applies to events you have registered. That scoping is the same rule the tracking plan uses, and it exists so a schema can never silently drop events you did not opt in.

## 3. The schema editor

Click **Add schema**, or **Edit** on an existing row. The editor has four fields:

* **Event type** — the event name your SDK sends, matched verbatim (case-sensitive). This is the natural key: re-saving the same value updates the row in place, and creating a second schema with the same value overwrites the first. The field is locked when you edit an existing row — to rename an event type, delete the row and recreate it under the new name.
* **Enforcement mode** — `off`, `warn`, or `strict`, as above.
* **Description** — optional free text, useful for noting which team owns the event.
* **JSON Schema** — the payload contract. New schemas start from a template that declares a few typed properties with `required` and `additionalProperties: false`, so you paste your shape over it rather than starting from a blank textarea.

Save is an upsert against `POST /api/v1/cdp/schemas` keyed on the event type, and every create, update, and delete lands in the workspace audit log.

**JSON parse errors are handled before the save round-trips.** If the textarea does not parse, the editor shows a friendly hint inline ("Invalid JSON — check for missing brackets, commas, or mismatched quotes", with a position pointer when one is available) instead of the raw parser message, and the save never fires until you fix the text.

## 4. Supported JSON Schema subset

The validator runs a deliberately small, deterministic subset of JSON Schema. Keywords under `properties` that participate in validation:

* `type`
* `enum` (value sets)
* `minimum` / `maximum`
* `minLength` / `maxLength`
* `pattern` / `format`

And at the object level: `required` and `additionalProperties`.

Keywords outside that list (`oneOf`, `allOf`, `$ref`, `definitions`, …) are accepted and stored but treated as no-ops by the validator. The practical rule: keep validation to the plain keywords above, and treat anything else as documentation that travels with the schema. If your pipeline pastes in a fuller JSON Schema from a codegen tool, it stores fine — just do not rely on the unsupported keywords to gate traffic. The [tracking plan](/guides/cdp-tracking-plan) declares the same bounded keyword set inline (type, enum, required), so producers wiring both surfaces write one shape per event.

## 5. Example: warn-first rollout, then flip to strict

The pattern that keeps you from rejecting real traffic:

1. **Register in `warn`.** Add the schema for the event type with mode `warn` the day the event goes live. Payloads keep flowing; violations accumulate against that event type.
2. **Watch the violations.** In `warn`, violations surface without costing you events — a violation feed tells you which properties the producers disagree on, and by how much. Fix the producer or widen the schema until the feed goes quiet.
3. **Flip to `strict`.** PATCH the mode (edit the row, or re-POST with `"enforcement_mode": "strict"`) once the producers you control are clean. From then on, a diverging payload gets a 422 at the boundary instead of silently corrupting the segments built on it.

The same discipline the tracking plan uses: only flip to `strict` for events whose producers you can update end-to-end. A mobile SDK on a version you cannot force-upgrade will 422 real purchases if the schema and installed clients disagree.

From the API the flip is one call:

```bash theme={null}
curl -X PATCH "https://api.orbit.devotel.io/api/v1/cdp/schemas/Order%20Completed" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "enforcement_mode": "strict" }'
```

`POST /api/v1/cdp/schemas` upserts keyed on `event_type`, and `PATCH /api/v1/cdp/schemas/:event_type` takes only the fields you name — so a mode flip never touches the stored schema.

## 6. Schemas vs the tracking plan

The two surfaces govern different layers of the same contract:

* **[Tracking plan](/guides/cdp-tracking-plan)** declares which event names exist and what shape their `properties` take, as `event_name` rows with `soft` or `hard` enforcement on property rules you write inline (type, enum, required).
* **Event schemas** (this page) enforce a full JSON Schema per `event_type` at ingest, with the three-mode gate (`off` / `warn` / `strict`) and the subset above.

Use the tracking plan to catalog your event taxonomy and declare each event's expected property shape; use event schemas when a subset of those events needs hard, schema-level enforcement at the ingest boundary — for example the events that feed billing computations or model training, where a wrong type is unacceptable. Many workspaces run both: the tracking plan as the catalog of record, event schemas as the gate on the events where correctness pays for rejection.

If an event type has both a tracking-plan row and an event-schema row, the two validations run independently; a payload can trip either, both, or neither.

## 7. Troubleshooting

**422s in strict mode, unexpectedly.** A producer diverged from the schema. Because unmatched event types always pass, the 422 means the event type *matched* a strict row. Reopen the row, flip it to `warn` to stop the rejection while you triage, then fix the producer or widen the schema and flip back.

**Violations in warn mode that look wrong.** Check whether the producer is sending the property under a different name (a rename masquerading as a schema gap) or with a type the subset cannot express — `oneOf` style "string-or-number" shapes are no-ops in the validator, so write them as a `format` hint or split them into separate properties.

**The list page is empty but you registered schemas over the API.** The page requires the owner/admin/developer role; a viewer role sees the console tiles but the list read is permitted at the same role level as the write side. Also check the mode filter and search box — a filtered empty-set renders a "No schemas match your filters" state rather than the true empty state.

**Rename an event type.** Delete the row (the event type reverts to passthrough on delete) and re-add it under the new name. The event type field is locked in the editor precisely to prevent accidental renames masquerading as updates.

## See also

* [CDP tracking plan](/guides/cdp-tracking-plan) — the event-name catalog with its own soft/hard enforcement and violations feed
* [CDP integration console overview](/guides/cdp-integration-console-overview) — the hub both tiles hang off
* [CDP data catalog and identity rules](/guides/cdp-data-catalog-and-identity-rules) — where event-property governance complements schema enforcement
* [CDP event debugger and DLQ](/guides/cdp-event-debugger-and-dlq) — live-tail the inbound stream while you roll a new schema out in warn
* [CDP audiences](/guides/cdp-segments) — the segments your strict-mode schemas keep clean
