Skip to main content
Your Adjar config can live in a single TOML file or be split across many — the loader merges everything into one logical account before any plan or apply runs, so the split is purely about how you want to read, diff, and edit. This page covers when to split, how the imports directive composes files, and the two rules that keep write-back predictable.

Single file or split?

Both are fully supported. adjar import bootstraps to a single file (or stdout) by default, so starting single and splitting later is the natural path — there’s no migration, just move blocks into new files and add an imports entry. For anything beyond a handful of campaigns:
Why this shape:
  • Diffs stay small and scoped. apply writes server-issued IDs back into the file that declared each element, so editing one campaign touches one file in git — not a 2,000-line monolith.
  • Clean boundaries for AI agents. An agent told to tune the competitor campaign edits competitor-terms.toml and can’t accidentally clobber another. One file = one unit of work.
  • Match files to change rate. [account], assets, and conversions change rarely; campaigns churn constantly. Keeping the stable sections in their own files means most diffs only hit campaigns/.
The example above is a Google Ads layout. OpenAI Ads has no asset or conversion sections, so its split is simpler — an openai.toml root (platform + [account]) importing openai/campaigns/*.toml. The mechanics below are identical.

How imports composes files

The imports array in a file lists the fragments to merge into it. See the account root reference for the field itself; the resolution rules are:
  • Glob-aware. "google/campaigns/*.toml" picks up every campaign file in the directory — adding a new campaign is just creating a new file.
  • Relative to the importing file’s own directory, not the repo root or your working directory.
  • Nesting is allowed. An imported file may itself declare imports, resolved relative to its location. Cycles are detected and rejected.
  • Deterministic merge order. Within each section, files merge in sorted order, so the result is reproducible.
Imported fragments may contain only the platform’s top-level array sections — for Google Ads that’s [[assets]], [[conversions]], and [[campaigns]]; for OpenAI Ads it’s [[campaigns]]. The singletons platform and [account] must stay in the root file, or the loader errors. This is what makes a fragment unambiguously a fragment.

Two rules that keep write-back predictable

Because apply rewrites your config to record server-issued IDs, two behaviors are worth knowing:
  1. New blocks land in the root file. When you add a campaign (or asset, or conversion) with no id and run apply, the loader has no recorded “slot” for it, so its written-back form goes to google.toml rather than into campaigns/. After the first apply, move the block into its own file — subsequent applies then keep it there.
  2. Configs are machine-edited, so comments don’t survive. Every apply re-serializes the files it touches. Inline TOML comments are dropped on write-back. Keep explanatory notes in commit messages, a README, or your agent’s SKILL.md — not inside the TOML.
  • Account Root — the platform, imports, and [account] fields in the root file, for Google Ads and OpenAI Ads
  • Introduction — why plain-text config in git is the foundation for AI-assisted ad management
  • The core workflow — how import, plan, apply, and report turn config edits into live changes