Create workflow and experiment records¶
OSC describes workflows and experiments using OGC API Records. A workflow names its project using osc:project; an experiment names its workflow using osc:workflow. Write these into record properties directly: OscExtension.apply_project() and apply_product() describe STAC project/product shapes, not these records.
import pystac
from pystac.extensions.ogc_record import OGCRecord
workflow = OGCRecord(
id="ocean-workflow",
properties={
"type": "workflow",
"title": "Ocean temperature workflow",
"description": "Processing steps for sea surface temperature",
"osc:project": "ocean-science",
},
)
workflow.add_link(pystac.Link(
rel="related", target="https://example.org/projects/ocean-science.json",
))
workflow.add_link(pystac.Link(
rel="child", target="https://example.org/experiments/experiment-42.json",
))
experiment = OGCRecord(
id="experiment-42",
properties={
"type": "experiment",
"title": "Ocean workflow execution 42",
"description": "An execution producing the ocean-temperature product",
"osc:workflow": workflow.id,
},
)
experiment.add_link(pystac.Link(
rel="related", target="https://example.org/workflows/ocean-workflow.json",
))
experiment.add_link(pystac.Link(
rel="child", target="https://example.org/products/ocean-temperature.json",
))
experiment.add_link(pystac.Link(
rel="environment", target="https://example.org/experiments/environment.yml",
))
experiment.add_link(pystac.Link(
rel="input", target="https://example.org/experiments/parameters.json",
))
experiment.keywords = ["ocean", "temperature"]
experiment.language = {"code": "en"}
document = experiment.to_record_dict()
assert document["type"] == "Feature"
assert document["properties"]["type"] == "experiment"
assert "stac_version" not in document
restored = OGCRecord.from_dict(document)
assert restored.properties["osc:workflow"] == "ocean-workflow"
The example's workflow and experiment resource type strings describe the application; they are not OscType enum members. It demonstrates the OSC relationship conventions, without asserting full OGC conformance. No OSC STAC schema declaration is added to these records: that schema only accepts project/product shapes.
Read, write, and copy¶
Use OGCRecord.from_dict(document) on decoded JSON. Use json.dumps(record.to_dict()) to serialize it. Pass href= to from_dict() to set the self link, replacing any incoming self link. Metadata is copied even when preserve_dict=False; migrate is accepted for PySTAC compatibility but has no effect.
record.clone() copies metadata and clones links and assets. record.record_metadata is a live view of record.properties; its to_dict() returns a deep copy. Assigning None to a common metadata accessor removes that property.
Export a STAC Item¶
from datetime import datetime, timezone
record = OGCRecord(
id="observation",
datetime=datetime(2026, 1, 1, tzinfo=timezone.utc),
properties={"type": "dataset", "title": "Observation"},
)
item = record.to_stac_item()
assert "stac_version" in item.to_dict()
STAC export requires datetime or both start_datetime and end_datetime. An OGC time member alone does not satisfy this requirement. Export does not validate the resulting Item. See validation boundaries.