Extracting Fields From a Marriage Certificate
8 min read · updated August 11, 2026
Every other document in this cluster fails loudly. A marriage certificate fails quietly: swap the two parties and you get a complete, well-formed, internally consistent record with two people’s details in each other’s fields, and no assertion you can write will notice.
Symmetry is the whole problem
A marriage record holds two parties with the same field set: full name, date of birth or age, place of birth, residence, occupation on older forms, parents’ names, and any previous marital status. The form lays them out either side by side in two columns or stacked in two identical blocks. Nothing about the shape of one block distinguishes it from the other.
Compare that to a birth certificate, where the registrant block and the parent block hold different fields, so a mix-up produces something structurally odd. Here the two blocks are interchangeable by construction — two instances of one entity type on one page, which is the case multi-entity document schema design exists for. Every validation you would normally reach for — required fields present, dates ordered, names well-formed — passes identically on the swapped record. The error is invisible to the data and visible only against the document.
Modern certificates increasingly use neutral labels: Party A and Party B, Spouse 1 and Spouse 2, or Applicant 1 and Applicant 2. Historic and some current forms use Bride and Groom. A schema hard-coded to either pair cannot represent the other, and a schema that maps one onto the other is asserting something about the parties that the document may not say. Store the label as printed alongside a positional index, and let the consumer decide.
How a scan loses its orientation
The swap is not hypothetical; it has specific mechanical causes, and each is common in a bulk digitisation run.
- A 180-degree rotation. A page fed upside down into a sheet feeder, or a photograph taken from the far side of a desk. Modern OCR usually detects and corrects this from text orientation — but a certificate with heavy decorative borders and sparse text gives the detector little to work with, and a certificate that is partly handwritten gives it less.
- A mirrored image. Rarer, but it happens with phone cameras in selfie mode and with some scanner drivers. Text is unreadable so it fails loudly — unless the model helpfully reads it anyway, which current vision models sometimes do.
- Column interleaving in the text layer. No rotation at all: a two-column form whose text runs are emitted in drawing order, so alternate lines come from alternate parties. The result is a single coherent-looking block containing half of each person.
- Two-page certificates. Some jurisdictions record the parties across a spread, and a page ordering error in the scan reverses them wholesale.
The third of those is the most dangerous because it involves no orientation error at all and therefore no orientation fix. It is the same reading-order problem that breaks two-column academic PDFs, and the response is the same: cluster text runs by x-coordinate into columns before reading within a column, rather than trusting the order the extractor emits.
Anchoring on labels instead of position
The robust design assigns a party by the label nearest the value, not by which half of the page it fell in. Concretely:
- Detect orientation before extraction, and correct it. Use the printed body text rather than any single field, and if orientation is uncertain, extract at both candidate rotations and compare.
- Locate the party labels as anchors — the literal strings the form prints, whatever they are. Record them verbatim.
- Assign each extracted value to the party whose label block contains it geometrically, rather than to a left or right position.
- Require the model to return, for each party, the label text it used. A party whose label is empty or invented is the signal that the anchoring failed.
- Run the one cross-check available: the officiant, the witnesses, the place and the date of the marriage are shared by both parties and appear once. If those come back duplicated per party, the blocks were mis-segmented.
Requiring the label text back is the single most useful of these. It converts an invisible failure into a visible one: a record whose party labels are Party A and Party A, or whose label text does not appear anywhere in the source, is detectably wrong without a human reading the certificate.
Licence, ceremony, registration
Three dates again, and again they get collapsed into one field. A marriage licence is issued before the marriage; the ceremony takes place on a date; the marriage is registered or recorded on a later date, and the certificate itself is issued on a fourth. Many jurisdictions print at least three of these on one document.
The date almost every downstream process actually wants is the date of the marriage, and it is not usually the most prominent date on the page. The registration date frequently is, because it sits with the registrar’s block at the foot of the certificate alongside the signature and seal. Name the fields explicitly in the schema — date_of_marriage, date_licence_issued, date_registered, date_certificate_issued — and allow each to be null, so that a document carrying only one of them produces a record that says which one it was.
The ordering assertion is the same shape as elsewhere: licence on or before marriage, marriage on or before registration, registration on or before certificate issue. A violation is a mis-assignment, and because it costs three comparisons it should be running on every record.
Names change, which is the point
A marriage certificate is frequently presented precisely as evidence of a name change, which means the document carries names in more than one state and a schema with one name field per party is losing the fact the document exists to prove.
Depending on the jurisdiction and the form, a party block may hold the name before the marriage, a name at birth distinct from the current name if a previous marriage intervened, and in some places a stated name to be used after the marriage. Forms label these variously and inconsistently. Model them as a list of name records with a context enum — at_birth, before_marriage, after_marriage, as_printed — rather than as fields called maiden name and married name, which do not map onto every form and do not map onto every party.
{
"parties": [
{ "index": 0, "label_as_printed": "Party A",
"names": [
{ "context": "before_marriage", "value": "Sample Alpha Person" },
{ "context": "after_marriage", "value": "Sample Alpha Example" }
],
"date_of_birth": "1990-06-02" },
{ "index": 1, "label_as_printed": "Party B",
"names": [ { "context": "before_marriage", "value": "Synthetic Beta Example" } ],
"date_of_birth": "1989-11-30" }
],
"marriage": {
"date_of_marriage": "2021-05-08",
"place": "Synthetic County, XX",
"officiant": "Recorded on document",
"witnesses": 2
},
"date_registered": "2021-05-14",
"date_certificate_issued": "2026-01-19",
"labels_distinct": true
}labels_distinct is the guard against the failure this whole page is about: it is false when the two party labels came back identical or empty, and it should route the document to review regardless of how complete the rest of the record looks. It is one boolean and it catches the error class that no other validation can see.