Skip to main content
Routing decides which dataset an event lands in, and which fields its record has. What decides depends only on how the event was sent:
Rules run on exactly one shape: POST /v1/ingest/{ingest_key} with no dataset in the path. An endpoint URL never consults them, so adding a rule and then posting to an endpoint changes nothing. Rules are for a sender that cannot say where each event belongs.

Rules

A rule has a name, a dataset, conditions, mappings and a status. For an event sent with an ingest key and no dataset, every enabled rule in the key’s project is tried:
  • Every rule that matches writes its own record, so one event can land in several datasets. Rules are not first-match.
  • When none matches, no record is written. The answer says "routed": [], and the submission is kept as Pending (no rule matched yet), so an event nobody routed is never mistaken for one that never arrived.
  • No conditions matches every event, and no mappings stores the payload whole.
For example, this event:
matches this rule, which stores {"order_id": 1042, "amount": 59.9} in the refunds dataset:
A rule holds up to 10 conditions and 50 mappings.

Conditions

Every condition in a list must hold. A condition reads a dotted path into the event as it was sent, such as customer.tier, and tests it with one of ten operators:
  • Rules compare as text. In rules and endpoint criteria, equals, not_equals and in compare as text, so 5 and "5" match. Workflows and triggers compare those three exactly, type included.
  • Numbers sent as text still compare. gt, gte, lt and lte read a field that is a number, or text that spells a plain decimal number: an optional + or -, digits and an optional fraction, with spaces around it ignored. So a form’s "150" is 150. Any other text never matches, including hex ("0x10"), exponents ("1e3"), thousands separators ("1,000") and empty text. The value you compare against must be a number.
  • Text stays text. contains and regex match only a field that is text.
A condition that could never match, such as an unknown operator or gt against a value that is not a number, is refused with 400 when you save it. As in the example above, an exact match is stored in the equals shorthand, and every other operator with op and value.

Mappings

A mapping copies one field of the event into the record under a key you choose: {"path": "customer.email", "key": "email"}. A key starts with a letter, uses only letters, digits and underscores, and is used once. Three special paths exist: $payload (the whole event), $submission.id and $submission.received_at. A path the event lacks leaves its key out of the record.

Criteria on endpoints

An endpoint applies its own conditions, called criteria, and its own mappings to everything sent to it. They work like a rule’s, with three differences:
  • The dataset is always the endpoint’s own.
  • An event that fails the criteria is kept as a submission and makes no record.
  • They are set when the endpoint is created over the API, MCP or the CLI, and changed only by a new version. The console’s Add endpoint sets neither.
An endpoint accepts at most 10 criteria, the same limit as a rule’s conditions, and 50 mappings. Project rules are never tried on an endpoint.

Build and test rules in the console

On a project’s Rules tab, choose Add rule. Until a project has a rule, the tab’s empty state says the same as the warning above: rules run only on POST /v1/ingest/{ingest_key} with no dataset in the URL, and endpoint URLs do not run rules. The editor has a Name, a Dataset picker (one of the project’s datasets, or a new name), a row per condition with its Path, Operator and value, a row per mapping with its Payload path and Record key, and a Status switch. A rule with no conditions is listed as matches everything. Test against a sample event runs the rule on a payload, prefilled with the project’s most recent event, and stores nothing. Test rule answers Matched, with the record the rule would store, or Not matched. Each rule’s menu has Edit, Duplicate, Enable or Disable, and Delete, which keeps the records the rule stored. Owners, admins and developers can change rules. Over the API, rules live at /admin/api/projects/{project_id}/rules, and POST …/rules/test with {conditions, mappings, payload} answers {matched, record} without writing anything. Connected agents use create_rule, update_rule and delete_rule.