Document structure
The OWG v0.92 document — every top-level field, the entity registries, parameters, and governance ceilings.
An OWG document is a single JSON object describing a production workflow: five entity registries, the tasks that connect them, the parameters a caller supplies, and the ceilings the run is held to.
{
"$schema": "https://openworkflowgraph.org/schemas/core/v0.92.json",
"owg_version": "0.92",
"id": "episodic-dailies",
"profiles": ["core", "cloud-services"],
"objective": "Turn camera cards into approved dailies with disclosure records.",
"params": { },
"organizations": [ ],
"participants": [ ],
"infrastructure": [ ],
"assets": [ ],
"contexts": [ ],
"relationships": [ ],
"tasks": [ ],
"governance": { }
}v0.92 is a draft specification. It is the current version; v0.91 and v0.9 remain resolvable at their own schema URLs and a document targeting either stays valid — v0.92 is additive over v0.91, adding an optional
profilesextension point oncontext,asset, andrelationships[](see Extensions), and v0.91 was additive over v0.9, addingtask.fan_out(see Fan-out). Earlier internal iterations before v0.9 are superseded and are not described here. The version number will reach 1.0 when the specification is stable enough to promise compatibility. Until then, expect field-level change, and pin the schema you validate against. See the changelog for what changed and when.
Required fields
Only two:
| Field | Type | Notes |
|---|---|---|
owg_version | string | Must be "0.9", "0.91", or "0.92". Pattern ^\d+\.\d+(\.\d+)?(-[a-z0-9]+)?$ |
id | string | Pattern ^[a-zA-Z0-9][a-zA-Z0-9._-]*$, 1–128 chars |
Everything else is optional, including tasks. A document with registries and no tasks is a valid registry document — a shared project registry of participants, assets, and infrastructure that other documents reference. This is used in practice and is deliberate.
All top-level fields
| Field | Type | Purpose |
|---|---|---|
owg_version | string | Required. The specification version this document targets |
id | string | Required. Document identifier |
$schema | string | Schema URI. Advisory, but if present must match the version |
project_id | string | Groups multiple documents under one production |
profiles | string[] | Conformance profiles in use. See Profiles |
description | string | Max 2000 chars |
objective | string | The macro-objective agents are governed against |
params | object | Caller-supplied parameters. See below |
organizations | array | Registry — studios, vendors, service providers |
participants | array | Registry — humans, agents, services |
infrastructure | array | Registry — compute, storage, facilities, devices |
assets | array | Registry — media and data objects |
contexts | array | Registry — productions, scenes, deliverable groupings |
context | string | object | The primary context: inline, or an id from contexts[] |
relationships | array | Explicit and proposed edges. See Relationships |
tasks | array | The work, and the authoritative source of provenance edges |
governance | object | Run-level ceilings and gates |
on_workflow_failure | string | Task id to run if the workflow fails |
webhook_url | string | HTTPS endpoint for run status updates |
timeout_seconds | integer | Whole-workflow wall clock, ≥ 1 |
metadata | object | Freeform. See Extensions |
Parameters
params declares what a caller must supply when submitting the document for execution. This is how a reusable workflow definition stays reusable: the definition holds no run-specific values, and the caller provides them.
{
"params": {
"source_url": {
"type": "string",
"required": true,
"description": "Public HTTPS URL of the source file"
},
"add_captions": {
"type": "boolean",
"required": false,
"default": false
},
"territories": {
"type": "array",
"required": false,
"default": ["US"]
}
}
}Each entry is keyed by parameter name and takes:
| Field | Type | Notes |
|---|---|---|
type | enum | Required. string | number | boolean | array | object |
required | boolean | Default false |
default | any | Only permitted when required is false |
description | string | What the caller should supply |
Two rules the schema enforces rather than merely stating:
- A parameter with
required: truemust not carry adefault. - A
default, when present, must match the declaredtype.
Tasks read parameters with $.params.<key>. The grammar is in Reference syntax.
Validation resolves parameter references. A task referencing
$.params.add_captionwhen the document declaresadd_captionsis a validation error, not a run-time surprise. See Validation.
Governance
Run-level ceilings. These are hard limits, not advisory budgets — an engine must refuse to exceed them.
{
"governance": {
"max_total_reroutes": 12,
"budget_usd": 4800,
"disclosure_gate": true
}
}| Field | Type | Purpose |
|---|---|---|
max_total_reroutes | integer ≥ 0 | Ceiling on QC re-routes across the whole run |
budget_usd | number ≥ 0 | Spend ceiling for the whole run |
disclosure_gate | boolean | Blocks delivery tasks until compliance certifies |
max_total_reroutes matters more than it looks: it is the circuit breaker on an agent-and-gate loop that would otherwise re-route indefinitely. See Gates and re-routes.
Context
Context is what scopes work. It may be given inline or by reference into contexts[].
{
"contexts": [
{ "id": "ctx_ep104", "show_id": "SHOW_001", "scene_id": "sc_014",
"territory": "US", "work_type": "creative-fictional" }
],
"context": "ctx_ep104"
}| Field | Type | Notes |
|---|---|---|
id | string | |
show_id | string | Production code |
scene_id | string | |
territory | string | e.g. US, EMEA |
organization_id | string | |
work_type | enum | creative-fictional | factual-news | advertising | sports | other |
profiles | object | Profile-contributed extensions, keyed by profile id (v0.92). See Extensions |
work_type is load-bearing for compliance rather than descriptive — it selects which disclosure obligations apply. See AI disclosure.
Tasks and assets are associated with a context rather than contained in it, because the same asset means different things in different places.
No shot identifier. OWG v0.9 deliberately has no
shot_id. MovieLabs' OMC v3.0 has no Shot entity; the video pipeline covering production scene through editorial shots and sequences arrives in a later OMC release. Rather than invent a mapping that will conflict, v0.9 omits it. Carry a shot identifier inidentifiers[]with your own scope until OMC defines one.
Extensions
Every object defined by this specification sets additionalProperties: false. An unrecognised key is a validation error, not a silently accepted field.
This is deliberate, and it is the single most important protection the schema offers an agent-authored document: a hallucinated or misspelled property fails loudly rather than validating and doing nothing.
Extensibility is preserved through three explicit escape hatches:
metadata— a freeform object available on the document and on every entity. Put anything here.x-prefixed keys — permitted anywhere, for vendor extensions that want to live alongside specified fields.profiles(v0.92) — a namespaced object oncontext,asset, andrelationships[], keyed by profile id. Unlikemetadata, a profile can publish a schema for its own key so the shape is documented and, once the validator resolves it, checkable — and unlikex-, two profiles' contributions on the same entity cannot collide, because each owns only its own key. See Profiles → Registry extension profiles.
{
"id": "grade_0140",
"metadata": { "internal_ticket": "POST-4471" },
"x-acme-cost-centre": "CC-88",
"profiles": { "broadcast-newsroom": { "evidential_position": "PRIMARY" } }
}All three are ignored by the validator's structural checks today and preserved byte-for-byte on round-trip — profiles resolution against a named profile's own schema is not yet wired in, see Profiles → Registry extension profiles for what that means in practice. Anything you would have been tempted to add as a bare unknown key belongs in one of the three.
Schema identity
| Artifact | URI |
|---|---|
| Core v0.92 (current) | https://openworkflowgraph.org/schemas/core/v0.92.json |
| Core v0.91 | https://openworkflowgraph.org/schemas/core/v0.91.json |
| Core v0.9 | https://openworkflowgraph.org/schemas/core/v0.9.json |
broadcast-newsroom profile v0.1 | https://openworkflowgraph.org/schemas/profiles/broadcast-newsroom/v0.1.json |
| JSON-LD context | https://openworkflowgraph.org/context/v0.9.jsonld |
Both are JSON Schema draft 2020-12.
The JSON-LD context
The context document maps OWG terms onto published vocabularies, so lineage is portable rather than locked to this schema:
{
"@context": {
"@version": 1.1,
"owg": "https://openworkflowgraph.org/schemas/core/v0.9#",
"omc": "https://movielabs.com/omc/json/schema/v3.0#",
"prov": "http://www.w3.org/ns/prov#",
"c2pa": "https://c2pa.org/specifications/...#",
"did": "https://www.w3.org/ns/did#",
"schema": "https://schema.org/",
"id": "@id",
"description": "schema:description",
"owg_version": "schema:version",
"objective": "owg:objective",
"organizations": { "@id": "owg:hasOrganization", "@container": "@set" },
"participants": { "@id": "owg:hasParticipant", "@container": "@set" },
"infrastructure": { "@id": "owg:hasInfrastructure","@container": "@set" },
"assets": { "@id": "owg:hasAsset", "@container": "@set" },
"contexts": { "@id": "owg:hasContext", "@container": "@set" },
"relationships": { "@id": "owg:hasRelationship", "@container": "@set" },
"tasks": { "@id": "owg:hasTask", "@container": "@set" },
"used": { "@id": "prov:used", "@type": "@id" },
"produced": { "@id": "prov:generated", "@type": "@id" },
"produced_by": { "@id": "prov:wasGeneratedBy", "@type": "@id" },
"performed_by": { "@id": "prov:wasAssociatedWith", "@type": "@id" },
"operated_by": { "@id": "prov:actedOnBehalfOf", "@type": "@id" },
"works_for": { "@id": "prov:actedOnBehalfOf", "@type": "@id" },
"predecessor": { "@id": "prov:wasDerivedFrom", "@type": "@id" },
"depends_on": { "@id": "owg:dependsOn", "@type": "@id" },
"ran_on": { "@id": "owg:ranOn", "@type": "@id" },
"kind": "owg:participantKind",
"role": "owg:role",
"organization_id": { "@id": "omc:organization", "@type": "@id" },
"identity": "owg:identity",
"credential_scope": { "@id": "owg:credentialScope","@container": "@set" },
"did_identity": { "@id": "did:subject", "@type": "@id" },
"type": "owg:entityType",
"version_relation": "owg:versionRelation",
"state": "owg:lifecycleState",
"assurance": "owg:assurance",
"confidence": "owg:confidence",
"method": "owg:inferenceMethod",
"credentials": { "@id": "c2pa:manifest", "@type": "@id" },
"identifiers": { "@id": "owg:identifier", "@container": "@set" },
"scope": "owg:identifierScope",
"storage": "owg:storage",
"provider": "owg:storageProvider",
"locator": "owg:storageLocator",
"timerange": "owg:timerange",
"composes": { "@id": "owg:composes", "@container": "@set" },
"component": { "@id": "owg:component", "@type": "@id" },
"arc_type": "owg:compositionArc",
"executor": "owg:executor",
"ai_role": "owg:aiRole",
"failure_mode": "owg:failureMode",
"when": "owg:condition",
"checks": { "@id": "owg:check", "@container": "@set" },
"on_pass": { "@id": "owg:onPass", "@type": "@id" },
"on_fail": { "@id": "owg:onFail", "@type": "@id" },
"editorial_signoff": "owg:editorialSignoff",
"billing": "owg:billing",
"show_id": "omc:Context.identifier",
"territory": "omc:territory",
"work_type": "owg:workType",
"depicts": { "@id": "owg:depicts", "@type": "@id" },
"depicts_real_entity": "owg:depictsRealEntity",
"governance": "owg:governance",
"disclosure_gate": "owg:disclosureGate",
"max_total_reroutes": "owg:maxTotalReroutes",
"budget_usd": "owg:budget"
}
}Three mapping decisions are worth explaining, because they are the ones a reader would otherwise query.
Provenance terms resolve to PROV, not OMC. A JSON-LD term carries one IRI, and OMC's current
identifier is a JSON Schema URL rather than a published RDF vocabulary. The OMC edge
correspondences are normative in their own right and listed in
Standards bindings; a direct omc: RDF
mapping follows MovieLabs' RDF/SHACL release.
Registry arrays are @set, not @list. Order carries no meaning anywhere in an OWG document
— execution order comes from depends_on, never from array position. Declaring them unordered
makes that explicit to an RDF consumer rather than leaving it as an assumption.
works_for and operated_by share an IRI. Both are PROV delegation; they differ only in
whether the principal is an organization or a participant. Collapsing them in RDF is correct —
the distinction lives in the endpoint's kind, which is where a query should look for it.
Fields deliberately unmapped: params, inputs, outputs, retry, subgraph, fan_out, and
metadata. These are execution mechanics rather than assertions about the world, and giving them
RDF semantics would imply a meaning they do not carry.
Where to go next
- Tasks and executors — the work objects, and the fields that route them
- Reference syntax — the formal grammar for
$.references andwhenexpressions - Relationships — explicit edges, assurance, and confidence
- Validation — what is checked, and what a failure looks like