Documentation preview. These pages target Registryctl v0.15.0, which is not published yet. Public download commands will not work until that release exists. For runnable released instructions, use v0.13.0.
This guide shows the caller-side OpenFn pattern for Registry Relay: an OpenFn workflow reads protected registry records, metadata, relationships, or aggregate outputs through a scoped Relay credential.
Use this pattern only when the workflow is authorized to read registry data directly. If the workflow needs a governed trust decision, such as whether a farmer is eligible or a certified value such as farmed land size, call Registry Notary instead.
Adaptor
Section titled “Adaptor”The current OpenFn language adaptors for Registry Stack live in:
https://github.com/jeremi/openfn-language-registry-stackWhen that repository is configured as an OpenFn local adaptor repository, Lightning and the worker load the Relay package as:
@openfn/language-registry-relay@localConfigure an OpenFn credential with:
relay_base_url: Registry Relay service base URL.token: bearer token or API key for the Relay caller credential.
The adaptor sends credentials as Authorization: Bearer <token>. It does not
send x-api-key.
Lab credentials
Section titled “Lab credentials”The public Registry Stack lab publishes current demo service URLs, scopes, and tokens at:
https://lab.registrystack.org/api/lab.jsonFor the agriculture Relay examples below:
- use
agri-row-readerfor row reads; - use
agri-aggregate-readerfor aggregate reads; - use
agri-metadatafor dataset discovery; - use
agri-evidence-onlyfor evidence offering discovery.
The lab UI at https://lab.registrystack.org shows the same public demo
credentials.
The examples use NAGDI, a fictional agriculture ministry from the demo lab.
Read one record
Section titled “Read one record”execute( getRecord({ dataset: "agri_registry", entity: "farmer", id: dataValue("farmer_id"), purpose: "https://demo.example.gov/purpose/nagdi/climate-smart-input-support", fields: ["id", "district", "registration_status"], as: "farmer", redactDataPaths: ["farmer_id"], }),
fn((state) => { const farmer = state.data.farmer.record;
return { ...state, data: { ...state.data, decision_input: { farmer_id: farmer.id, district: farmer.district, relay_request_id: state.data.farmer.request_id, }, }, }; }),);List records
Section titled “List records”Collection reads require an explicit limit and at least one filter unless
allowUnfiltered: true is set.
execute( listRecords({ dataset: "agri_registry", entity: "farmer", purpose: "https://demo.example.gov/purpose/nagdi/climate-smart-input-support", filters: { district: "north", "id.in": ["FARMER-1001", "FARMER-1002"], }, fields: ["id", "district", "registration_status"], limit: 50, as: "farmers", }),);Query an aggregate
Section titled “Query an aggregate”execute( queryAggregate({ dataset: "agri_registry", aggregate: "voucher_opportunities_by_district_crop_risk_input", purpose: "https://demo.example.gov/purpose/nagdi/climate-smart-input-support", dimensions: ["district_code"], measures: ["eligible_opportunity_count"], filters: { season: ["2026A"] }, maxRows: 100, as: "district_summary", }),
fn((state) => { const observations = state.data.district_summary.observations;
return { ...state, data: { ...state.data, north_voucher_opportunities: observations.find((row) => row.district_code === "north")?.eligible_opportunity_count ?? 0, }, }; }),);Discovery
Section titled “Discovery”execute( discoverDatasets({ as: "catalog" }), getEntitySchema({ dataset: "agri_registry", entity: "farmer", as: "farmer_schema", }), listEvidenceOfferings({ as: "evidence_offerings" }),);Relay evidence offering routes are discovery only. They tell the workflow which Registry Notary endpoint to use; Relay does not evaluate a claim.
Guardrails
Section titled “Guardrails”The adaptor is intentionally stricter than a generic HTTP helper:
- row, relationship, and aggregate helpers require
purpose; listRecordsrequireslimitand filters unlessallowUnfiltered: true;X-Request-Idusesstate.data.request_idwhen present;traceparentis forwarded whenstate.data.traceparentis present;ETag,Retry-After, request id, and pagination cursors are preserved;- credentials, raw request material, and
configurationare removed from final state; - Problem Details are reduced to
code,status,title, andretryable; the adaptor does not expose Problem Detailsdetail.
Common result branches are succeeded, not_modified, not_found,
auth_failed, forbidden, filter_required, cursor_invalid,
retryable_infrastructure, and failed.