> ## Documentation Index
> Fetch the complete documentation index at: https://docs.briefedmedia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Graph definitions

> The shape of a workflow graph: nodes, edges, triggers, gates and destinations.

# Graph definitions

A workflow version holds a graph. The same structure is accepted by `validate-definition` and by the version-create route, and both apply the same rules.

```json theme={null}
{
  "schemaVersion": 1,
  "nodes": [],
  "edges": [{ "from": "nodeId", "to": "nodeId" }]
}
```

## Part nodes

A part node names a catalogue part and carries whatever that part needs.

```json theme={null}
{
  "id": "lookup",
  "partId": "terminal.company.lookup",
  "partVersion": "1",
  "terminal": {
    "job": { "id": "company.lookup", "version": "1" },
    "subjectRefs": [
      { "canonicalId": "company:...", "kind": "company", "label": "Apple Inc.", "identifiers": [] }
    ]
  }
}
```

`terminal.subjectRefs` must come from [`resolve-subjects`](/api-reference/workflows/quickstart#2-pin-the-companies). Every `terminal.*` part needs its own pin; identity comes from the pin, never from free text. A run must not re-guess a company name at schedule time, which is why a pin is required before publish rather than resolved at fire time.

Specialist parts take an `objective` instead, fixed at publish and reused verbatim:

```json theme={null}
{ "id": "dig", "partId": "anya.investigation", "partVersion": "1", "objective": "Governance changes since the last filing" }
```

An objective is capped at 1000 characters and must not be framed around an individual's personal circumstances. Briefed produces research of general circulation, so an objective describing someone's holdings, position or situation is rejected with `personal_circumstances`.

## Trigger node

Exactly one, describing what starts a run.

```json theme={null}
{ "id": "t1", "kind": "trigger", "trigger": { "kind": "on_demand" } }
```

| Kind        | Fields                                                                                                               |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| `on_demand` | none                                                                                                                 |
| `scheduled` | `cadence` (`daily` or `weekly`), `hour` (integer 0–23), `weekday` (0–6, weekly only), `timezone` (IANA name or null) |
| `event`     | an event name from the event catalogue                                                                               |

`cadence: "daily"` means every weekday, not every day.

A composition needs no `defaultQuestion`. The graph already says what to
research: every step carries its own pinned subjects, which is the same reason
a terminal-pinned workflow has never needed one. The field is accepted and
ignored.

A scheduled trigger still requires `defaultQuestion` on the one host that has
nothing else to go on: a workflow with neither a pin nor a graph. An unattended
run with no bound subjects and nobody present has to say what it is
investigating.

## Gate node

What to do when evidence comes back below the floor.

```json theme={null}
{ "id": "g1", "kind": "gate", "belowFloor": "hold" }
```

The gate sits between the research steps and delivery, so it decides whether a partial result is released. This is the mechanism behind honest degrade: a below-floor run is held rather than delivered as though it were complete.

## Destination node

Where output goes. More than one is allowed.

```json theme={null}
{ "id": "d1", "kind": "destination", "destination": { "kind": "standalone" } }
```

Kinds are `standalone`, `brief_insert` and `external`. An `external` destination references a configured integration by `destinationId`.

## Edges

```json theme={null}
{ "from": "lookup", "to": "pack" }
```

An edge is legal only when the source's `produces` satisfies the target's `requires`. Edges between a trigger, gate or destination and the rest of the graph are derived from the structure and do not need to be declared.

## Validation faults

`validate-definition` returns every fault at once rather than stopping at the first, and each names the node it applies to.

| Code                     | Meaning                                                         |
| ------------------------ | --------------------------------------------------------------- |
| `empty_graph`            | No nodes                                                        |
| `unknown_part`           | `partId` is not in the catalogue                                |
| `missing_terminal_pin`   | A `terminal.*` part with no pinned subjects                     |
| `missing_objective`      | A specialist part with no objective                             |
| `invalid_objective`      | Objective longer than 1000 characters                           |
| `personal_circumstances` | Objective framed around an individual's situation               |
| `unmet_requirement`      | A part's `requires` is not satisfied by any incoming edge       |
| `invalid_wire`           | An edge whose source output does not satisfy the target's input |
| `cycle`                  | The graph loops                                                 |

Because validation and save share one implementation, a definition that validates will save. If it does not, that is a bug worth reporting with the `X-Request-ID`.

## Version history

```bash theme={null}
curl "$BRIEFED_API/api/v1/workflows/$WF/versions" \
  -H "Authorization: Bearer $BRIEFED_API_KEY"
```

Versions are returned newest first, capped at **50** per response. A workflow
edited daily passes that in under two months, so do not treat the response as
the complete history: the version numbers themselves are the durable
reference, and the newest 50 are what this route will show you.

Publishing a version does not delete the ones before it. `rollback` republishes
an earlier version rather than reverting the definition, so the history stays
append-only and an audit reads in one direction.
