---
title: "Check a formula before saving it"
method: POST
path: "/v1/tables/{tableId}/fields/preview"
tags: ["Tables"]
---

# Check a formula before saving it

`POST /v1/tables/{tableId}/fields/preview`

## Request

- Base URL: `https://api.banksync.io`
- URL: `https://api.banksync.io/v1/tables/{tableId}/fields/preview`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Compile a formula (`source`) or a Lookup / Rollup / Count (`preset`) against the workspace schema WITHOUT saving it, and evaluate it against up to three real rows. Answers 200 either way: `ok: false` with `issues` IS the answer to "is this valid?". Same evaluator the write runs, so a preview cannot disagree with the field it produces.

## Path parameters

- `tableId` string, required: Table ID

## Request body

- object
  - `source` string
  - `preset` union
    - object
      - `preset` 'count', required
      - `relationFieldId` string, required
      - `condition` union: A filter tree shared by tables, dashboards and widgets. A clause names a field by key (or one hop through a relation as `relation.field`), an operator, and a value; groups combine clauses with and / or / not.
        - union
          - object
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'and', required
            - `conditions` FilterNode[], required
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'or', required
            - `conditions` FilterNode[], required
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
        - union
          - object
            - `field` string, required
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean
            - `optional` boolean
            - `logic` 'and' | 'or', required
            - `conditions` FilterNode[], required
          - object
            - `logic` 'and', required
            - `conditions` FilterNode[], required
          - object
            - `logic` 'or', required
            - `conditions` FilterNode[], required
        - union
          - object
            - `field` string, required
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean
            - `optional` boolean
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
          - object
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
    - object
      - `preset` 'lookup', required
      - `relationFieldId` string, required
      - `targetFieldId` string, required
      - `condition` union: A filter tree shared by tables, dashboards and widgets. A clause names a field by key (or one hop through a relation as `relation.field`), an operator, and a value; groups combine clauses with and / or / not.
        - union
          - object
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'and', required
            - `conditions` FilterNode[], required
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'or', required
            - `conditions` FilterNode[], required
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
        - union
          - object
            - `field` string, required
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean
            - `optional` boolean
            - `logic` 'and' | 'or', required
            - `conditions` FilterNode[], required
          - object
            - `logic` 'and', required
            - `conditions` FilterNode[], required
          - object
            - `logic` 'or', required
            - `conditions` FilterNode[], required
        - union
          - object
            - `field` string, required
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean
            - `optional` boolean
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
          - object
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
      - `limit` integer
    - object
      - `preset` 'rollup', required
      - `relationFieldId` string, required
      - `targetFieldId` string
      - `aggregation` 'show_original' | 'show_unique' | 'count_all' | 'count_values' | 'count_unique' | 'count_empty' | 'count_not_empty' | 'percent_empty' | 'percent_not_empty' | 'sum' | 'average' | 'median' | 'min' | 'max' | 'range' | 'stddev' | 'stddev_pop' | 'variance' | 'variance_pop' | 'earliest' | 'latest' | 'date_range' | 'checked' | 'unchecked' | 'percent_checked' | 'percent_unchecked' | 'all' | 'any' | 'count_per_option' | 'percent_per_option' | 'join', required
      - `condition` union: A filter tree shared by tables, dashboards and widgets. A clause names a field by key (or one hop through a relation as `relation.field`), an operator, and a value; groups combine clauses with and / or / not.
        - union
          - object
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'and', required
            - `conditions` FilterNode[], required
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'or', required
            - `conditions` FilterNode[], required
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
          - object
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
            - `field` string, required: Field key, or `relation.field` for one hop.
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean: Server-bound clause: cannot be dropped by a viewer and is enforced at the data plane.
            - `optional` boolean: Skip the clause (treat as passing) for rows where `field` is absent, as opposed to present-but-null. Default false.
        - union
          - object
            - `field` string, required
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean
            - `optional` boolean
            - `logic` 'and' | 'or', required
            - `conditions` FilterNode[], required
          - object
            - `logic` 'and', required
            - `conditions` FilterNode[], required
          - object
            - `logic` 'or', required
            - `conditions` FilterNode[], required
        - union
          - object
            - `field` string, required
            - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'starts-with' | 'ends-with' | 'between' | 'is-null' | 'is-not-null' | 'matches' | 'has-passed' | 'not-passed', required
            - `value` union
              - …
            - `security` boolean
            - `optional` boolean
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
          - object
            - `logic` 'not', required
            - `condition` FilterNode, required (recursive)
      - `separator` string
    - object
      - `preset` 'thread', required
      - `aggregate` 'last_message_at' | 'first_message_at' | 'last_side' | 'last_persona' | 'last_channel' | 'count' | 'unanswered_since' | 'reaction_count', required
      - `match` object
        - `side` string[]
        - `channel` string[]
        - `role` string[]
        - `audience` string[]
        - `published` boolean
      - `reactionKind` string
  - `fieldId` string
  - `sampleRows` integer

## Response `200`

Compilation result, with a sampled evaluation when the formula is valid

- object
  - `success` true, required
  - `ok` boolean, required
  - `resultKind` object
    - `base` 'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'option_counts' | 'currency_totals' | 'record' | 'attachment' | 'user' | 'null', required
    - `list` boolean
  - `volatile` boolean
  - `depth` integer
  - `issues` object[], required
    - `message` string, required
    - `code` 'syntax' | 'unknown_function' | 'arity' | 'type' | 'unknown_field' | 'self_reference' | 'cycle' | 'depth' | 'complexity' | 'unsupported' | 'volatile_scope', required
    - `start` integer
    - `end` integer
  - `rows` object[], required
    - `rowId` string, required
    - `primary` string, nullable, required
    - `value` unknown
    - `error` 'division_by_zero' | 'invalid_value' | 'missing_reference' | 'type_mismatch' | 'complexity_limit' | 'depth_limit' | 'link_limit' | 'unavailable'

## Other responses

- `400`: Validation failed, or a read-only field was written — `code` names the refusal and `details[]` the offending field or cell
- `401`: Unauthorized
- `403`: Forbidden — missing scope, insufficient role, or plan limit reached
- `404`: Not found

---

[API](https://skmtc.dev/banksync/apis/banksync-api.md) · [All operations](https://skmtc.dev/banksync/apis/banksync-api/llms.txt) · [OpenAPI document](https://skmtc.dev/banksync/apis/banksync-api/revisions/94465f5ee922?raw)
