---
title: "Validate every event before you store it · Agent Wranglers"
url: https://agentwranglers.se/playbook/data/product-analytics/event-validation
updated: 2026-10-20
licence: "All rights reserved"
---

# Validate every event before you store it

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Product analytics](https://agentwranglers.se/playbook/data/product-analytics)
- Rule: Check each incoming event against its schema at the collector. Store valid events, and set invalid ones aside with the reason.
- How: Compile the tracking plan into JSON Schema 2020-12 validators, such as Ajv's `Ajv2020` class or Python's `jsonschema`. Reject unknown events, unknown properties and wrong types. Write rejects to a separate table with the error and the app version, as with a [dead letter queue](https://agentwranglers.se/playbook/data/transactions-and-concurrency/dead-letter-queue). Alert when rejects pass 1 percent of events in an hour, because a release has broken tracking.
- Check: Send one valid and one invalid event to the staging collector. The valid one is in the events table, and the invalid one is in the rejects table with its error.

Example:

```js
import Ajv2020 from 'ajv/dist/2020.js';

const ajv = new Ajv2020({ allErrors: true });
const validators = Object.fromEntries(Object.entries(EVENT_SCHEMAS).map(([name, schema]) => [name, ajv.compile(schema)]));

export async function collect(event) {
  const validate = validators[event.name];
  if (validate?.(event.properties)) return db.events.insert(event);
  return db.eventRejects.insert({ event, error: validate ? ajv.errorsText(validate.errors) : 'unknown event' });
}
```

```python
from jsonschema import Draft202012Validator

VALIDATORS = {name: Draft202012Validator(schema) for name, schema in EVENT_SCHEMAS.items()}

def collect(event: dict):
    validator = VALIDATORS.get(event["name"])
    errors = [e.message for e in validator.iter_errors(event["properties"])] if validator else ["unknown event"]
    if errors:
        return db.event_rejects.insert(event=event, error="; ".join(errors))
    return db.events.insert(event)
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers data playbook: https://agentwranglers.se/playbook/data/product-analytics/event-validation`.

- Previous: [Keep a tracking plan in the code](https://agentwranglers.se/playbook/data/product-analytics/tracking-plan)
- Next: [Track only after consent](https://agentwranglers.se/playbook/data/product-analytics/analytics-consent)
- [Product analytics](https://agentwranglers.se/playbook/data/product-analytics)
