# Configuration
`braze-sync` is driven by a single YAML file, `braze-sync.config.yaml`, in
the root of your workspace. `braze-sync init` scaffolds a commented
template; this page documents every field.
The config schema is **frozen at v1.0** under the `version: 1` tag. A
future v2 schema would bump that number — v1.x binaries will only accept
`version: 1`.
## Full example
```yaml
version: 1
default_environment: dev
defaults:
rate_limit_per_minute: 40
environments:
dev:
api_endpoint: https://rest.fra-02.braze.eu
api_key_env: BRAZE_DEV_API_KEY
prod:
api_endpoint: https://rest.fra-02.braze.eu
api_key_env: BRAZE_PROD_API_KEY
rate_limit_per_minute: 30
resources:
catalog_schema:
enabled: true
path: catalogs/
catalog_items:
enabled: true
parallel_batches: 4
content_block:
enabled: true
path: content_blocks/
email_template:
enabled: true
path: email_templates/
custom_attribute:
enabled: true
path: custom_attributes/registry.yaml
naming:
catalog_name_pattern: "^[a-z][a-z0-9_]*$"
content_block_name_pattern: "^[a-zA-Z0-9_]+$"
custom_attribute_name_pattern: "^[a-z][a-z0-9_]*$"
```
## Top-level fields
### `version` (required)
Config schema version. Must be exactly `1` for every v1.x release of
`braze-sync`. Any other value is a config error (exit code `3`).
### `default_environment` (required)
Name of the environment used when `--env` is not passed on the command
line. Must match a key under `environments`.
### `defaults` (optional)
Workspace-wide defaults applied to every environment unless overridden.
| `rate_limit_per_minute` | integer | `40` | Request cap enforced client-side by a token-bucket limiter. Lower this if you see 429s, raise it if Braze has approved a higher quota for your workspace. |
### `environments` (required, at least one entry)
Map of environment name → settings. Pick the active one with
`--env <name>`; otherwise `default_environment` applies.
| `api_endpoint` | URL | yes | Braze REST endpoint for your instance (see [Braze API endpoints](https://www.braze.com/docs/api/basics/#endpoints)). The scaffold defaults to the EU `fra-02` cluster — change it if your instance lives elsewhere. |
| `api_key_env` | string | yes | Name of the **environment variable** holding the API key. The key itself must never appear in this file. |
| `rate_limit_per_minute` | integer | no | Per-environment override of `defaults.rate_limit_per_minute`. |
API keys are loaded into `secrecy::SecretString` at startup and never
appear in `Debug` output, tracing, or panic messages. A `.env` file in
the current working directory is loaded via `dotenvy` (no recursive
parent-directory search).
### `resources` (optional)
Toggles and paths for each v1.0 resource kind. Every sub-block is
optional — omitted entries fall back to the defaults shown below. To
skip a resource entirely in a workspace, set `enabled: false`.
#### `catalog_schema`
| `enabled` | bool | `true` |
| `path` | path | `catalogs/` |
Directory holding one `<catalog>/schema.yaml` per catalog.
#### `catalog_items`
| `enabled` | bool | `true` |
| `parallel_batches` | integer | `4` |
Items are read from `<catalogs.path>/<catalog>/items.csv`, streamed, and
uploaded in batches of up to 50 rows (Braze API limit). `parallel_batches`
caps how many batches are in flight at once; the overall request rate is
still bounded by `rate_limit_per_minute`.
#### `content_block`
| `enabled` | bool | `true` |
| `path` | path | `content_blocks/` |
Directory of `<name>.liquid` files.
#### `email_template`
| `enabled` | bool | `true` |
| `path` | path | `email_templates/` |
Directory holding one `<template>/` subdirectory per email template, each
containing `template.yaml`, `body.html`, and `body.txt`.
#### `custom_attribute`
| `enabled` | bool | `true` |
| `path` | path | `custom_attributes/registry.yaml` |
A single-file registry — see [registry-mode.md](registry-mode.md).
### `naming` (optional)
Optional name validators enforced by `braze-sync validate`. Each entry
is a regex evaluated by the [`regex-lite`](https://docs.rs/regex-lite)
crate — a subset of the full `regex` crate with no Unicode classes
(`\p{…}`), limited `\d`/`\w` behavior, and no look-around. Consult the
`regex-lite` syntax reference when writing patterns. Omitted patterns
mean "no check".
| `catalog_name_pattern` | Catalog names |
| `content_block_name_pattern` | Content block names |
| `custom_attribute_name_pattern` | Custom attribute names |
Naming checks exit code `3` on mismatch, the same as any other
`validate` failure.
## Strictness
The config file itself uses `#[serde(deny_unknown_fields)]` at every
level, so typos and stray keys fail fast with a pointer to the offending
line. Resource files (`schema.yaml`, `template.yaml`, the registry, etc.)
are intentionally **permissive** — unknown fields there are ignored,
preserving forward-compatibility across v1.x.