Reten Docs

Validate

Validates an event filter definition with a BigQuery dry-run and returns the estimated cost, without running the query.

POST /api/event-criterias/validate-from-body

Validates an event filter definition: it compiles it to SQL and submits it to a BigQuery dry-run, which checks the query against the real schema and estimates its cost without running it.

Auth: Required — event-criteria:view-sql permission

There is no POST /event-criterias/:id/validate. Audiences have the by-id variant; this resource only validates from the body. To validate a saved filter, get its definition with GET /:id and send its queryBlocks to this route.

Request Body

FieldTypeRequiredDescription
queryBlocksQueryBlockDto[]YesAt least 1 element. The definition to validate.

The structure of each block is in Definition.

This endpoint receives only the definition: name, description and format are not part of the body and sending them produces a 400 for unknown property.

Example

curl -X POST https://api.reten.ai/api/event-criterias/validate-from-body \
  -H "Authorization: Bearer <token>" \
  -H "x-tenant-id: <tenant-id>" \
  -H "x-project-id: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "queryBlocks": [
      {
        "order": 0,
        "from": { "table": "event_transactions", "joins": [] },
        "select": {
          "distinct": true,
          "items": [
            { "kind": "column", "column": { "table": "event_transactions", "column": "event_id" } }
          ]
        },
        "where": {
          "kind": "group",
          "logicalOperator": "AND",
          "children": [
            {
              "kind": "predicate",
              "column": { "table": "event_transactions", "column": "status" },
              "comparisonOperator": "EQUALS",
              "value": "Facturada"
            }
          ]
        }
      }
    ]
  }'
import axios from 'axios';

const body = {
  queryBlocks: [
    {
      order: 0,
      from: { table: 'event_transactions', joins: [] },
      select: {
        distinct: true,
        items: [
          { kind: 'column', column: { table: 'event_transactions', column: 'event_id' } },
        ],
      },
      where: {
        kind: 'group',
        logicalOperator: 'AND',
        children: [
          {
            kind: 'predicate',
            column: { table: 'event_transactions', column: 'status' },
            comparisonOperator: 'EQUALS',
            value: 'Facturada',
          },
        ],
      },
    },
  ],
};

const response = await axios.post(
  'https://api.reten.ai/api/event-criterias/validate-from-body',
  body,
  { headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const { valid, errors, bytesProcessed } = response.data;

Response

Status 201 Created. The body reports the result of the dry-run:

FieldTypeRequiredDescription
validbooleanYestrue if the dry-run succeeded; false if BigQuery rejected the query.
errorsstring[]NoPresent only when valid: false. Contains the BigQuery error message(s).
bytesProcessednumberYesBytes BigQuery estimates it will process. On success, the dry-run estimate; on failure, 0.

The dry-run does not execute the query or return rows: it only validates it against BigQuery and estimates its cost. To get a sample of real rows use Preview; to see the generated SQL, Generate SQL.

errors is an array by contract, but in practice the dry-run returns a single message (the one BigQuery reports). Always discriminate by valid before reading errors.

Example — valid (valid: true)

{
  "valid": true,
  "bytesProcessed": 10485760
}

With valid: true, errors is omitted and bytesProcessed carries the BigQuery estimate (in this example, ~10 MB).

Example — invalid (valid: false)

The definition passed structural and business validation and compiled to SQL, but the BigQuery dry-run rejected it. Even so, the status is 201:

{
  "valid": false,
  "errors": [
    "Unrecognized name: legacy_column at [3:8]"
  ],
  "bytesProcessed": 0
}

Structural validation vs. dry-run

This is the key point of the endpoint: not all failures reach errors[]. A request goes through stages and each one fails differently.

StageHow it failsWhere it shows
1. DTO validation (structure, types, enums)HTTP 400 errormessage (array of strings); stops the request.
2. Business validation (catalog, aggregation, time)HTTP 400 errormessage (string); stops the request.
3. BigQuery dry-runvalid: false (status 201)errors field of the response body.

Do not assume that an "invalid definition" always arrives as valid: false. Most shape and business errors are HTTP 400 errors and never reach the dry-run. valid: false specifically means that BigQuery rejected a query that had already passed all local validation.

Validation Rules

Before the dry-run, the definition goes through the two layers of validation. The full catalog of rules and their exact messages is in Definition → Validation Rules.

Error Responses

These are the HTTP errors (distinct from valid: false, which travels with status 201):

StatusDescription
400Invalid DTO (missing field, wrong type, enum out of range, unknown property) or a broken business rule.
401Missing or invalid Authorization: Bearer.
403The user lacks the event-criteria:view-sql permission, or the x-tenant-id is not authorized.
404The tenant has no dataset configured (Dataset not configured for tenant <id>).