CDP Tracking Plan
A tracking plan is the contract between the apps that send you events and the CDP that stores them. You declare which event names exist and what theirproperties should look like, and every inbound payload is checked against that contract at ingest time. This guide covers declaring events, choosing between soft and strict enforcement, reading the violations feed, and keeping the plan under version control. The segments and scores documented in CDP audiences are only as trustworthy as the events they filter over — a tracking plan is how you keep that input honest.
The same surface backs the dashboard at Integrations → CDP → Tracking Plan. Everything below also works from the API, so your CI can drive it.
1. What a tracking plan is
A tracking plan row declares one event:event_name— theeventstring your SDK sends (case-sensitive). One row per name; re-posting the same name updates it.properties_schema— a subset of JSON Schema:propertiesmaps each property name to a rule (typeand optionally anenumvalue set), andrequiredlists properties that must be present. Types supported:string,number,integer,boolean,object,array.enforcement—soft(log violations, accept the event) orstrict(reject violating events with HTTP 422).
track, page, screen, and batch leg whose event name matches a plan row is validated. Mismatches are recorded in the violations feed whether or not the event was rejected, so soft mode and strict mode share one audit trail.
Validation rules, in the order they fire:
- A property listed in the plan’s
requiredarray that arrives missing or null →missing_required. - A property whose value’s type differs from the rule’s
type→type_mismatch(integer values satisfy anumberrule). - A property whose value falls outside the rule’s
enumset →enum_mismatch. - A property present in the payload without a rule in the schema →
extra_property(only flagged once the schema declares at least one property). - An event name with no plan row at all →
unknown_event, recorded as soft/accepted so SDK drift shows up without costing you events.
2. Prerequisites
- Access. An API key for your workspace, or a dashboard session with the owner, admin, or developer role. Analyst and marketer roles cannot edit the plan — a strict-mode flip changes whether customer events are accepted, so the write side is restricted.
- An events list. The event names you want to govern. Start with the two or three that drive critical segments or flows, not the whole catalog.
- A naming convention for
event_name. It must match theeventstring in your SDK exactly, including case.
3. Declare an event
POST /api/v1/cdp/tracking-plan upserts a plan row, so the same call creates and later updates a row:
* only included when you sent them — description defaults to null):
- Required properties can come from
properties_schema.required, from a top-levelrequired_propertiesarray, or both — the server merges, deduplicates, and stores them sorted. - New events almost always start in
softmode. Watch the violations feed for a few days, then flip tostrictonce the producers are clean. - Schema fields beyond
properties,required, and the governanceclassificationblock are stored but ignored by the validator, so you can paste a fuller JSON Schema and get forward-compat storage.
4. List, update, and delete
List the catalog (keyset-paginated — walkpagination.cursor):
event_name — the request above with "enforcement": "strict" flips enforcement in place. Delete stops validation for that event name (historical violations are kept):
5. Soft vs strict enforcement
The mode decides what happens when a violation fires on a matched plan row:- Soft — the event is accepted and stored as usual, and the violation is recorded for you to review. Producers drift without losing data.
- Strict — the event is rejected with HTTP 422 and the violation is recorded with
was_rejected: true. Nothing untyped enterscdp_events, so segments and models stay pure.
- Ship new events in
soft. The feed tells you whether your SDK matches what you declared before you arm the gate. - Flip to
strictonly for events whose producers you control end-to-end. A customer-side SDK on a mobile version you cannot force-update will 422 real purchases if the plan and SDK disagree.
6. Reading the violations feed
GET /api/v1/cdp/tracking-plan/violations is newest-first and filterable by event_name and enforcement, with a cursor for paging:
was_rejected tells you whether the event was dropped (strict) or stored anyway (soft). In the dashboard, the Violations tab on the Tracking Plan page shows the same stream with event-name and enforcement filters.
7. Plan-as-code in CI/CD
Treat the plan like any other infrastructure — a JSON file in your repo, applied on deploy:POST is an upsert, running the script twice is a no-op when nothing changed. A common pipeline: authors edit the JSON in a pull request, CI posts it on merge, and the violations feed confirms the schema and producers agree before anyone flips enforcement to strict. Keep curl exit codes checked so a 422 on the plan itself fails the deploy.
8. Troubleshooting
Events rejected with 422 in strict mode, unexpectedly. A producer is sending a payload that diverges from the plan. Open the violations feed filtered to that event — theviolation_path and actual fields name the offending property. Either fix the SDK payload, or widen the schema (add the property rule, extend the enum) if the new shape is legitimate. If you cannot fix the producer immediately, flip the event back to soft to stop the data loss while you triage.
missing_required across every event of a type. Usually a producer version that stopped sending the property (a refactor renamed it) or sent it as null. Compare the actual payloads; the fix is on the producer side.
extra_property noise. Your SDK sends something the plan does not declare. Add a rule for it — optional properties only need a type entry under properties.
Schema evolution. Adding new optional properties to a payload is always safe. Removing a property, narrowing a type, or changing a value set are breaking changes: coordinate the plan edit with the producer rollout, and under soft enforcement watch the feed while both versions are live.
unknown_event entries. An event arrived that no row governs. Either declare a row for it, or fix the caller’s event-name casing/spelling — the match is case-sensitive.
See also
- CDP audiences — the segments and scores your plan rows protect
- CDP predictive models — models trained on the events the plan validates
- CDP API reference — tracking events, event schemas, and data catalog endpoints
- CDP event model — how
track,page, andscreenpayloads are shaped