Structured Output the Reporting System Accepts
An analyst has done everything right. She extracted the activity data with its source, verified the emission factors against the database, reconciled the totals to last year. Then she hands the result to the reporting platform as a tidy paragraph: "Lyon facility used 48,210 kWh of electricity in September 2026, from the metered utility bill, paired with the 2026 grid factor." The platform cannot ingest it. The assurer, weeks later, cannot query it. A reviewer has to read the sentence and retype the number into a field. Every bit of rigour she built is now trapped inside prose that no system can read. This lesson is about the last, unglamorous step that decides whether all the earlier rigour survives: making the model emit a clean, tagged record instead of a paragraph.
Prose Is Where Lineage Goes to Die
The reporting platform and the external assurer need the same thing, and it is not paragraphs. They need lineage: a structured trail in which every figure carries, in its own labelled fields, what it is worth, what unit it is in, where it came from, whether it is measured or estimated, and which period it covers. A sentence can contain all of that information and still be useless, because to a system a sentence is an opaque blob. The platform cannot map "48,210 kWh" to its electricity-consumption field if the number is buried in a clause. The assurer cannot filter for "every estimated figure in Scope 3" if "estimated" is a word in a paragraph rather than a tag on a record. Prose is human-readable and machine-opaque, and disclosure now runs on machines reading data.
This is why structured output is not a formatting preference, it is the carrier of everything the previous lessons produced. You can extract with perfect provenance, but if the provenance does not travel as a field, it is lost the moment the value leaves your spreadsheet. You can label a figure primary or estimated in your head, but if that label is not a tag on the record, the distinction the assurer lives by simply is not in the data. The structured record is the container that keeps source, unit, and data type attached to the number all the way into the platform and the assurance file. Lose the container and you lose the lineage, no matter how careful the work upstream was.
Consider what a sustainability dataset actually has to do once it leaves the analyst. It is imported into a reporting platform, which maps each figure to a specific disclosure field. It is sampled by an assurer, who needs to pull individual datapoints and trace each to its origin. It is filtered, sliced by scope, by category, by primary-versus-secondary, by facility, to answer questions like "what share of our Scope 3 is primary data?" It is aggregated into totals that must reconcile, and it is queried again next year when someone reconstructs a prior figure. Every one of those operations is a machine operating on fields. Not one of them can be performed on a paragraph. The moment you express a datapoint as a sentence, you have opted it out of every downstream system that disclosure depends on, and you have volunteered a human to manually re-key it back into fields at each stage, which is both slow and a fresh opportunity to introduce error. Prose is not a neutral choice that is merely less convenient; it is an active subtraction of capability from the data.
The platform and the assurer do not want your paragraph. They want your record. Lineage that is not in a field is lineage that does not exist.
What a Clean, Tagged Record Contains
A record the reporting system accepts and the assurer can read has a predictable set of fields, each doing a distinct job. You do not need every field on every datapoint, but the core set is stable across activity data, emission factors, and emissions results.
Value. The number itself, and nothing but the number, with no unit or commentary fused into it. "48210" belongs in the value field, not "48,210 kWh from the bill."
Unit. The unit as its own field, exactly and explicitly: kWh, litres, tonne-km, tCO2e, kgCO2e per kWh. A value without an explicit unit field is ambiguous, and ambiguity is where unit-mix errors hide.
Source. The provenance, as structured as you can make it: the file identifier, the page, the line or field reference, and for a factor the database, table, row, version, and year. This is the field that lets anyone reopen the origin and confirm the number.
Primary or secondary. A tag declaring whether the figure is measured primary data or estimated secondary data. This is the single most important non-numeric field for the assurer, because it tells them which figures rest on measurement and which on estimation, and it must never be left blank or inferred.
Period. The reporting period the figure covers, with explicit start and end where it matters. This is what would have caught the fourteen-month-invoice error from the prior lesson: a period field that says 1 September to 31 October of the following year screams its own anomaly.
Supporting fields as needed. Method (for an estimate), uncertainty, the responsible facility or entity, the scope and category, and a status or sign-off marker. The principle is that anything an assurer might ask about becomes a field they can read rather than a question they have to ask.
Notice how this set maps directly onto the disciplines of the earlier lessons, which is not a coincidence: the fields are simply where each earlier discipline comes to rest. The source field is where traced extraction lands. The primary-or-secondary tag is where the measured-versus-estimated distinction lands. The period field is where the scope-of-time discipline lands, the very thing that would have caught the fourteen-month invoice. The unit field is where the preserve-the-original-unit rule lands. The structured record is not a new requirement layered on top of the chapter; it is the chapter's requirements made permanent. Each thing you were careful about upstream needs a field to live in downstream, or that care does not survive the journey. Designing the schema is therefore mostly an act of asking, for each discipline you practised, "which field carries this forward?", and ensuring none of them has nowhere to go.
JSON and CSV, Not Paragraphs
In practice the two formats you will ask the model to emit are JSON and CSV. CSV is a table: one row per datapoint, one column per field, ideal for tabular data that flows into a spreadsheet or a platform import. JSON is a nested structure of labelled fields, better when a record has sub-records, such as an emissions result that bundles its activity value, its factor, and the factor's own provenance. You do not need to write either by hand; you instruct the model to produce them, which it does well precisely because they are rigid and unambiguous. What matters is the discipline of asking for the structure with named fields, not a description of the data in sentences.
You do not need to become a programmer to work this way, and that is worth saying plainly because the formats can look intimidating to a disclosure professional who has never written a line of code. You will never have to author the JSON or the CSV yourself. Your job is narrower and entirely within reach: decide what the fields should be, tell the model to produce records with exactly those fields, and then read the result to confirm the fields are populated correctly. Reading a CSV is reading a table, which every reporting professional already does all day. Reading a JSON record is reading a labelled list of "field: value" pairs, which is no harder than reading a form. The skill being asked of you is not engineering, it is insisting on a structure and checking that you got it, which is a disclosure skill, not a technical one. The model handles the mechanics of the format; you handle the judgment about what belongs in it and whether it is right.
How to Ask the Model for It
Getting clean structured output is mostly about telling the model the exact schema and refusing to accept anything else. A few disciplines make it reliable.
Specify the fields explicitly and name them. Do not ask for "the data in a structured way." Give the model the field list, value, unit, source, primary_or_secondary, period_start, period_end, and tell it to populate each. A named schema is what makes the output map cleanly into the platform's import.
Forbid prose in the data fields. The value field holds a number, the unit field holds a unit, and explanation goes in its own note field if it goes anywhere. A model left to its own devices will often helpfully merge "48,210 kWh (estimated)" into one field, which silently destroys both the unit separation and the primary/secondary tag.
Make missing fields explicit, never invented. If the source is unknown, the source field reads "unknown" or "missing" and the record is flagged, exactly as in extraction. A model that fills a blank source field with a plausible-sounding citation has reintroduced the fabrication problem at the formatting stage.
Keep one datapoint per record. Resist letting the model collapse a month of readings into a single summarised record. Each measured value is its own row with its own source, because the assurer samples individual datapoints, not summaries.
Validate the output against the schema before you trust it. Structured does not mean correct. A record can be perfectly formatted and carry a fabricated source or a mislabelled estimate. The format makes verification easier, it does not replace it; you still run the checks from the previous lessons, now against tidy fields instead of buried prose.
This last point is worth dwelling on, because the tidiness of a well-formed record is seductive in a dangerous way. A clean CSV with neat columns and consistent types looks authoritative. It looks finished. It looks like data that has earned trust simply by being orderly. But order is not truth, and a model is perfectly capable of producing a beautifully structured record whose source field names a database row that does not exist, whose primary-or-secondary tag is wrong, or whose value is a misread. The structure has made the record easy to read and easy to import; it has done nothing whatsoever to make it correct. The right mental posture is that structured output is a precondition for verification, not a substitute for it. You verify the fields precisely because they are now legible enough to verify: you can run quote-the-source on the source field, recompute against the value and unit fields, range-check the value, and reconcile to last year, all far more easily than you could against a paragraph. The format is what makes the checking fast. The checking is still what makes the data true.
A Worked Example: From Paragraph to Record
Watch one datapoint move from unusable prose to an assurable record.
Before, the paragraph. The model returns: "The Lyon facility consumed 48,210 kWh of electricity in September 2026 according to the metered utility bill, which we paired with the 2026 grid emission factor to estimate around 3.1 tonnes of CO2e." Read by a human it sounds complete. As data it is a disaster. The number is fused to its unit and its commentary. There is no field a platform can map. The word "estimate" is dangerously ambiguous, the electricity reading is measured primary data and only the emissions result is calculated, but the sentence blurs them. The source is named but not structured, so no system can reopen it. And the factor's own provenance, the database and year, has vanished entirely.
After, the record. The same information, asked for as structured output, comes back as two linked records. The activity record: value 48210, unit kWh, source "utility_bill_Lyon_2026-09.pdf, page 1, line 'Consommation'", primary_or_secondary "primary", period_start 2026-09-01, period_end 2026-09-30, facility "Lyon", scope "2". The factor record it references: value 0.0533, unit "kgCO2e per kWh", source "national grid factors 2026, electricity table, row FR", primary_or_secondary "secondary", year 2026. The emissions result is then its own record that points to both, with its value, unit tCO2e, and a method field, so the calculation reconstructs from the result back through the factor and the activity value to the line on the bill. Now the platform imports it without a human retyping anything. The assurer queries "show me every primary activity value in Scope 2" and this row appears. The "estimate" ambiguity is gone, because primary and secondary are explicit tags, not a word in a sentence. The factor's vintage is a field, not a memory.
The information content barely changed between the two versions. What changed is that the second one is readable by the systems that have to read it, and every thread of lineage the analyst built upstream is now preserved in a field rather than dissolved in prose.
Why Structured Output Is the Finish Line of Extraction
This lesson sits at the end of the extraction chapter for a reason. Everything earlier in the chapter, traced extraction, factor verification, prior-period reconciliation, produces rigour that lives in your working files. Structured output is the step that gets that rigour out of your files and into the systems where disclosure actually happens, with the lineage intact. It is the bridge between the analyst's careful work and the platform that files the report and the assurer who tests it.
It also sets up everything that follows. The structured record is what a reporting platform ingests, what an assurance file is built from, and what later workflows tag against ESRS and ISSB datapoints. A datapoint that arrives as a clean, tagged record with value, unit, source, primary-or-secondary, and period is a datapoint that can flow through the rest of the reporting cycle without anyone re-keying it or losing its provenance. A datapoint that arrives as a paragraph has to be rebuilt by hand at every stage, and rebuilt provenance is weaker provenance.
So the rule that closes the chapter is the one that connects it to the whole program: the report is not a document the AI writes, it is a structure the AI populates and the human verifies. Make the model emit records, not prose, and you keep the chain of custody whole from the line on a scanned bill all the way to the assured, published figure. Let it write paragraphs, and you quietly hand back all the traceability you worked so hard to build.
Key Takeaways
- The platform and the assurer want a record, not a paragraph. A sentence can hold all the right information and still be useless, because to a system prose is an opaque blob it cannot map, query, or filter.
- Lineage that is not in a field does not exist. Structured output is the carrier of the provenance, the units, and the primary-or-secondary distinction that the earlier lessons produced; lose the container and you lose the lineage.
- A clean record has stable fields. Value, unit, source, primary-or-secondary, and period are the core, with method, uncertainty, scope, and sign-off as needed, each doing one distinct job.
- Value holds a number, unit holds a unit, and prose stays out of the data fields. Merging "48,210 kWh (estimated)" into one field silently destroys both the unit separation and the data-type tag.
- Primary-or-secondary is the most important non-numeric field. It tells the assurer which figures rest on measurement and which on estimation, and it must never be left blank or inferred.
- Ask for the exact schema and forbid invention. Name every field, require missing values to read "missing" rather than be filled, keep one datapoint per record, and refuse anything that is not the structure you specified.
- Structured does not mean correct. A tidy record can still carry a fabricated source or a mislabelled estimate, so you still run the verification checks, now against clean fields instead of buried prose.
- This is the finish line of extraction. The structured record is what gets your upstream rigour into the platform and the assurance file with lineage intact, so the chain of custody survives from the scanned bill to the published figure.
Skill.re