Skip to main content

EPD Schema (API)

Once an EPD has been created, the schema endpoint tells you exactly which fields it expects, which requirements will be checked against it, and what constraints apply. Use it to drive dynamic forms, build validation in your own integrations, or simply discover what a given EPD's spec looks like.

This page is the field-by-field reference for the response. For the concepts behind templates, locked fields, and constraints, see EPD Templating and Specs and Validation.

Endpoint​

GET /api/v2/epds/{epd_id}/schema/?view={full|summary}
Query paramDefaultEffect
view=full✅Returns the verbose internal shape. Stable across releases but carries inheritance metadata, legacy duals, and resolver-internal flags.
view=summaryReturns a slim, integrator-friendly shape. Recommended for new integrations and dynamic form generation.

The endpoint walks the EPD's pinned spec inheritance chain (PCR → standard → category-specific overrides) and emits the fully resolved set of input fields and requirements. Requirements are returned without their internal checks — those are evaluated server-side by the validation engine.

Removed items are omitted

The schema only ever contains fields and requirements that are live for this EPD's spec. When a PCR override removes an inherited field or requirement, it does not appear in either view — there is no _removed flag in the public response. (Internally the resolver marks removed items so the spec editor can render them struck-through, but that marker is never exposed here.) Treat the schema as authoritative: anything present is collected; anything removed is simply absent.

Two views at a glance​

view=fullview=summary
Top-level keysinput_field_defs, requirements, indicator_catalogfields, requirements, counts
Field key stylefield_id, is_required, group_id, show_if_jsonid, required, group, show_if
Inheritance metadataIncluded (_inherited, _inherited_from, override_mode)Stripped
Legacy dualsIncluded (constraints_json, scalar cross_ref_id)Stripped (only normalized form remains)
Best forInternal tooling, debugging the resolverAPI integrators, form builders, third-party validators
tip

If you're starting fresh, use ?view=summary. The full view exists for backwards compatibility and parity with the resolver's internal shape.


Summary view reference​

curl -H "Authorization: Bearer $TOKEN" \
"https://sandbox.smartepd.xyz/api/v2/epds/$EPD_ID/schema/?view=summary"

Response shape:

{
"fields": [ /* EpdSchemaSummaryFieldOut */ ],
"requirements": [ /* EpdSchemaSummaryRequirementOut */ ],
"counts": { "fields": 47, "requirements": 23 }
}

fields[]​

Each field describes one input on the EPD form.

KeyTypeMeaning
idstringStable field identifier. Use this as the document key when filling the EPD (e.g., product-name, a1_a3_gwp).
typestringInput type: text, number, date, select, multiselect, richtext, table, plant_picker, ec3_category, etc. Drives which form widget to render.
labelstringHuman-readable label for the form field.
help_textstringHint shown beneath the input. May be empty.
groupstring | nullThe id of the field group this field belongs to, or null if ungrouped. Use to render related fields together.
orderintDisplay order within the group (or globally, if ungrouped). Sort ascending.
requiredboolIf true, the EPD will fail submission unless this field has a value.
lockedboolIf true, the value is prefilled by the template and cannot be edited. Render as read-only and use locked_value for the displayed value.
locked_valueany | nullThe prefilled value when locked is true. Type matches type (string, number, list, etc.).
optionslist | nullFor select and multiselect types: the available choices, each as {label, value, ...}. null for free-text fields.
constraintslistValidation rules attached to this field. See Constraint shapes.
show_ifobject | nullConditional visibility rule. The field only renders when the rule evaluates to true against the current document values. See Conditional logic.
defaultany | nullDefault value to seed the input with on first render.

requirements[]​

Requirements are compliance clauses (typically from the PCR) that the EPD must satisfy. The schema endpoint exposes them so integrators can present them alongside the form, but the actual evaluation happens server-side when the EPD is submitted (/epds/{id}/submit/) or validated (/epds/{id}/validate/).

KeyTypeMeaning
idstringStable requirement identifier (e.g., EN15804_4.1).
titlestringShort heading.
textstringFull requirement text — show this to verifiers and manufacturers.
topicstringLogical grouping (e.g., lifecycle_stages, declared_unit). Use to organize the verifier sidebar.
comment_typestringOne of mandatory, guidance, info. Drives severity in the UI.
applies_tolist<string>Document types this requirement applies to: usually ["epd"], sometimes ["lca_report"] or ["pcr"].
refslist<string>The field.id values this requirement cross-references. Use to link a requirement back to the form fields it governs.
applies_ifobject | nullConditional applicability — a document-text search gate ({"document_type", "query", "mode"} plus an optional boolean negate; modes: all_phrases, any_phrases, any_topics, regex), evaluated against the submitted documents. Not the show_if shape — see Conditional logic.

counts​

A small object with {"fields": N, "requirements": M}. Useful for quick sanity checks ("does this template have the expected number of fields?") without iterating the lists.


Constraint shapes​

Each item in field.constraints has this shape:

{
"constraint_type": "range",
"parameters_json": { "min": 0, "max": 1000, "bounds_mode": "inclusive" },
"severity": "error",
"applies_if_json": null
}
KeyMeaning
constraint_typeOne of range, statistical, formula, allowed_values.
parameters_jsonType-specific params — see EPD Templating for full details. Keys per type: min/max/bounds_mode (range); distribution/threshold plus mean/std_dev for normal or geometric_mean/geometric_std_dev for lognormal (statistical); expression/operator with threshold, or min/max for the between operator (formula); values/case_sensitive (allowed_values).
severityerror blocks variant approve/auto-approve, and blocks a reference EPD's submission when the template has constraints_enforced on unless the author acknowledges the failure. warning and info are advisory.
applies_if_jsonOptional conditional gate — {field, operator, value} leaves with all/any combinators. See Conditional logic below. Not the show_if shape.

Spec-level constraints anchor implicitly to the field that declares them. Template-level constraints (the template's template_constraints list) additionally carry a required reference_field_id — the anchor field that violations are attributed to — and a display label.


Conditional logic​

Three different conditional gates exist in the schema, and they do not share a shape — don't copy snippets between them.

show_if (field visibility) puts the operator in the key:

{ "field": "product-form", "eq": "powder" }

Leaf operators: eq, ne, and contains (list membership, for multiselect fields). Compose with any / all:

{
"all": [
{ "field": "country", "eq": "US" },
{ "field": "applicable-in", "contains": "EU" }
]
}

A null or non-object condition, and any unknown leaf operator, default to visible. Combinator values must be arrays of condition objects — other shapes are outside the contract. (Note that {"any": []} is well-formed and evaluates false: no branch matched, so the field is hidden.) Evaluate against the in-flight document values to decide whether to render the field; the server applies the same rules at submission time (hidden fields are not validated), so client-side evaluation is purely for UX.

Constraint applies_if_json uses {field, operator, value} leaves with operators eq, neq, in, gt, gte, lt, lte, present, absent, composed with all / any — see the conditional application grammar.

Requirement applies_if is a document-text search (document_type + query + mode), unrelated to either of the above.


Groups​

Field groups aren't returned as a separate top-level list in either view (they live in the spec, not the schema response). To render grouped forms, bucket fields by group yourself:

curl -H "Authorization: Bearer $TOKEN" \
"https://sandbox.smartepd.xyz/api/v2/epds/$EPD_ID/schema/?view=summary" \
| jq '.fields | group_by(.group) | map({group: .[0].group, fields: map(.id)})'

If you need group labels and ordering, fetch the spec version directly via GET /api/v2/specs/{spec_id}/versions/{version}/, which exposes field_groups[] with id, label, description, order, and nested parent_group.


Full view reference​

The full view is the verbose default. It carries everything the summary does, plus the keys in the table below. Most integrations should prefer ?view=summary; reach for view=full only when you need the resolver's internal state (e.g., debugging an inheritance override).

Extra keys on each input_field_defs[] entry​

KeyMeaning
field_idSame as summary id.
is_requiredSame as summary required.
group_idSame as summary group.
placeholderUI placeholder for the input.
default_valueSame as summary default.
options_jsonSame as summary options.
show_if_jsonSame as summary show_if.
constraints_jsonLegacy. Inline constraint blob; superseded by the normalized constraints[] list. Prefer constraints[].
view_hiddenIf true, the field is collected during data entry but suppressed from the digital EPD view.
_inheritedtrue if the field came from a parent spec rather than the leaf spec.
_inherited_fromDisplay name of the parent spec, if inherited.
override_modeHow an inherited field was modified by a child spec: replace, merge, etc.

indicator_catalog​

Full view only. The slice of the LCIA indicator catalog this EPD's catalog-mode results table may draw rows from, or null when its spec declares no such table. Shaped {catalog_version, methods[], entries[]} — the same payload GET /api/v2/specs/indicator-catalog/ returns, restricted to the methods the table permits.

Because the summary view omits this key, integrations on ?view=summary should read the catalog from the standalone endpoint. See Writing Results Data for the entry shape and how the ids are used.

Extra keys on each requirements[] entry​

KeyMeaning
requirement_idSame as summary id.
document_typesSame as summary applies_to.
cross_ref_idsSame as summary refs.
cross_ref_idLegacy. Single scalar cross-reference held during the migration to cross_ref_ids[]. Prefer the list.
output_targetsWhere the requirement appears in generated output (e.g., ["pdf", "digital"]).
sources_jsonCitations and external references (e.g., PCR section numbers).
compliance_requirementshall, should, or may.
applies_if_jsonSame as summary applies_if.
inheritedtrue if the requirement came from a parent spec.

Worked example: render a form from the schema​

This jq snippet pulls the summary view and prints a flat outline grouped by section, with required fields marked:

curl -s -H "Authorization: Bearer $TOKEN" \
"https://sandbox.smartepd.xyz/api/v2/epds/$EPD_ID/schema/?view=summary" \
| jq -r '
.fields
| group_by(.group // "ungrouped")
| .[]
| "## \(.[0].group // "Ungrouped")\n" +
(map(
"- \(.id) [\(.type)]" +
(if .required then " *required*" else "" end) +
(if .locked then " (locked: \(.locked_value | tojson))" else "" end) +
(if (.constraints | length) > 0 then " — \(.constraints | length) constraint(s)" else "" end)
) | join("\n"))
'

For a richer integration, walk fields to build form widgets keyed by type, evaluate each show_if against the current document state, and render constraints inline as helper text or client-side validators. Then submit the populated document to PUT /api/v2/epds/{epd_id}/document/ — the server re-evaluates everything authoritatively at POST /api/v2/epds/{epd_id}/submit/.


  • EPD Templating — what locked, unlocked, and constrained fields mean in the verification model.
  • Specs and Validation — how requirements, fields, and the inheritance chain are authored upstream.
  • API Quickstart, step 6 — the end-to-end walkthrough where this endpoint first appears.