Open Workflow GraphPre-release
Specification

Document structure

The OWG v0.92 document — every top-level field, the entity registries, parameters, and governance ceilings.

View as markdownMachine-readable source for agents and scripted implementers

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 profiles extension point on context, asset, and relationships[] (see Extensions), and v0.91 was additive over v0.9, adding task.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:

FieldTypeNotes
owg_versionstringMust be "0.9", "0.91", or "0.92". Pattern ^\d+\.\d+(\.\d+)?(-[a-z0-9]+)?$
idstringPattern ^[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

FieldTypePurpose
owg_versionstringRequired. The specification version this document targets
idstringRequired. Document identifier
$schemastringSchema URI. Advisory, but if present must match the version
project_idstringGroups multiple documents under one production
profilesstring[]Conformance profiles in use. See Profiles
descriptionstringMax 2000 chars
objectivestringThe macro-objective agents are governed against
paramsobjectCaller-supplied parameters. See below
organizationsarrayRegistry — studios, vendors, service providers
participantsarrayRegistry — humans, agents, services
infrastructurearrayRegistry — compute, storage, facilities, devices
assetsarrayRegistry — media and data objects
contextsarrayRegistry — productions, scenes, deliverable groupings
contextstring | objectThe primary context: inline, or an id from contexts[]
relationshipsarrayExplicit and proposed edges. See Relationships
tasksarrayThe work, and the authoritative source of provenance edges
governanceobjectRun-level ceilings and gates
on_workflow_failurestringTask id to run if the workflow fails
webhook_urlstringHTTPS endpoint for run status updates
timeout_secondsintegerWhole-workflow wall clock, ≥ 1
metadataobjectFreeform. 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:

FieldTypeNotes
typeenumRequired. string | number | boolean | array | object
requiredbooleanDefault false
defaultanyOnly permitted when required is false
descriptionstringWhat the caller should supply

Two rules the schema enforces rather than merely stating:

  • A parameter with required: true must not carry a default.
  • A default, when present, must match the declared type.

Tasks read parameters with $.params.<key>. The grammar is in Reference syntax.

Validation resolves parameter references. A task referencing $.params.add_caption when the document declares add_captions is 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
  }
}
FieldTypePurpose
max_total_reroutesinteger ≥ 0Ceiling on QC re-routes across the whole run
budget_usdnumber ≥ 0Spend ceiling for the whole run
disclosure_gatebooleanBlocks 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"
}
FieldTypeNotes
idstring
show_idstringProduction code
scene_idstring
territorystringe.g. US, EMEA
organization_idstring
work_typeenumcreative-fictional | factual-news | advertising | sports | other
profilesobjectProfile-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 in identifiers[] 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 on context, asset, and relationships[], keyed by profile id. Unlike metadata, a profile can publish a schema for its own key so the shape is documented and, once the validator resolves it, checkable — and unlike x-, 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

ArtifactURI
Core v0.92 (current)https://openworkflowgraph.org/schemas/core/v0.92.json
Core v0.91https://openworkflowgraph.org/schemas/core/v0.91.json
Core v0.9https://openworkflowgraph.org/schemas/core/v0.9.json
broadcast-newsroom profile v0.1https://openworkflowgraph.org/schemas/profiles/broadcast-newsroom/v0.1.json
JSON-LD contexthttps://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