Skip to content

API contracts: operators, templates, and users

At a glance

  • Three DTO groups: operators (small), templates (large — the actual persisted mapping), users (tiny).
  • Every DTO is a Zod schema with an inferred type, not a hand-written interface — same pattern as schema serialization.
  • TemplateSave.plugins is exactly the shape plugins' serialize() produces.

Alongside the tree/schema data model covered in Tree nodes and schemas, packages/types also defines three Zod-validated DTO groups: operators-api, templates-api, and users-api. These describe records that get persisted and sent over the wire, as opposed to the in-memory tree model a template is built from.

Operators

Operator (validated by operatorSchema) is the smallest of the three: an id, operatorid, a list of domains, an optional language, and an optional schemes — a nested record (domain -> type -> variant -> schema name) for overriding which schema variant applies for that operator in a given domain/type combination. The file's own example is the clearest description of the shape:

json
{
  "operatorid": "100",
  "domains": ["retail"],
  "schemes": {
    "retail": {
      "standard": {
        "table": "customRetailTableSchema"
      }
    }
  }
}

Templates

This is the largest of the three groups, built around TemplateRef — a template's own metadata: id, operatorId, name, path/deployPath, domain, priority, inputsource (a SchemaSource), enabled/deleted flags, and optional EDI fields (ediType, ediVariables — see Reserved names for how EDI variables relate to the runtime scope) and a recentFiles list.

Layered on top of TemplateRef:

  • TemplateSave — the actual saved content of a template: entries and graphs (both loosely typed as Record<string, any> at this layer — the concrete shapes come from schema-serialization's SerializedSchema types, not from here), a config block (preconfiguration, an optional configSnapshot, updateDiscardIds, lastTextContentHash), and a plugins block that separately tracks global plugin state (plugins, versions, bootstrapped) and per-node registered plugin instances (version + slice per key). See Built-in plugins for what a plugin's slice holds.
  • TemplateResourceTemplateRef merged with { save: TemplateSave }, minus path. This is the full template as the API would return it.
  • CreateTemplateResourceDto / UpdateTemplateResourceDto — request bodies for creating/updating a template. Both replace save with a string — i.e. the client sends the save data serialized, not as a live object. Update... additionally makes every field optional (a Zod .partial()) and drops id.
  • TemplateDeploy / RawTemplateDeploy — the shapes used when deploying a template's compiled output (RawTemplateDeploy is just { deployPath, content }).
  • TemplateCategory — a separate, much smaller record for grouping templates by category within a domain.

Users

User (via userSchema) is the smallest of all three groups: just userId and operatorId. There's no users-api equivalent of the template/operator richness — it exists to tag records with who/which operator they belong to, not to model a user profile.

Why these are Zod schemas, not plain types

Every DTO here is defined as a z.object(...) with a z.infer<...> type derived from it, rather than a hand-written interface. That gives callers both the compile-time type and a runtime validator from the same definition — relevant at API boundaries, where the data hasn't been type-checked by TypeScript yet. See Schema serialization for how this same runtime-validator-as-source-of-truth approach extends to the tree model itself.