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:
glotto schema --out glotto.schema.json
# 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
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
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.
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).
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:
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 source.
Because ref can be a moving branch, the 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
($refs 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:
redocly bundle ./openapi/main.yaml -o ./openapi/bundled.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:
openapi:
source:
command: redocly
args: [bundle, ./openapi/main.yaml]
output: { kind: stdout } # or { kind: file, path: ./.glotto/bundled.yaml }
environments
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
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.
Leave it out and Glotto resolves the default for you, in this order:
- an environment named
production, if you declared one — the nameglotto initwrites; - 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. Naming an environment that
isn’t declared is an error, GLOTTO_CONFIG_DEFAULT_ENVIRONMENT.
targets
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/Kotlinpackage(+ source directory + Gradlegroup), Gogo.modmodule path, PHP PSR-4 namespace, Ruby/Elixir top-level module, Python’s import package (dots nest into directories, soacme.commercegivesfrom acme.commerce import Client), and Rust’s Cargo[lib] name(a lib name is a flat identifier, so dots collapse —acme.commercegivesuse 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_nameis the only knob, and settingnamespaceon one of them warns,GLOTTO_CONFIG_NAMESPACE_NOT_APPLIED, rather than being silently ignored. -
repo— the target’s release repo (owner/name; Azure Reposorg/project/repo), with an optionalrepo_provider(github,gitlab,bitbucket, orazure-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_providerrequiresrepo. -
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 onerepowhen each names a distinctrepo_path— each gets its own subtree, release branch, and release PR. Optional (requiresrepo) — absent means the SDK owns the repo root, the one-repo-per-SDK layout. -
release— this target’s branch names, overriding the project-levelreleaseblock one name at a time. Three optional keys, all defaulting to Glotto’sgenerated→next→mainvocabulary: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 codebranchandbaseline_branchmust differ — one branch cannot be both the merged result and the base it merges against. Two targets releasing to the samerepomay not name the samebranch. Usebase_branchwhen a repository’s released branch is not calledmain: Glotto refuses to open the PR rather than guessing at a branch you never named. -
variants—spec_repoonly: which documents the published spec repo carries. A non-empty list ofbase(your spec as supplied,exclude-pruned),with_transforms(after everytransformscorrection — the document your SDKs were generated from), andwith_code_samples(with per-operation SDK samples embedded asx-codeSamples). Optional — absent means all three. An unknown member, and a member listed twice, are bothglotto generateerrors 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. -
formats—spec_repoonly: the serializations each variant is published in. A non-empty list ofyamlandjson, same duplicate/unknown-member rules. Optional — absent meansyamlalone. 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
import <your-sdk>/plugin from the generated package in your Expo config. Dart
accepts secureStorage: true (emit the flutter_secure_storage-shaped token-store adapter — see
Dart). Each target is an open
object, so language-specific keys are allowed. See React Native for the
RN-specific options.
resources
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 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/invoicesemitsclient.invoices.list()in every language, whatever the operation’soperationIdsays. Declare nothing and the emitted name is theoperationIdexactly as before — naming is opt-in per operation, so a config with nomethodskeys 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) — 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
aliasesand mark it withdeprecated, 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 — client runtime defaults, transforms, naming, custom casings, positional parameters, and enum naming.
- Model shaping — model names and inlining, enum types, soft-required inputs, and auto-populated values.
- API surface — target filtering, global exclusion, client methods, aliases and deprecations, streaming, and query serialization.
- Project settings — diagnostics, project-wide settings, code owners, MCP, generated docs, licensing, releases, README examples, and hosted binding.
See the CLI reference for the commands that read this config, the pipeline concept for how it’s consumed, or Getting started for a walkthrough.