Open Causal Graph Data Standard (CausalJSON)
Version 0.2 · August 2026 · supersedes version 0.1 of December 2025
CausalJSON is a JSON format for causal graphs — directed acyclic graphs (DAG), acyclic directed mixed graphs (ADMG), maximal ancestral graphs (MAG), partial ancestral graphs (PAG) and causal loop diagrams (CLD) — that records not just the nodes and edges, but the reasoning behind each edge and the credit, licence and provenance of the graph itself. Every graph published here is available in this format from the record's Code tab.
Structure
Four top-level keys are required: $schema, graph,
nodes and edges.
{
"$schema": "https://opencausal.org/causaljson/0.2/causaljson.schema.json",
"graph": { ... }, // identity, credit, licence, provenance
"nodes": [ ... ], // the variables
"edges": [ ... ], // relationships asserted to exist
"excluded_edges": [ ... ] // relationships considered and ruled out
}
Nothing else is allowed: a document carrying a field this standard does not define is invalid, rather than quietly carrying data the next tool will drop. Version 0.2 has no extension mechanism, so a conversion from a richer source loses whatever does not fit.
The graph object
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Human-readable title, up to 255 characters. |
| description | string | Yes | Purpose, context, scope, and the assumptions a reader needs in order to use the graph correctly. |
| type | enum | Yes | dag, admg, mag, pag, pdag, cld or other. Decides how the edges are read, and which edge types are legal. |
| created | ISO 8601 datetime | Yes | When this version was created. There is no modified: a revision is a new version with its own date. |
| authors | array of agents | Yes | The citation list, in citation order. An agent is given_name, family_name, orcid and affiliations (each with a name and an optional ROR id); an organisation uses name with type: "organisation". |
| license | string | Yes | SPDX identifier, e.g. CC-BY-4.0, CC0-1.0. |
| id | string | No | Identifier within the issuing system. Platform-local; a file passed between colleagues need not have one. |
| doi | string | No | Bare DOI: 10.83031/k3m9x2qp, with no https://doi.org/ prefix. |
| version | integer | No | Counts from 1, the way preprint servers number versions. |
| submitter | agent | No | The single person who deposited the record. Not part of the citation. Required for publication here. |
| research_question | string | No | The causal question the graph was drawn to answer, in one sentence. |
| related_identifiers | array of objects | No | Evidence, documentation, source datasets, earlier versions. Each has an identifier, a scheme (DOI, URL, PMID, arXiv, …) and a relation_type from the DataCite vocabulary, which defaults to References. |
| statistical_unit | string | No | What the variables are defined on: individual, household, country-year. Whether a study's unit matches decides whether the graph applies at all. |
| population | string | No | The population and setting the graph is claimed to hold for. |
| provenance | string | No | How the graph was built, in prose: expert workshops, a literature review, a discovery algorithm and its settings. |
| rights_url | string | No | Licence deed URL. |
| publisher | string | No | The issuing platform. |
| language | string | No | A BCP 47 language tag, default en. |
| layout | string | No | The coordinate frame node positions are expressed in. |
Nodes
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique within the document, matching ^[A-Za-z0-9_]{1,64}$ so that it survives DOT, R and Python without quoting. |
| label | string | Yes | Display name. Unrestricted text. |
| description | string | No | Precise enough that another researcher would operationalise the variable the same way. |
| role | enum | No | exposure, outcome or covariate (the default). Confounder, mediator and collider are deliberately absent: they are properties of a variable relative to an exposure–outcome pair, computed from the structure rather than declared. |
| unmeasured | boolean | No | Default false. True when the variable is not in the data the graph is meant for. |
| variable_type | enum | No | binary, categorical, ordinal, continuous, count, time_to_event, composite, other. |
| unit | string | No | Unit of measurement. UCUM codes recommended where they fit (kg/m2, mm[Hg]), plain words where they do not. |
| time_point | integer | No | Occasion of measurement, counting from 0. Only the ordering carries meaning: no directed edge may run from a higher time point to a lower one. Two nodes sharing a label at different time points are the same variable measured twice. |
| identifiers | array of strings | No | Ontology terms as CURIEs: snomedct:22298006, mesh:D001835, loinc:39156-5. This is what makes nodes comparable across graphs. |
| position | object {x, y} | No | Layout coordinates, in the frame declared by graph.layout. |
Edges
| Field | Type | Required | Description |
|---|---|---|---|
| source | string | Yes | Node id. |
| target | string | Yes | Node id. |
| type | enum | Yes | One of six values — see below. |
| sign | enum | No | positive, negative or ambiguous (the sign depends on context). Absent means it has not been stated. Required on every edge of a CLD. |
| strength | number | No | How strong the effect is, on whatever scale the author is working in — a standardised coefficient, a qualitative weight, an effect estimate. State the scale in justification whenever the number is meant to be read quantitatively: 0.62 as a coefficient and 0.62 as an odds ratio are different claims. |
| delayed | boolean | No | Default false. True when the effect arrives materially later than the others — the hash marks on a causal loop diagram arrow. How long goes in justification. |
| justification | string | No | Why this edge is in the graph: the reasoning, evidence or expert judgement behind it. |
| references | array of strings | No | Sources, as CURIEs or URLs: doi:10.1093/ije/dyaa213, pmid:33269396. |
| curvature | number | No | Drawing hint, default 0. The midpoint is displaced perpendicular to the straight line by curvature × the distance between the endpoints. |
What each edge type claims
Each type is defined by the mark at each end — a tail, an arrowhead or a circle. That is what lets one format cover DAGs, ADMGs, MAGs and PAGs at once.
| Type | Drawn | Also written | Reading |
|---|---|---|---|
directed | A → B | -> | A is a direct cause of B |
bidirected | A ↔ B | <-> | an unmeasured common cause of A and B |
undirected | A — B | -- | A and B are adjacent; the orientation is not determined |
partially_directed | A o→ B | @-> | B is not a cause of A; either A causes B or they share a latent cause |
nondirected | A o—o B | @-@ | adjacent, with both endpoints undetermined |
partially_undirected | A o— B | --@ | A is not a cause of B; the other end is undetermined |
These three are not interchangeable, and confusing them is the most common import error
in causal graph tooling. “A and B share a cause we did not measure” is one
bidirected edge. “A causes B and B causes A” is two
directed edges, which makes the graph cyclic and therefore a cld.
“A and B are related but we have not resolved which way” is one
undirected edge, in a graph whose type is pdag.
Which edge types each graph type allows
| type | Allowed edges | Cycles | Also |
|---|---|---|---|
dag | directed | no | |
admg | directed, bidirected | no | may carry both X→Y and X↔Y between one pair |
mag | directed, bidirected, undirected | no | |
pag | all six | no | the equivalence class FCI returns |
pdag | directed, undirected | no | partially oriented: some arrows resolved, some not |
cld | directed | expected | sign required on every edge |
other | all six | unchecked | anything the other six do not describe |
Excluded edges
A missing edge is the strongest claim a causal graph makes, and there is no way to tell
a considered absence from an oversight by looking at the picture.
excluded_edges records the pairs that were thought about and deliberately
left out, with the reasoning:
"excluded_edges": [
{
"source": "ice_cream",
"target": "drowning",
"justification": "No mechanism at the population-week level; the association is attributed entirely to weather.",
"references": ["doi:10.1234/example"]
}
]
It asserts that no direct causal relationship exists between the pair, in either
direction, so nothing should render these as arrows. Note that an exclusion and
a bidirected edge between the same pair are not in conflict: “no direct
effect either way, but a shared unmeasured cause” is the exclusion restriction of
instrumental-variable analysis.
Example
{
"$schema": "https://opencausal.org/causaljson/0.2/causaljson.schema.json",
"graph": {
"id": "k3m9x2qp",
"doi": "10.83031/k3m9x2qp",
"version": 1,
"title": "Ice cream sales and drowning deaths",
"description": "Ice cream sales and drowning deaths move together across the year because both are driven by warm weather, not because either causes the other.",
"research_question": "Does ice cream consumption increase the risk of drowning?",
"type": "dag",
"created": "2026-04-13T09:00:00Z",
"authors": [
{
"given_name": "Abdullah",
"family_name": "Ademoğlu",
"orcid": "0000-0002-1825-0097",
"affiliations": [{ "name": "Utrecht University", "ror": "https://ror.org/04pp8hn57" }]
}
],
"license": "CC-BY-4.0",
"publisher": "Open Causal",
"statistical_unit": "population-week",
"provenance": "Constructed as a teaching illustration; no literature search was performed.",
"layout": { "coordinate_system": "screen" }
},
"nodes": [
{ "id": "weather", "label": "Warm weather", "variable_type": "continuous",
"unit": "Cel", "identifiers": ["wikidata:Q11466"],
"position": { "x": 50, "y": 50 } },
{ "id": "ice_cream", "label": "Ice cream sales", "role": "exposure",
"variable_type": "count", "position": { "x": 200, "y": 150 } },
{ "id": "drowning", "label": "Drowning deaths", "role": "outcome",
"variable_type": "count", "position": { "x": 350, "y": 150 } }
],
"edges": [
{ "source": "weather", "target": "ice_cream", "type": "directed", "sign": "positive",
"justification": "Demand for ice cream rises with temperature." },
{ "source": "weather", "target": "drowning", "type": "directed", "sign": "positive",
"justification": "Warm weather increases open-water exposure, and so the opportunities to drown." }
],
"excluded_edges": [
{ "source": "ice_cream", "target": "drowning",
"justification": "No mechanism by which buying ice cream changes drowning risk." }
]
}
Validation
CausalJSON is defined by a JSON Schema (draft 2020-12), identified by the URL that every
document carries in its $schema key:
https://opencausal.org/causaljson/0.2/causaljson.schema.json. A second, stricter profile describes
what Open Causal additionally requires before publishing a graph and minting its DOI: a
DOI, a version, a submitter, one of the Creative Commons licences, and a full name plus
ORCID for every author.
Some rules cannot be expressed in JSON Schema and are checked separately: node ids are
unique, every edge endpoint is a declared node, an excluded pair is not also asserted as
an edge, no directed edge runs backwards in time, and a graph declaring itself a
dag really is acyclic.
Metadata standards
The metadata follows existing standards rather than reinventing them — ORCID for people, ROR for organisations, SPDX for licences, DOI and the DataCite relation vocabulary for links, CURIEs for ontology terms — so a record maps onto DataCite mechanically.
Versions and feedback
This is CausalJSON 0.2, of August 2026, superseding
version 0.1 of December 2025. The format is
pre-1.0 and may still change in ways that break earlier files; each version is
identified by its $schema URL, so a document always says which one it
follows.
We welcome contributions from the research community. If you have suggestions or feedback, or would like to help develop the standard, please get in touch.