> ## Documentation Index
> Fetch the complete documentation index at: https://docs.textql.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Config File Reference

> Syntax, references, and every field of playbook, agent, dashboard, and data app config files.

> [!NOTE]
>
> **Availability:** Config files are a **Beta** feature. See [Config Files](https://docs.textql.com/core/ontology/config-files.md) for how they work.

## Syntax

Every config file is one record: a constructor name followed by fields in braces.

```text theme={null}
Playbook {
  -- comments start with two dashes
  name = "Weekly revenue recap",
  run_as = "sa:reporting-bot@acme.com",
  cron_string = "0 13 * * 1",
  tools = ["sql", "python"],
  files = [
    { path = "regions.csv", alias = "regions" }
  ],
  enabled = true
}
```

* Config files are **not JSON**. Field names are bare (never quoted), and values use `=`, never `:`.
* Comments start with `--` and run to the end of the line. `//` and `#` are errors.
* Fields and list items are separated by commas. A trailing comma after the last item is an error.
* Strings are double-quoted, with backslash escapes. Booleans are lowercase `true` and `false`.
* Lists use `[ ]` and nested records use `{ }`.
* A field you leave out takes its default. Unknown fields and values outside a field's allowed list are errors.

## References

Config files refer to other things in a few fixed forms.

| Refers to | Written as | Example |
| - | - | - |
| A member | `user:<email>` | `"user:alice@acme.com"` |
| A service account | `sa:<email>` | `"sa:reporting-bot@acme.com"` |
| A role | `role:<name>` | `"role:Finance"` |
| A database connector | `id:<number>` | `"id:7"` |
| A dashboard, template, API connector, or MCP server | `id:<uuid>` | `"id:8f1c9d2e-0b7a-4c3d-9e5f-1a2b3c4d5e6f"` |
| An Ontology file | a path | `"prompt.md"`, `"../shared/regions.csv"`, `"/finance/regions.csv"` |

Paths work like this, unless a field says otherwise:

* A bare name is a file beside the config file.
* A path without a leading slash is relative to the config file's folder.
* A path with a leading slash starts at the root of the Ontology.

Members are always written by email address, not by id.

### Prompt files

A playbook or agent sets its instructions with exactly one of `prompt` or `prompt_file`. Use `prompt` only for a prompt that fits on one line; anything longer goes in a markdown file that `prompt_file` points to. A long inline prompt is flagged by a check and moved into a file by [automatic fixes](https://docs.textql.com/core/ontology/config-files.md#automatic-fixes). Add or change the prompt file in the same change as the config file.

### Attached files

Playbooks, agents, and dashboards can attach Ontology files to every run with `files`, a list of `{ path, alias }` records. `alias` is optional.

```text theme={null}
files = [
  { path = "regions.csv", alias = "regions" },
  { path = "/finance/fiscal-calendar.md" }
]
```

## Playbook

File: `<name>.playbook`. Constructor: `Playbook`.

```text playbooks/weekly-revenue/weekly-revenue.playbook theme={null}
Playbook {
  name = "Weekly revenue recap",
  prompt_file = "prompt.md",
  run_as = "sa:reporting-bot@acme.com",
  cron_string = "0 13 * * 1",
  schedule_timezone = "America/New_York",
  connectors = ["id:7"],
  tools = ["sql", "python"],
  report_output_style = "concise",
  slack_channel = "C12345",
  slack_mentions = ["user:alice@acme.com"]
}
```

| Field | Description |
| - | - |
| `name` *(required)* | Display name. |
| `prompt` | The prompt inline, for a prompt that fits on one line. Set exactly one of `prompt` and `prompt_file`. |
| `prompt_file` | Path to a markdown file holding the prompt. Set exactly one of `prompt` and `prompt_file`. |
| `run_as` *(required)* | Who the playbook runs as: `sa:<email>` or `user:<email>`. |
| `cron_string` *(required)* | 5-field cron schedule. May not fire more often than your org's minimum schedule interval. |
| `schedule_timezone` | IANA time zone for `cron_string`, such as `America/New_York`. Default: UTC. |
| `enabled` | Set `false` to stop scheduled runs. Default: `true`. |
| `connectors` | Database connectors, each `id:<number>`. |
| `tools` | Any of `sql`, `python`, `web_search`, `ontology`, `bash`, `javascript`. Default: `["sql", "python"]`. |
| `report_output_style` | `executive`, `verbose`, or `concise`. Default: `executive`. |
| `model` | A model name, or a tier: `default`, `default-small`, `default-large`, `default-reasoning`. Default: `default`, which follows your org's default model. |
| `harness` | `ana`, `codex`, or `claude_code`. Default: the runner's own default. |
| `email_recipients` | Email addresses that receive each report. |
| `slack_channel` | Slack channel id to post the report in. Can't be combined with `slack_dm`. |
| `slack_mentions` | Members to mention in the Slack post, each `user:<email>`. Requires `slack_channel`. |
| `slack_dm` | One member to direct-message instead, `user:<email>`. |
| `teams_channel` | Teams channel to post the report in. |
| `teams_mentions` | Members to mention in the Teams post, each `user:<email>`. |
| `files` | [Attached files](#attached-files). |
| `dashboards` | Dashboards attached to each run, each `id:<uuid>`. |
| `api_connectors` | API connectors, each `id:<uuid>`. |
| `api_connector_scope` | `all` (every API connector the runner can use), `selected` (exactly `api_connectors`), or `none`. Left out, a non-empty `api_connectors` is the selection and an empty one means all. |
| `mcp_servers` | MCP servers, each `id:<uuid>`. |
| `mcp_server_scope` | `all`, `selected` (exactly `mcp_servers`), or `none`. Default: `all`. |
| `template` | A template to loop over, `id:<uuid>`. Left out, the playbook runs once per scheduled time. |
| `template_rows` | Template rows to run for, each `id:<uuid>`. Requires `template`. Default: every row. |
| `template_filters` | Row conditions a row must all meet, each `{ field, operator, value }`. Requires `template`. |
| `recipient_email_column` | Template column holding each row's email recipient. Requires `template`. |
| `access` | Who may read the Threads the playbook's runs produce. See below. Left out, the Threads inherit their usual access. |

`access` controls the run Threads, not the file:

```text theme={null}
access = {
  visibility = "restricted",
  grants = [
    { to = "role:Finance", level = "viewer" },
    { to = "user:analyst@acme.com", level = "owner" }
  ]
}
```

`level` is `viewer`, `editor`, or `owner`. Only a member can be an `owner`, not a role, and each member or role may appear once.

## Agent

File: `<name>.agent`. Constructor: `Agent`.

```text agents/revenue-watcher/revenue-watcher.agent theme={null}
Agent {
  name = "Revenue watcher",
  prompt_file = "prompt.md",
  run_as = "sa:revenue-bot@acme.com",
  connectors = ["id:7"],
  tools = ["sql", "python"],
  methodology = "thorough",
  schedules = [
    { cron = "0 13 * * 1", timezone = "America/New_York" },
    { cadence = "DAILY" }
  ],
  slack_channel = "C12345",
  slack_trigger = { enabled = true, channels = ["C12345"] }
}
```

| Field | Description |
| - | - |
| `name` *(required)* | Display name. Other agents refer to this agent by it in `subagents`. |
| `prompt` | The prompt inline, for a prompt that fits on one line. Set exactly one of `prompt` and `prompt_file`. |
| `prompt_file` | Path to a markdown file holding the prompt. Set exactly one of `prompt` and `prompt_file`. |
| `run_as` *(required)* | Who the agent runs as: `sa:<email>` or `user:<email>`. |
| `enabled` | Default: `true`. |
| `connectors` | Database connectors, each `id:<number>`. |
| `tools` | Any of `sql`, `python`, `web_search`, `ontology`, `bash`, `email`, `javascript`, `context_editing`, `ontology_editing`. Unlike a playbook, an agent gets **no** tools unless you list them. `email` lets the agent send email itself. |
| `api_connectors` | API connectors, each `id:<uuid>`. |
| `api_connector_scope` | `all`, `selected` (exactly `api_connectors`, even an empty list), or `none`. Left out, a non-empty `api_connectors` is the selection, and an empty or missing one keeps the agent's saved selection; use `none` to clear it. |
| `mcp_servers` | MCP servers, each `id:<uuid>`. |
| `mcp_server_scope` | `all`, `selected`, or `none`. Default: `all`. |
| `model` | As for playbooks. Default: `default`. |
| `methodology` | `adaptive`, `prescriptive`, `thorough`, `careful`, or `ontology_building`. Default: `adaptive`. |
| `fast_mode` | Faster inference, where your org allows it and the model supports it. Otherwise it stays off. Default: `false`. |
| `auto_approve_edits` | Apply the Ontology edits the agent proposes without review. Default: `false`. |
| `schedules` | When the agent posts unprompted. Each entry is `{ cron, timezone }` or `{ cadence }`, where `cadence` is `HOURLY`, `FOUR-HOUR`, `EIGHT-HOUR`, `DAILY`, or `WEEKLY`. Left out, the agent runs only when addressed. |
| `slack_channel` | Slack channel the agent posts in. |
| `teams_channel` | Teams channel the agent posts in. |
| `skip_org_default_channel` | Don't also post to your org's default channel. Default: `false`. |
| `slack_trigger` | Let the agent answer when mentioned in Slack: `{ enabled, workspace, channels }`. `workspace` is a Slack workspace id (left out: every workspace); `channels` is a list of channel names or ids (empty: every channel). |
| `email_output` | Email each run's result to `email_recipients`. Default: `false`. |
| `email_recipients` | Member email addresses. |
| `slack_dm_recipients` | Members to direct-message on Slack, each `user:<email>`. |
| `teams_dm_recipients` | Members to direct-message on Teams, each `user:<email>`. |
| `feed` | Whether the agent posts to the feed. Default: `true`. |
| `feed_channels` | Feed channels to post in, by name. |
| `memory` | Folder for the agent's persistent memory, always written from the Ontology root. Setting it turns memory on; left out, the agent has no memory. |
| `subagents` | Agents this one may delegate to, by name. Each must set `callable_as_subagent = true`. |
| `callable_as_subagent` | Whether other agents may delegate to this one. Default: `false`. |
| `subagent_invokers` | Who may call this agent as a subagent, each `user:<email>` or `role:<name>`. Left out, anyone otherwise eligible may. |
| `allow_ad_hoc_subagents` | Whether the agent may start subagents beyond its `subagents` list. Default: `false`. |
| `files` | [Attached files](#attached-files). |
| `dashboards` | Dashboards attached to each run, each `id:<uuid>`. |

`email_recipients`, `slack_dm_recipients`, `teams_dm_recipients`, `feed_channels`, `subagents`, and `subagent_invokers` keep only the entries that match. An empty or omitted list clears the saved list. If a non-empty list has no matches, the agent's saved list is left as it was. `slack_trigger.channels` is different: an empty list means every channel.

## Dashboard

File: `<name>.dashboard`. Constructor: `Dashboard`. The dashboard's code lives beside it.

```text dashboards/sales-overview/sales-overview.dashboard theme={null}
Dashboard {
  name = "Sales overview",
  run_as = "sa:reporting-bot@acme.com",
  description = "Pipeline and bookings by region.",
  type = "dash",
  entrypoint = "app.py",
  data_sources = [
    { type = "sql_query", name = "bookings", connector = "id:12", query = "SELECT region, amount FROM bookings" }
  ],
  cron_string = "0 9 * * *",
  schedule_timezone = "America/New_York"
}
```

| Field | Description |
| - | - |
| `name` *(required)* | Display name. |
| `run_as` *(required)* | Whose access the dashboard's data uses: `sa:<email>` or `user:<email>`. |
| `type` *(required)* | `streamlit` or `dash`. |
| `entrypoint` *(required)* | Path to the dashboard's main code file. |
| `description` | A short description. |
| `includes` | Extra code files, as paths or globs, always written from the Ontology root. |
| `data_sources` | Data the dashboard loads. See below. |
| `files` | [Attached files](#attached-files). |
| `cron_string` | 5-field cron schedule for refreshes. Left out, the dashboard has no schedule. |
| `schedule_timezone` | IANA time zone for `cron_string`. Default: UTC. |
| `enabled` | Default: `true`. |
| `capabilities` | Platform actions the code may call. See [Capabilities](#capabilities). |

Each data source has a `type` and a `name`, plus the fields its type needs:

| `type` | Also requires | Optional |
| - | - | - |
| `sql_query` | `connector` (`id:<number>`), `query` | `dataset`, `params_json`, `parameters` |
| `ontology_sql` | `ontology` (`id:<number>`), `query` | `params_json`, `parameters` |
| `library_tql` | `tql_path` (Ontology path to a `.tql` file) | `params_json`, `parameters` |
| `python_code` | `path` (the Python file) | |
| `powerbi_dax` | `connector`, `query` (the DAX), `workspace_id`, `dataset_id` | `parameters` |

`parameters` declares live query parameters, each `{ name, type, default }`; only `name` is required.

## Data App

File: `app.dataapp`, in a folder of its own under `apps/`. Constructor: `DataApp`. The folder is the app: its code, queries, and assets live beside the file, and other config files inside it are not read as objects.

```text apps/quarterly-revenue/app.dataapp theme={null}
DataApp {
  name = "Quarterly Revenue",
  run_as = "sa:reporting-bot@acme.com",
  description = "Revenue rollups by quarter and region.",
  schedule = { enabled = true, cron = "0 6 * * 1", timezone = "America/New_York" },
  sources = [
    { name = "revenue_rows", type = "sql_query", path = "sources/revenue_rows.sql", connector = "id:42" }
  ],
  functions = [
    { name = "top_regions", description = "Rank regions by revenue.", path = "compute/top_regions.py", returns = "object" }
  ],
  jobs = [
    { name = "weekly_digest", function = "top_regions", cron = "0 9 * * 1", timezone = "America/New_York" }
  ]
}
```

| Field | Description |
| - | - |
| `name` *(required)* | Display name. |
| `run_as` *(required)* | Whose access the app's data uses: `sa:<email>` or `user:<email>`. Required on any new or edited app. |
| `description` | A short description. |
| `root_path` | Folder holding the app's content. Default: the folder `app.dataapp` is in. |
| `schedule` | Refresh schedule: `{ enabled, cron, timezone }`. Default: off. |
| `staleness_seconds` | How old cached data may get, in seconds. |
| `query_cache` | Shared cache for live viewer queries, off by default: `{ enabled, ttl_seconds }`, with `ttl_seconds` from 1 to 900 (default 60). |
| `app_db_setup` | SQL statements that set up the app's private database, run in order. Add new statements to the end; don't edit ones that already ran. |
| `sources` | Data the app loads. See below. |
| `functions` | Functions the app's code can call. See below. |
| `compute` | Worker settings. See below. |
| `jobs` | Background jobs. See below. |
| `design_system` | The design system the app uses, by its folder from the Ontology root, such as `/design-systems/acme`. Left out, the app uses your org's brand color. |
| `capabilities` | Platform actions the code may call. See [Capabilities](#capabilities). |

`path` values in `sources` and `functions` are relative to the app's content root: `root_path` when set, otherwise the folder holding `app.dataapp`.

**Sources.** Each has a `name` and a `type`:

| `type` | Also requires | Optional |
| - | - | - |
| `sql_query` | `path` (a `.sql` file), `connector` | `params_json`, `parameters` |
| `library_tql` | `path`, `connector` | `params_json`, `parameters` |
| `python_code` | `path` | |
| `file` | `file_name`, `dataset_id` | `sheet_index` (zero-based, for spreadsheets) |
| `powerbi_dax` | `path`, `connector`, `workspace_id`, `dataset_id` | `parameters` |

**Functions.** Each has a `name` and exactly one body:

* `path`: a file in the app's folder. The extension picks the kind: `.py` for Python, `.sql` for SQL against the app's private database, `.tql` for TQL. A `.tql` body also takes a `connector`, where `id:-1` means the app's private database.
* `tql_path`: a saved `.tql` file elsewhere in the Ontology.
* `server_handled = true`: handled by a function of the same name in the app's server module.

Optional: `description`, `returns`, and `parameters` (each `{ name, type, description }`).

**Compute.**

| Field | Description |
| - | - |
| `manual_spawn_only` | Start a worker only when the app calls a function, not when someone opens it. Default: `false`. |
| `keep_warm_seconds` | How long a worker stays up after the last view. Default: your org's setting. |
| `max_replicas` | Most workers the app may scale to under load, from 1 to 16. Takes effect only once multiple workers are turned on for your org; contact your TextQL representative. Default: 1. |
| `timeout_seconds` | Time limit for one run of the app's code, from 60 to 1800 seconds. Default: 600. |

**Jobs.** Each job calls one of the app's functions:

| Field | Description |
| - | - |
| `name` *(required)* | A Python identifier, unique among the app's jobs. |
| `function` *(required)* | The function to call. |
| `description` | A short description. |
| `params_json` | JSON object passed to the function. |
| `enabled` | Default: `true`. |
| `cron` | Run on this schedule. Can't be combined with `after_refresh`. |
| `timezone` | IANA time zone for `cron`. Default: UTC. |
| `after_refresh` | Run after each refresh of the app. Default: `false`. |
| `per_recipient` | Run once for each recipient, as that member. Requires `recipients`. Default: `false`. |
| `recipients` | `{ member_emails, roles, subscribers }`. |
| `timeout_seconds` | Time limit in seconds, 60 to 1800. Default: the app's `compute.timeout_seconds`. |

A job with neither `cron` nor `after_refresh` runs only when started by hand.

## Capabilities

Dashboards and data apps list the platform actions their code may call in `capabilities`. Each entry has a `type`, and a `name` the code calls it by (not needed for `state` and `assets`).

| `type` | What it allows | Requires |
| - | - | - |
| `write` | Run one SQL statement, with `:name` placeholders | `connector`, `statement` |
| `state` | Keep saved state | `scope`: `app` (shared) or `viewer` (per member) |
| `assets` | Store and read files | |
| `notify` | Send a message | `subject`, `body`. Optional `audience`: `viewer` (default) or `subscribers` (data apps only). Optional `recipients_param`: with `audience = "subscribers"`, names a declared parameter carrying member emails, narrowing the message to those subscribers |
| `deliver` | Send a report (data apps only) | `subject`, `body`, `recipients`. Optional `channels`: `email`, `in_app`, `slack_channel`, `teams_channel` (default email and in-app) |
| `ask` | Ask an agent | Optional `agent`: path to an `.agent` file, such as `/agents/churn.agent` (default: your org's base agent). Optional `app_db`: `read` or `write`, letting a base-agent ask query or change the app's private database; can't be combined with `agent` |
| `decide` | Ask your org's decision model | |

Capabilities other than `ask` and `decide` can declare `parameters`, each `{ name, type, description }`; `ask` and `decide` take none. Each capability type must be available to your organization; a check flags one that isn't.

`recipients` for `deliver` takes any of `subscribers` (`true` for the app's subscribers), `member_emails`, `roles`, `runtime_emails` (`true` when the code names recipients as it sends), `slack_channel_ids` (requires `slack_channel` in `channels`), and `teams_channel_ids` (requires `teams_channel`).

```text theme={null}
capabilities = [
  {
    type = "write",
    name = "log_note",
    connector = "id:42",
    statement = "INSERT INTO notes (body) VALUES (:body)",
    parameters = [ { name = "body", type = "string" } ]
  },
  { type = "state", scope = "viewer" }
]
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.