Docs › Pedigree JSON
Pedigree JSON, version 0.4
Status: Final. Date: 2026-10-06. Media-type-style name: pedigree-json/0.4.
File convention: *.pedigree.json.
Pedigree JSON is an open, plain-JSON format for exchanging a patient family history: everything a pedigree diagram shows (individuals, sex, vital status, dates, coded health conditions, parent/sibling/partner relationships) plus what research and clinical analysis need (coded conditions, stated-versus-inferred relationships, multi-family collections, a de-identification declaration). Think of it as an unformat: it invents almost nothing, and just writes down obvious ideas (name your fields, say what you know, say how sure you are) so that people will actually use them. It is vendor-neutral: nothing in it depends on any one tool, and anyone may read, write, implement, or build on it freely (see §16).
It is meant for the whole community that works with pedigrees: genetic counselors,
clinical geneticists, physicians, researchers, and the people who build their
software. It aims to sit alongside the formats those groups already use, not to
replace them, and §14 says how it maps to FHIR
FamilyMemberHistory, GA4GH Phenopackets, and PLINK .PED.
This document is the normative rule set. The companion JSON Schema is
pedigree-json-0.4.schema.json. The schema covers
structure and types; the rules in §12 are checked in code.
The key words MUST, MUST NOT, SHOULD, and MAY in this document are used as in
RFC 2119 and RFC 8174 when written in capitals; elsewhere "must", "should", and "may"
carry the same force.
Contents
- Requirements
- Document shape
- Layers
- Sections
- People
- Conditions
- Relationships and inference levels
- The disease index
- Forms of a document
- Presentation
- De-identification
- Semantic validation
- Reserved
- Standards alignment
- Changelog
- Conformance, versioning, and openness
1. Requirements
- Human readable. Plain JSON with spelled-out keys. Anyone can read a document without this spec.
- Flexible but unambiguous. Every field is optional, the core vocabulary is closed, and new information is added by adding fields, never by reinterpreting old ones.
- Optional multi-family. One family is a document; many are a collection of the same shape, for cross-family analysis.
- Shortcuts for analysis. Stated relationships (
kin), inferred ones (infer), and a derived disease lookup (index). - Encoded data. Inline base64 is possible but discouraged; link out first.
- Common use cases. Symbol positions, annotations, title text, free text.
- Color-coded symbols. Quarter-symbol fills driven by a legend.
- Link-out by standard URI (public FQDN or filesystem).
- LLM and database friendly. A dense compact form for models, a uniform normalized form for JSONB, and a JSON Schema for both.
- Not everything now. Anticipated extensions are reserved (§13).
2. Document shape
A document is one JSON object with four sections, each about a different thing:
| Key | Holds |
|---|---|
format |
Required. "pedigree-json/0.4": what this is and which version |
meta |
The document itself: family id, title and header text, free text, notes, links, the de-identification declaration |
pedigree |
The family: the condition dictionary, people, siblings, couples |
view |
How to draw it (presentation) |
index |
Derived lookup tables (rebuildable) |
It is either a pedigree (has pedigree) or a collection (has families), never
both. A collection is { format, meta?, families: [ … ], index? } where every family is
the same shape as a pedigree document, minus format, and with meta.family required.
{
"format": "pedigree-json/0.4",
"meta": {
"family": "doe",
"title": "Doe Family",
"text": "Referred for early-onset colon cancer; father's line uncertain."
},
"pedigree": {
"conditions": {
"crc": { "label": "Colon cancer", "codes": ["ICD10:C18.9"] },
"asthma": { "label": "Asthma", "codes": ["ICD10:J45"] }
},
"people": [
{ "id": 1, "sex": "M", "name": { "given": "Robert", "family": "Doe" },
"dob": "1955-02-10", "deceased": true },
{ "id": 2, "sex": "F", "name": { "given": "Carol", "family": "Doe" }, "dob": "1957-08-22" },
{ "id": 3, "sex": "M", "name": { "given": "Steven", "family": "Doe" }, "dob": "1982-03-01",
"proband": true,
"health": [{ "condition": "crc", "onset": "P38Y", "source": "documented" }, "asthma"],
"parents": [1, { "id": 2, "type": "step" }],
"view": { "pos": [120, 240] } },
{ "id": 4, "sex": "F", "name": { "given": "Nancy", "family": "Doe" }, "dob": "1984-11-19",
"parents": [1, 2] },
{ "id": 9, "sex": "M",
"health": [{ "condition": "crc", "source": "reported" }],
"kin": { "of": 3, "rel": "GRFTH" },
"infer": { "candidates": ["FF", "MF"], "chosen": "FF", "level": 2,
"basis": "only the father's line has recorded data" } }
],
"siblings": [[3, 4]],
"couples": [{ "partners": [1, 2], "consanguineous": true }]
},
"view": {
"show": { "title": true },
"labels": { "name": true, "dob": true, "health": [true] },
"legend": { "crc": { "color": "#d62728", "slot": "NW" },
"asthma": { "color": "#1f77b4", "slot": "NE" } }
}
}
2.1 Conventions
- Absent means not recorded.
nullis forbidden everywhere. (It is ambiguous for readers, awkward in JSONB, and a hazard in LLM output.) - Keys are spelled out. There are no aliases and no abbreviations. Density
comes from shorthands (§9.1) and
deduplication (the
conditionsdictionary), not from short key names. - Extension by
extonly. Vendor or local data goes underext(inmeta, or on a person), keyed by a reverse-DNS name:"ext": { "org.example.lab": { … } }. Core objects are closed; a writer must not add other keys. - Readers are lenient, writers are strict. A reader that meets an
unknown key must keep it when re-writing and should warn. A writer emits
only keys defined here or under
ext. This lets a v0.4 reader survive a later minor version. - Ids.
idis a positive integer unique within its family. It carries no meaning (not birth order, not generation). A reference across families is the string"<family>:<id>"; a family id must not contain:. - Dates are ISO 8601, truncatable:
YYYY,YYYY-MM,YYYY-MM-DD. Durations (age at onset) are ISO 8601 durations, e.g.P38Y,P6M.
3. Layers
The sections are also layers that can be stripped independently. A consumer that wants
only the facts reads pedigree (and meta) and ignores the rest.
| Layer | Where | Contents |
|---|---|---|
| Facts | meta, pedigree |
Stated and deterministic facts |
| Inference | kin, infer on a person |
What was said vaguely, and what was concluded from it (§7) |
| Presentation | view (and a person's view) |
Positions, colors, which labels to draw (§10) |
| Derived | index |
Rebuildable lookup tables (§8) |
The inference levels are defined in §7: the higher the level, the higher the uncertainty.
4. Sections
4.1 meta
Everything about the document rather than the people in it. All keys optional.
| Key | Type | Notes |
|---|---|---|
family |
string | Family id (opaque, no :). Required inside a collection |
title, subtitle, footer |
string | Header text drawn above and below the diagram |
text |
string | Free-text description of the family. Not the source narrative (see narrative) |
notes |
array | Annotations: strings or {text, author?, date?} |
as_of |
date | When ages were recorded: a person's age is their age on this date |
narrative |
string or Reference | The original free-text narrative this was extracted from: inline text, or a link or encoded payload (§5.1). A plain string is a primary form, not a shorthand, so normalization leaves it alone. Discouraged: it costs space and can carry PHI (§11) |
links, attachments |
array | §5.1 |
identifiers |
array | {system, value} for the family as a whole (e.g. a study id) |
deidentified |
array | §11 |
attestation |
object | §11.4 |
ext |
object | Extensions |
A collection's own meta may hold title, text, notes, links, deidentified,
attestation and ext.
4.2 pedigree
Required, but may be empty ("pedigree": {}): a document grows as people are added. The family itself.
| Key | Type | Notes |
|---|---|---|
conditions |
object | The condition dictionary (§6) |
people |
array | §5 |
siblings |
array | Arrays of two or more person ids. One array per sibling group |
couples |
array | {partners: [a, b], consanguineous?: bool}. Exactly two partners. A person with several partners has several entries |
4.3 view and index
5. People
| Key | Type | Notes |
|---|---|---|
id |
integer | Required |
sex |
M F U O |
Required. Closed set. U = not recorded, O = other/intersex. Mapping to FHIR and .PED in §14 |
name |
string or object | {given?, middle?, family?, text?}. A plain string is the text form (e.g. a pseudonym "patient_123") |
dob |
date | Truncatable |
dod |
date | Date of death. Implies deceased: true |
deceased |
boolean | true = deceased, date unknown (or known); false = known living; absent = not recorded |
age |
integer | Age in years on meta.as_of, for a living person. Use it when dob is not known (a stated age and a dob must agree within a year, V15) |
age_at_death |
integer | Age in years at death, for a deceased person (V15) |
proband |
boolean | The index case. At most one per family |
ancestry |
object | {ashkenazi_jewish?, race_ethnicity?}. race_ethnicity is one of white, black_african_american, hispanic_latina, asian, pacific_islander, native_american_alaska_native, other, unknown: broad categories; map to a model's own when running it |
genetics |
array | Genetic test results: {gene, result, variant?, zygosity?, date?}. gene is an HGNC symbol; result is pathogenic, likely_pathogenic, vus or negative (§6.4) |
risk_factors |
object | Personal factors that risk models use, normally on the proband (§6.4) |
health |
array | §6.2 |
parents |
array | Up to two. A bare id is a biological parent; {id, type} qualifies it. type: bio (default), adopt, step, foster |
kin |
object | How the user described this person (§7) |
infer |
object | What was concluded from kin |
same_as |
string | Global reference to the same real person in another family |
notes, links, attachments, identifiers |
array | As on the pedigree |
view |
object | {pos?, fill?} (§10) |
ext |
object | Extensions |
Siblings and partners are not stored on the person; they live in the
pedigree's siblings and couples. Parents are stored on the child, which
makes "who are this person's parents" a local read.
5.1 References: links and attachments
One shape serves links, attachments, and narrative:
{ "href": "https://ehr.example.org/family/48213", "title": "EHR record",
"rel": "related", "mediaType": "text/html", "sha256": "…" }
hrefis an RFC 3986 URI:https://(public FQDN),file:///…, or a relative reference resolved against the document's own location.datais base64 (standard alphabet, RFC 4648 §4) and replaceshreffor an inline payload. Exactly one ofhrefordatais present.mediaTypeis required withdata, andsha256(hex, of the decoded bytes) is required withdata. A writer should warn above 64 KiB decoded. Inline data is possible but discouraged: link out first.linksentries carryhrefonly.attachmentsentries may carry either.- A bare string in
linksis shorthand for{href}.
6. Conditions
6.1 The dictionary
Every condition mentioned on a person is declared once in the family's
conditions dictionary. People refer to it by key. There are no free-text
condition strings on people.
"conditions": {
"crc": { "label": "Colon cancer",
"codes": ["ICD10:C18.9", { "system": "http://snomed.info/sct", "code": "363406005" }] }
}
- The key is
[A-Za-z0-9_.-]+and is local to the family. labelis human text.codesis an array of codings. Each is either"PREFIX:code"shorthand or a FHIR-style{system, code, display?}with a full system URI. Coding is encouraged (ICD-10 is recommended) but never required, and the standard never validates a code against its system.- A writer must expand known prefixes to system URIs in the normalized profile and may keep them in the compact profile. Unknown prefixes pass through unchanged.
| Prefix | System URI |
|---|---|
ICD10 |
http://hl7.org/fhir/sid/icd-10 |
ICD10CM |
http://hl7.org/fhir/sid/icd-10-cm |
SNOMED |
http://snomed.info/sct |
HPO |
http://purl.obolibrary.org/obo/hp.owl |
OMIM |
http://www.omim.org |
MONDO |
http://purl.obolibrary.org/obo/mondo.owl |
6.2 Health entries
"health": [
{ "condition": "crc", "onset": "P38Y", "status": "affected", "source": "documented",
"notes": ["Diagnosed at routine screening"] },
"asthma"
]
conditionis a dictionary key. A bare string inhealthis shorthand for{condition: <key>}.status:affected(default),unaffected(asked and denied: different from not recorded),carrier,presymptomatic,unknown.onsetis an ISO 8601 duration (age) or a truncatable date.source:reported(someone said so) ordocumented(a record says so).notes: as on a person.- Order is preserved and can be clinically meaningful (first diagnosed first).
- Cancer details for risk models, all optional:
laterality(left,right,bilateral),receptors({er?, pr?, her2?}, eachpositive,negativeorunknown),histology(text),location(proximalordistal, colorectal) andmarkers({msi?, ihc?}:msiishigh,low,stableorunknown;ihcmapsMLH1,MSH2,MSH6,PMS2topresent,absentorunknown).
6.3 Canonical condition key for cross-family matching
Local keys collide across families ("c1" in one family is not "c1" in the
next). Everything that crosses families uses the canonical condition key:
- the first entry of
codes, asPREFIX:code(using the prefix table where the system is known, otherwise<system>|<code>); else text:plus thelabellowercased with whitespace collapsed.
Coded and uncoded spellings of the same disease therefore do not match each
other. That is a deliberate consequence: a writer that wants cross-family
analysis should code its conditions. Hierarchical matching (ICD10:C18 covering
ICD10:C18.9) is a query-time behavior and is not stored.
6.4 Clinical data for risk models
Pedigree risk models (Gail, Claus, Tyrer-Cuzick, BOADICEA, BRCAPRO, PREMM5, MMRpro) each need a different minimum of data; this format carries the union of what they ask for, all optional. Models use the structures above plus:
- A cancer is a health entry whose condition has a
sitein the conditions dictionary ({"label": "Breast cancer", "site": "breast"}), withonset(age at diagnosis) and the optional details of §6.2.siteis one ofbreast,ovary,prostate,pancreas,colorectum,endometrium,stomach,small_bowel,urothelial,hepatobiliary,brain,sebaceous_skin. A condition with nositeis matched by words in its key and label, but a statedsitealways wins. - Ages: a living person has
doborage; a deceased person hasdodorage_at_death.meta.as_ofsays whenagewas recorded. - Genetic results are
geneticson the person . risk_factors(all optional):menarche_age,first_live_birth_age,parity,menopausal,menopause_age,hrt,hrt_years,oral_contraceptives(never,former,current),bmi,height_cm,benign_biopsies,atypical_hyperplasia,lcis,breast_density(atod),alcohol_g_per_day. Absent means not recorded; a woman with no live births hasparity: 0and nofirst_live_birth_age.
7. Relationships and inference levels
Relationships carry an inference level (0 to 3). The level rises with the uncertainty.
| Level | Name | Meaning | Stored in |
|---|---|---|---|
| 0 | known | The user said it | parents, siblings, couples, kin |
| 1 | deterministic | Follows logically (children of the same two parents are siblings) | Core, optionally (derivable, so writers may omit) |
| 2 | probable | A choice among possibilities was made on a stated basis | infer only |
| 3 | possible | Several possibilities and none chosen | infer only (nothing is materialized) |
Level 0 is never altered and never lost. What the user said is kept
verbatim in kin, even after it has been resolved to a graph edge.
7.1 kin: what the user said
"kin": { "of": 3, "rel": "GRFTH", "side": "paternal" }
of: the id of the person the relation is relative to (usually the proband).rel: an HL7 v3 RoleCode (the value set FHIRFamilyMemberHistory.relationshipuses), e.g.FTH,MTH,GRFTH,GRMTH,GGRFTH,SIB,AUNT,UNCLE,COUSN. Asystemkey may name another vocabulary; an unrecognized code is preserved and means "unresolvable", never an error.side: optionalpaternalormaternal, which fixes the first hop.
A person with kin need not have any parents: kin alone places them.
7.2 Paths
A path is a string of steps read left to right from of to the person.
Empty string = the same person.
| Step | Move |
|---|---|
F |
to the father |
M |
to the mother |
P |
to a parent, sex not specified |
C |
to a child |
S |
to a sibling |
W (partner) is reserved for in-laws. The sex of a final F/M step must
agree with the person's sex (rule V9).
A RoleCode expands to one or more candidate paths (a table the spec
maintains; GRFTH = FF, MF; PGRFTH = FF; MGRFTH = MF; FTH = F;
GRPRN = FP, MP; AUNT = PS with the person's sex deciding). side
filters the candidates.
7.3 infer: what was concluded
"infer": { "candidates": ["FF", "MF"], "chosen": "FF", "level": 2,
"basis": "only the father's line has recorded data" }
| Key | Notes |
|---|---|
candidates |
Every path consistent with kin (after side) |
chosen |
The path taken, if any. Must be one of candidates |
level |
1 if exactly one candidate (nothing was a guess), 2 if chosen was picked among several, 3 if none was chosen |
basis |
Free text: why this choice |
- Level 1/2: a reader walks
chosenfromof. At each step it uses the relative already recorded there if it is unambiguous, and otherwise creates a placeholder person (placeholder: true) so the graph is complete. The resulting edges are tagged with the level. They are materialized in memory only:parentson other people is not edited. - Level 3: nothing is materialized. The person hangs off
ofbycandidatesalone. inferis always re-derivable and discardable. New level-0 facts (a user later says "my mother's father had colon cancer") invalidate and recompute it. Code must never treat level ≥ 2 as authoritative.- The default heuristic for choosing is implementation-defined and is named in
basis. The machine-readable part islevel.
8. The disease index
An optional derived lookup for fast "who had what, and how are they related".
"index": {
"of": "sha256:9f2c…",
"proband": 3,
"conditions": {
"ICD10:C18.9": [
{ "id": 3, "path": "", "level": 0 },
{ "id": 9, "paths": ["FF", "MF"], "chosen": "FF", "level": 2 }
],
"ICD10:J45": [ { "id": 3, "path": "", "level": 0 } ]
}
}
- Keyed by the canonical condition key (§6.3).
- Each entry is a carrier (
statusaffected or carrier;statusis written only when it iscarrier) with its path from the proband, and, for level 2 or 3, all candidatepathsand thechosenone.pathis for a single resolved path. An entry'slevelis the highest level of any relationship on its path: a path is only as certain as its weakest link. A carrier with no known relation to the proband is level 3 with no path. - Persons with an
inferblock take their paths from it (composed with the path from the proband tokin.of); everyone else takes the shortest path through the graph, preferring the lower level on ties. - Paths for non-ancestors use the same steps, e.g. a first cousin is
PSC. - Staleness.
ofissha256:of the RFC 8785 (JCS) form of the document with everyindexand everyview(pedigree, family, person) removed, so moving a symbol never makes an index stale. Ifofis missing or does not match, a reader ignores the index and rebuilds it. The index is never authoritative. - Queries name a mode: strict (levels 0–1), probable (0–2), any-candidate (0–3, a level-3 carrier matches if any candidate path does).
- A collection may carry a collection-level
indexwhose entries addfamily:{ "family": "doe", "id": 9, … }. This supports a cohort query ("every carrier ofICD10:C18.9") without opening each family. - Building an index in memory is O(people); the stored index matters for large cohorts, where a database can filter families on it before loading them.
9. Forms of a document
9.1 Compact and normalized profiles
Both are valid Pedigree JSON and mean the same thing.
| Compact | Normalized | |
|---|---|---|
| Shorthands | used wherever lossless | none |
parents |
[1, {"id":2,"type":"step"}] |
[{"id":1},{"id":2,"type":"step"}] |
health |
["asthma"] |
[{"condition":"asthma"}] |
name |
may be a string | always an object |
notes, links |
strings | objects |
codes |
"ICD10:C18.9" |
{system, code} |
| Use | LLM output, hand authoring, files | databases, schema-constrained decoding |
A reader accepts both. A writer picks one. Normalization is the canonical ingest step for a database.
9.2 Canonical serialization
Byte-identical output needs one rule set. Because JSONB discards key order and whitespace, key order is not part of the format: canonical bytes are the RFC 8785 (JCS) form, which sorts keys and removes whitespace. Equality of two documents is semantic (parsed value equality).
Within that, array order is fixed:
pedigree.people: ascendingid.pedigree.siblings: members ascending, groups by lowest member.couples: by lower partner id, partners ascending.parents: ascending id.deidentified: sorted, unique.health,notes,links,attachments: order preserved (insertion / elicitation order can be meaningful).- In the compact profile a writer uses a shorthand whenever it loses nothing,
and omits defaults (
type: "bio",status: "affected").
Pretty-printed JSON is fine for humans and is not canonical.
9.3 Files
- A document is UTF-8 JSON, conventionally
*.pedigree.json. Any.jsonfile can be recognised as Pedigree JSON by itsformatvalue, which starts withpedigree-json/. - A multi-family dataset is also commonly stored as JSON Lines: one pedigree per line, each a complete document. This streams, greps, and suits model output.
- In PostgreSQL store a document as
jsonb. Containment (@>) andjsonb_path_opsGIN indexes work directly on the normalized profile.
10. Presentation: view
Everything about how a pedigree is drawn is in view and can be stripped
without losing a fact.
"view": {
"show": { "title": true, "subtitle": true, "footer": true },
"labels": { "name": true, "dob": true, "health": [true, false, false] },
"legend": { "crc": { "color": "#d62728", "slot": "NW" } }
}
show,labels: which header text and per-symbol label lines are drawn (a pedigree-wide setting).labels.health[i]selects the i-th health entry of each person.- Quarter symbols.
legendmaps a condition key to acolor(CSS hex) and aslot:whole, a halfNSEW, or a quarterNWNESWSE. Each person's symbol is filled from their affected conditions, each in its slot. A person's ownview.fill: [{condition, slot?, color?}]overrides that for one symbol. - The legend is required wherever symbols are specified. Every condition
key named by a person's
view.fillmust be in the same family'sview.legend, and a family with no legend draws no fills. A collection has no legend of its own: each family stays self-contained and extractable. - Person
view.posis[x, y](numbers) for the symbol's position. - Canvas notes.
notesis a list of free-text notes drawn on the diagram, each{ text, pos: [x, y], headline?, size?, color?, bold? }:textandposare required,sizeis a font size from 6 to 96 (default 16),colora CSS hex colour (default black),headlineis drawn in bold above the text. Because they are only drawn, a canvas note is presentation: strippingviewloses no fact. Free text that should be kept but not drawn is a fact-side annotation instead:meta.notesfor the family (or a person'snotes), andmeta.narrativefor the text the pedigree was made from (§4.1).
"view": {
"notes": [ { "headline": "Check", "text": "Dates from Aunt May, not confirmed", "pos": [320, 40], "color": "#d62728", "bold": true } ]
}
11. De-identification
The format's commitment is narrow: make it easy to see, and mechanically check, which fields a writer claims are free of protected health information. It does not verify the claim.
11.1 The deidentified list
meta.deidentified is an array of field paths. An entry asserts that the named
field contains no PHI. The field itself stays in place, with its own name, and the
document stays valid.
{
"format": "pedigree-json/0.4",
"meta": {
"family": "fam_007",
"title": "Family 007",
"deidentified": ["meta.family", "meta.title", "pedigree.people.name",
"pedigree.people.dob", "pedigree.people.dod"]
},
"pedigree": {
"people": [
{ "id": 3, "sex": "M", "name": "patient_123", "dob": "1982", "proband": true },
{ "id": 1, "sex": "M", "name": "patient_124", "dob": "1955", "dod": "2011" }
]
}
}
- Scope and paths. A path is written from the family object, so it always names
exactly one place:
meta.title,pedigree.people.name(thenameof every person in this family),pedigree.people.health.notes(thenotesof every health entry). A person may carry its owndeidentifiedlist, with bare names (name,dob,health.notes), to add coverage for itself. Coverage is the union of all applicable lists. - Each family carries its own list (in its
meta) in a collection, so a family is self-contained. The collection's ownmeta.deidentifiedcovers only the collection's ownmetafields. - Exact names. Entries are case-sensitive canonical field names (
dob, notDOB). An entry that is not a PHI-capable field is an error (V11), because a typo that silently fails to cover a field is the dangerous failure. - An entry for a field that is absent is allowed, so one list can be reused across a dataset.
- The list is sorted and unique in canonical form.
- Listing a field never changes its value. A de-identifying writer must already
have replaced the value (
"patient_123", a year-only date) before listing it.
11.2 PHI-capable fields
A closed list. Anything here that is present and not covered by a
deidentified list is treated as possibly containing PHI.
| Field | HIPAA identifier (45 CFR 164.514(b)(2)) |
|---|---|
pedigree.people.name |
Name |
pedigree.people.dob, .dod |
Dates (all elements except year) |
meta.identifiers, pedigree.people.identifiers |
MRN, SSN, plan/account/license numbers, device and other ids |
meta.links, pedigree.people.links |
Web URL, and any file path |
meta.attachments, pedigree.people.attachments |
Photographic images, finger/voice prints, any document |
meta.family, .title, .subtitle, .footer, .text, .notes, .narrative; pedigree.people.notes, pedigree.people.health.notes |
Free text: any of the 18 |
meta.ext, pedigree.people.ext |
Opaque: may contain anything |
contact (reserved) |
Address, phone, fax, email, IP |
Not PHI-capable: sex, proband, health, parents, kin, infer,
siblings, couples, view, index, conditions. They are health data and
can still re-identify (§11.5), but deidentified does not speak
to them.
11.3 What this gives a researcher
Scanning 1000 pedigrees:
Criteria check: does each file list the fields my study requires?
SELECT id FROM pedigrees WHERE doc->'meta'->'deidentified' ?& array['meta.title','pedigree.people.name','pedigree.people.dob','pedigree.people.dod'];Completeness check: is every PHI-capable field that is actually present covered? This is a pure function of the document (
isFullyDeidentified(doc)), defined by §11.2.De-identifying export is defined and deterministic: drop every PHI-capable field that is not covered by a list. One exception: a family's
meta.familyid is required inside a collection, so an uncovered one is replaced by an opaque id (and listed as covered) rather than dropped, andsame_asand index references to it are rewritten.
11.4 Attestation
deidentified is machine-checked coverage. attestation is human
accountability and is separate:
"attestation": { "method": "safe-harbor", "by": "J. Smith, Data Steward",
"date": "2026-09-30", "statement": "Reviewed against 45 CFR 164.514(b)(2)." }
method: safe-harbor, expert-determination, or other. All keys optional.
11.5 Limits
deidentifiedis an assertion, not a verification. The format never calls data "anonymous".- Clinical content and pedigree structure are not covered. A small pedigree with a rare condition can be re-identifying even when every listed field is clean.
- Safe Harbor specifics (year-only dates, ages over 89) are the writer's responsibility. v0.4 defines no age-bucketing value.
12. Semantic validation
The schema cannot express these. A conforming validator reports them; the level is Error or Warning.
| # | Rule | |
|---|---|---|
| V1 | id unique within a family; person ids referenced by parents, siblings, couples, kin.of, index exist |
E |
| V2 | At most one proband per family |
E |
| V3 | At most two parents; a person is not their own ancestor |
E |
| V4 | couples[].partners are two distinct people |
E |
| V5 | A person with dod does not have deceased: false |
E |
| V6 | health[].condition is a key of the family's pedigree.conditions |
E |
| V7 | dob not after dod; parent dob before child dob |
W |
| V8 | infer.chosen ∈ infer.candidates; level agrees (§7.3) |
E |
| V9 | Sex of the person agrees with the last F/M step of chosen |
E |
| V10 | A data reference has mediaType and a sha256 matching the decoded bytes; href xor data |
E |
| V11 | Every deidentified entry is a PHI-capable field path for its scope |
E |
| V12 | Every condition in a view.fill is in the same family's view.legend |
E |
| V13 | index.of matches the document (§8); mismatch is not an error but means "rebuild" |
W |
| V14 | A collection's meta.family ids are unique |
E |
| V15 | age_at_death only on a deceased person, age only on a living one; ages agree with dob, dod and meta.as_of (within a year) |
W |
| V16 | A diagnosis (onset in years) is not later than the person's age or age_at_death |
W |
| V17 | laterality and receptors only on a breast cancer; location only on a colorectal cancer |
W |
| V18 | risk_factors agree with themselves: no first_live_birth_age with parity 0, no menopause_age when menopausal is false, no first birth before menarche |
W |
13. Reserved for future versions
Not specified in v0.4; the names are reserved so they are not used for
something else: twins and zygosity (multiple), pregnancy and infertility
(reproductive), contact, same_as semantics beyond identity, relationship status
on couples (separated, divorced), and path step W.
14. Standards alignment
| Concern | Standard |
|---|---|
| Dates, durations | ISO 8601 |
| Canonical bytes | RFC 8785 (JCS) |
| URIs | RFC 3986 |
| Inline binary | RFC 4648 base64 |
| Stated relationship | HL7 v3 RoleCode (FHIR FamilyMemberHistory.relationship) |
| Condition coding | FHIR Coding (system + code); ICD-10, SNOMED CT, HPO, OMIM, MONDO |
| Media types | IANA |
| Link relations | IANA link relations (rel) |
| Structure | JSON Schema 2020-12 |
| Sex | M F U O → FHIR administrative-gender (male female unknown other); .PED 1 2 0 0 (O lossy) |
| Interchange | FHIR FamilyMemberHistory, GA4GH Phenopackets (Family, Pedigree), PLINK .PED |
.PED mapping. The paternal and maternal columns are derived from parents
(biological parents only). The phenotype column needs a caller-supplied definition of
"affected", given as a condition key or canonical key (§6.3).
.PED cannot carry codings, inference, or presentation, so conversion to .PED is lossy.
15. Changelog
v0.4 (2026-10-06) — first public version of Pedigree JSON: "format": "pedigree-json/0.4",
four sections (meta, pedigree, view, index), a conditions dictionary with coded
conditions, kin/infer with inference levels, index, multi-family collections,
deidentified and attestation, view.legend quarter-symbol fills, narrative, and
references with inline base64.
16. Conformance, versioning, and openness
Conformance. A document conforms if it validates against the JSON Schema and
violates no Error-level rule in §12. A writer conforms if
everything it emits is a conforming document and it follows §2.1
(no null, no keys outside this specification except under ext). A reader
conforms if it accepts both profiles (§9.1),
preserves unknown keys when re-writing, and treats infer and index as
non-authoritative.
Versioning. The format value carries major.minor. A minor version may add
optional fields and values but never changes the meaning of an existing one, so a
v0.4 reader can read a later v0.x document leniently (§2.1). A
change of meaning requires a new major version (the leading 0 means the format is
still young, but v0.4 is considered stable for implementation).
Openness. Pedigree JSON is an unformat: an open set of practices, not a product,
built from ideas that should already be obvious. Anyone may use
it, implement it, copy it, adapt it, and build on it, for any purpose, with no permission,
fee, registration, or attribution required. You are encouraged to innovate on it: add
data under ext, propose changes, or fork it. If you extend it in ways others may want,
sharing what you did helps everyone, but nothing requires it.