> For the complete documentation index, see [llms.txt](/llms.txt)

# glotto.yml reference

`glotto.yml` is the single config for a project: it describes your organization, the API spec to
read, the SDK targets to emit, your API surface, and how the generated clients behave. This
reference documents every key across this overview and the linked optional-reference pages. Where
a key has its own page, it links there rather than repeating detail.

## Editor validation

Generate the JSON Schema for `glotto.yml` and reference it from the file for inline completion and
validation in VS Code, Cursor, Neovim, and other editors with the YAML language server:

```bash
glotto schema --out glotto.schema.json
```

```yaml
# yaml-language-server: $schema=./glotto.schema.json
organization:
  name: Acme
  # …
```

`glotto schema` (no flags) prints the schema to stdout instead. It's the same byte-stable schema
core-config exports as `glottoConfigJsonSchema()`.

---

## Required keys

### `organization`

```yaml
organization:
  name: Acme
  contact: support@acme.com
  homepage: https://acme.com   # optional

  # optional: security metadata → a SECURITY.md in every generated repo
  security_contact: security@acme.com
  security_policy_url: https://acme.com/security
  security_policy_terms: Reports are acknowledged within two business days.
```

`name` and `contact` are required; `homepage` is optional.

#### Security metadata → `SECURITY.md`

Setting **any** of `security_contact`, `security_policy_url`, or `security_policy_terms` makes
`glotto generate` write a `SECURITY.md` into every generated repo — the file GitHub's "Report a
vulnerability" affordance keys off, so a researcher who finds an issue in your SDK has a documented
route to you. Set none of them and nothing is emitted; your output is byte-for-byte what it was.

Each key contributes one section, and an unset key contributes none:

| Key | Section it adds |
|---|---|
| `security_contact` | **Reporting a Vulnerability** — the address, plus a note directing researchers away from public issue trackers. Must be an email address. |
| `security_policy_url` | **Disclosure Policy** — a link to your published policy, named with `organization.name`. Must be an absolute `http`/`https` URL. |
| `security_policy_terms` | **Additional Terms** — your prose, verbatim. Free text, no grammar. |

The document is deterministic: it embeds no date or other ambient value, so regenerating an
unchanged config reproduces it byte-for-byte.

### Input source — exactly one of `openapi` / `asyncapi` / `graphql`

```yaml
openapi:
  source: ./spec/openapi.yaml          # a local path or an http(s) URL
  code_samples:
    formats: [x-codeSamples, readme]   # optional: code-sample formats to emit
```

`source` accepts a **string** — a local path (resolved against the directory holding your
`glotto.yml`, so it means the same thing wherever you run Glotto from) or an `http(s)://` URL —
or one of three structured objects: `command` (run your own exporter), `introspect`
(read a framework's source directly), or `git` (below). `asyncapi` takes a `source`; `graphql`
takes a `source` plus optional `operations` and `autogenerate`. Exactly one input source must be
present.

**The same source forms apply to all three inputs**, and to `graphql`'s second document: a
git-hosted AsyncAPI channel document, or a GraphQL schema produced by your own exporter, is
written exactly the way the OpenAPI equivalent is.

```yaml
asyncapi:
  source:
    git: { repo: https://github.com/acme/api-definitions.git, ref: main, path: events/chat.yaml }

# or
graphql:
  source: { command: npm, args: [run, print-schema], output: { kind: stdout } }
  operations: ./operations.graphql
```

The one exception is `introspect`, which is accepted on **`openapi.source` only**: introspection
reads a framework's source and *synthesizes an OpenAPI document*, so it cannot be what an
`asyncapi` or `graphql` key means. Writing it anywhere else is a `glotto generate` error
([`GLOTTO_CONFIG_INTROSPECT_SOURCE_OPENAPI_ONLY`](/docs/diagnostics)).

#### Specs in another repository

When your OpenAPI document lives in a **different git repository** — the usual shape when a
separate API-definition repo produces the spec — point `source` at it directly instead of
vendoring a copy you then have to keep in step by hand:

```yaml
openapi:
  source:
    git:
      repo: https://github.com/acme/api-definitions.git
      ref: main                    # a branch, tag, or commit sha
      path: openapi/petstore.yaml  # relative to the repository root
```

All three keys are required. `path` is resolved inside the cloned repository, never against your
`glotto.yml` — a path escaping the repository is rejected. Glotto shallow-clones `repo` at `ref`
on each generation, reads that one file, and removes the clone; authentication uses your ambient
git configuration. The same `{ git: … }` object is accepted wherever a spec or overlay is
referenced — `asyncapi.source`, `graphql.source`, `graphql.operations`, every
`docs.versions[]` snapshot, `docs.changelog.previous_spec.source`, and an
[`apply_overlay`](/docs/transforms) `source`.

Because `ref` can be a moving branch, the [verification report](/docs/verification-report) records
the exact bytes it read, under the machine-independent origin `git:<repo>#<ref>:<path>`.

#### Multi-file specs

Glotto ingests a **single** OpenAPI document. If your spec is split across multiple files
(`$ref`s into sibling files — common for large APIs and a frequent migration case), bundle it into
one document first. Glotto does not merge multiple sources itself, so the bundling stays in your
control — there are two ways to wire it:

**Pre-bundle** — run a bundler as a build step and point `source` at the result:

```sh
redocly bundle ./openapi/main.yaml -o ./openapi/bundled.yaml
```

```yaml
openapi:
  source: ./openapi/bundled.yaml
```

**Bundle inline** — let Glotto run the bundler at generate time via the `command` form of `source`;
its stdout (or a file it writes) is ingested directly, with no committed intermediate:

```yaml
openapi:
  source:
    command: redocly
    args: [bundle, ./openapi/main.yaml]
    output: { kind: stdout } # or { kind: file, path: ./.glotto/bundled.yaml }
```

### `environments`

```yaml
environments:
  production: https://api.acme.com
  staging:    https://staging-api.acme.com
```

A map of environment name → base URL, with at least one entry.

### `default_environment`

```yaml
environments:
  dev:        https://dev.acme.com
  production: https://api.acme.com

default_environment: production
```

Which environment everything Glotto emits points at when it needs **one** base URL — your SDK
clients and their README quickstarts, the code samples on your docs site, your docs playground, and
your MCP server's upstream. Optional; it must name one of the keys under
[`environments`](#environments).

Leave it out and Glotto resolves the default for you, in this order:

1. an environment named `production`, if you declared one — the name `glotto init` writes;
2. otherwise the first environment **by sorted name**.

That second rule exists so your generated code depends only on what your config *says*, never on
the order you happened to type it in. It is a determinism rule, not a guess at what you meant — so
when it is what decides the answer (two or more environments, none of them named `production`,
none declared), `glotto generate` tells you, with
[`GLOTTO_CONFIG_DEFAULT_ENVIRONMENT_INFERRED`](/docs/diagnostics). Naming an environment that
isn't declared is an error, [`GLOTTO_CONFIG_DEFAULT_ENVIRONMENT`](/docs/diagnostics).

### `targets`

```yaml
targets:
  typescript:
    package_name: "@acme/sdk"
  react_native:
    package_name: "@acme/sdk-react-native"
    secure_storage: keychain    # or 'mmkv', 'expo'
    expo_plugin: true           # optional explicit opt-in for the public ./plugin subpath
  csharp:
    package_name: Acme.Commerce  # NuGet PackageId (publish identity)
    namespace: Acme.Commerce     # C# namespace (code identity)
  go:
    namespace: github.com/acme/commerce-go   # go.mod module path
  java:
    namespace: com.acme.commerce             # Java package + Gradle group
  dart:
    secureStorage: true          # emit the flutter_secure_storage token-store adapter
  python: {}
```

A map of language slug → per-target config. Every target accepts:

- `package_name` — the **published/registry** identity stamped on the manifest (npm name,
  PyPI name, NuGet `<PackageId>`, …).
- `namespace` — the SDK's **code** identity, interpreted idiomatically per engine: the C#
  `namespace`/`<RootNamespace>`, Java/Kotlin `package` (+ source directory + Gradle `group`),
  Go `go.mod` module path, PHP PSR-4 namespace, Ruby/Elixir top-level module, Python's import
  package (dots nest into directories, so `acme.commerce` gives `from acme.commerce import
  Client`), and Rust's Cargo `[lib] name` (a lib name is a flat identifier, so dots collapse —
  `acme.commerce` gives `use acme_commerce`). Optional — absent means each engine's default
  (`Glotto` / `com.glotto` / `glotto_sdk`). Engines whose code identity *is* the published
  package — `typescript`/`react_native` (npm name), `dart`, `swift` — have no separate code
  identity to set: `package_name` is the only knob, and setting `namespace` on one of them
  warns, [`GLOTTO_CONFIG_NAMESPACE_NOT_APPLIED`](/docs/diagnostics), rather than being
  silently ignored.
- `repo` — the target's **release repo** (`owner/name`; Azure Repos `org/project/repo`),
  with an optional `repo_provider` (`github`, `gitlab`, `bitbucket`, or `azure-repos`)
  naming the VCS host. Used by the hosted platform's targets sync, which derives each
  project's SDK-target records from this file — where release PRs and tags for that
  language's SDK go. Optional — omit both for registry-only targets; `repo_provider`
  requires `repo`.
- `repo_path` — for a **monorepo** release repo: the repo-relative directory this language's
  SDK lives under (e.g. `sdks/typescript`). Several targets may share one `repo` when each
  names a distinct `repo_path` — each gets its own subtree, release branch, and release PR.
  Optional (requires `repo`) — absent means the SDK owns the repo root, the one-repo-per-SDK
  layout.
- `release` — this target's branch names, overriding the project-level
  [`release`](/docs/glotto-yml-project-settings#release) block one name at a time. Three optional
  keys, all defaulting to Glotto's
  [`generated` → `next` → `main`](/docs/multi-vcs-release) vocabulary:

  ```yaml
  targets:
    typescript:
      repo: acme/acme-typescript
      release:
        branch: sdk-next            # the release PR's head — the merged result
        baseline_branch: sdk-generated  # the pristine generator output (the merge base)
        base_branch: trunk          # the release PR's base — released code
  ```

  `branch` and `baseline_branch` must differ — one branch cannot be both the merged result and
  the base it merges against. Two targets releasing to the same `repo` may not name the same
  `branch`. Use `base_branch` when a repository's released branch is not called `main`: Glotto
  refuses to open the PR rather than guessing at a branch you never named.
- `variants` — **[`spec_repo`](/docs/spec-repo) only**: which documents the published spec repo
  carries. A non-empty list of `base` (your spec as supplied, `exclude`-pruned), `with_transforms`
  (after every [`transforms`](/docs/transforms) correction — the document your SDKs were generated
  from), and `with_code_samples` (with per-operation SDK samples embedded as `x-codeSamples`).
  Optional — absent means all three. An unknown member, and a member listed twice, are both
  `glotto generate` errors rather than being ignored or deduplicated: a variant you asked for and
  did not get is never silently dropped. Asking for one your input cannot produce fails loudly —
  see [which variants each input can publish](/docs/spec-repo#which-variants-your-input-can-publish).
- `formats` — **`spec_repo` only**: the serializations each variant is published in. A non-empty
  list of `yaml` and `json`, same duplicate/unknown-member rules. Optional — absent means `yaml`
  alone. Emitted file names are a function of the selection's content, never of the order you
  listed it in.

React Native additionally accepts `secure_storage` and `expo_plugin`. The Expo plugin is emitted by
default only when the API has an intrinsic plugin capability. An API without OAuth or
bearer-style/secure-storage auth capability—for example, one using only custom or API-key auth—has
no `./plugin` export by default. Set `expo_plugin: true` to opt that target in explicitly, then
### `resources`

```yaml
resources:
  invoices:
    models:
      invoice: '#/components/schemas/Invoice'
    methods:
      createInvoice:   post /v1/invoices
      getInvoice:       get /v1/invoices/{id}
      listInvoices:    { endpoint: get /v1/invoices, paginated: cursor }
    subresources:
      line_items:
        methods:
          addInvoiceLine: post /v1/invoices/{invoice_id}/lines
```

Your API surface, grouped into resources. A method is either a `"VERB /path"` string or an object
`{ endpoint, paginated?, streaming?, polling?, default_request_options? }`. `resources` recurse through `subresources`
arbitrarily deep.

Set [`default_request_options`](/docs/glotto-yml-client-behavior#default_request_options) on a
resource or method to configure routing headers, attempt timeouts and retry limits for its calls.

> **The method key declares the emitted method name.** Writing `list: get /v1/invoices` emits
> `client.invoices.list()` in every language, whatever the operation's `operationId` says. Declare
> nothing and the emitted name is the `operationId` exactly as before — naming is opt-in per
> operation, so a config with no `methods` keys produces a byte-identical SDK.
>
> The **resource tree itself is still derived from your spec's paths**: the key names the *method*,
> not the resource, and never moves a method between resources.
>
> Two things are refused rather than guessed, and both stop `glotto generate`
> ([`GLOTTO_CONFIG_RESOURCE_METHOD_NAME`](/docs/diagnostics)) — a name you asked for and did not get
> is never silently dropped:
>
> - an endpoint matching no operation in your spec (the verb and path must match **exactly**,
>   placeholder names included);
> - two operations on one resource ending up with the same method name. Glotto names both and stops,
>   rather than picking a winner or appending a suffix — you wrote both names, so you hold the fix.
>
> Renaming a method your customers already call is an **API-compatible** change if you say so: keep
> the old name working with [`aliases`](/docs/glotto-yml-api-surface#aliases--deprecated) and mark
> it with [`deprecated`](/docs/glotto-yml-api-surface#aliases--deprecated),
> and breaking-change detection reports the rename as non-breaking.

---

## Optional-key reference pages

Optional settings are grouped by the part of the generated surface they shape:

- [Client behavior](/docs/glotto-yml-client-behavior) — client runtime defaults, transforms, naming,
  custom casings, positional parameters, and enum naming.
- [Model shaping](/docs/glotto-yml-model-shaping) — model names and inlining, enum types,
  soft-required inputs, and auto-populated values.
- [API surface](/docs/glotto-yml-api-surface) — target filtering, global exclusion, client methods,
  aliases and deprecations, streaming, and query serialization.
- [Project settings](/docs/glotto-yml-project-settings) — diagnostics, project-wide settings,
  code owners, MCP, generated docs, licensing, releases, README examples, and hosted binding.

---

See the [CLI reference](/docs/cli) for the commands that read this config, the
[pipeline](/docs/pipeline) concept for how it's consumed, or
[Getting started](/docs/getting-started) for a walkthrough.
