Validation
When SCHEMA_BUNDLE_PATH is set, the collector validates every incoming
event against the per-event JSON Schema from your tracking plan. Bad events
take one of two paths depending on the configured mode.
| Mode | Bad-event response | Storage | Use when |
|---|---|---|---|
Reject | 400 schema_validation_failed with structured details | Not persisted | You want producers to fix bad events before they ship. |
Quarantine | 202 Accepted | Persisted to the dead-letter queue with the validation error | You want lossless ingest with a manual review path. |
Reject is correct for most product teams: the SDK retry treats 400 as
permanent (no infinite retry loop), the structured details surface in the
SDK’s console.warn, and the bad event never pollutes your analytics.
Quarantine is correct when ingest is from third parties you cannot
quickly fix.
Validation error envelope
Section titled “Validation error envelope”In Reject mode the response body is:
{ "error": { "code": "schema_validation_failed", "message": "event failed schema validation", "details": [ { "path": "/total_cents", "message": "must be >= 0" }, { "path": "/currency", "message": "must match pattern \"^[A-Z]{3}$\"" } ] }}Paths are JSON Pointers into the event payload.
Quarantine path
Section titled “Quarantine path”In Quarantine mode bad events land in the dead-letter queue table with
their original wire JSON and the structured validation error. Operators can:
- List entries:
GET /sites/:siteId/dlq?limit=&cursor=(admin oradmin-scoped api_key). - Re-POST a single entry to
/events:POST /sites/:siteId/dlq/:eventId/replay. On a202the DLQ row is deleted; on any other collector status the row stays in place.
The replay path re-enters the validator, the consent gate, and the rate limiter — operators cannot bypass the wire contract by replaying.
See Dead-letter queue in the API reference for response shapes.
Unknown events
Section titled “Unknown events”An event whose type is not in the plan is treated as an unknown event.
Behaviour depends on mode:
Rejectreturns400 schema_validation_failedwithdetails: [{ path: "/type", message: "unknown event" }].Quarantinepersists the event to the DLQ with the same error.
If you want truly free-form custom events, declare a wildcard event in the
plan (type: object, additionalProperties: true, required: []) — but do
this knowingly, because it suppresses the schema discipline that motivates
having a plan in the first place.
What is NOT validated
Section titled “What is NOT validated”- The collector does not validate envelope fields (
id,site_id,anon_id,ts,url) against the plan. Those have their own canonical validation in@syntarie/shared/events.schema.jsonand are enforced at the wire boundary. - Customer plan changes are NOT enforced for backwards compatibility — the platform cannot decide what is breaking for your downstream consumers. That is the plan-diff tool’s job.
Performance
Section titled “Performance”Validation runs in the hot path. The platform guarantees the collector p99 ingest latency stays under 5 ms (ex-network) with validation enabled. Schema bundles are precompiled at startup.