Extracting Terms From a Franchise Disclosure Document
10 min read · updated August 11, 2026
A franchise disclosure document is two or three hundred pages with a twenty-three-item skeleton, a stack of exhibits, and a set of state addenda at the back that change what the body said. Extract the body alone and you have produced a confident answer to the wrong question for every state that filed an amendment.
Twenty-three items, in a fixed order
The US Federal Trade Commission’s franchise rule, at 16 CFR 436.5, specifies the disclosures a franchisor must make and the order they appear in. Every FDD has the same twenty-three items:
1 The Franchisor, and any Parents, 12 Territory
Predecessors, and Affiliates 13 Trademarks
2 Business Experience 14 Patents, Copyrights, and
3 Litigation Proprietary Information
4 Bankruptcy 15 Obligation to Participate in the
5 Initial Fees Actual Operation of the Franchise
6 Other Fees Business
7 Estimated Initial Investment 16 Restrictions on What the Franchisee
8 Restrictions on Sources of May Sell
Products and Services 17 Renewal, Termination, Transfer,
9 Franchisee's Obligations and Dispute Resolution
10 Financing 18 Public Figures
11 Franchisor's Assistance, 19 Financial Performance Representations
Advertising, Computer Systems, 20 Outlets and Franchisee Information
and Training 21 Financial Statements
22 Contracts
23 ReceiptsBehind the items sit the exhibits: the franchise agreement itself, area development agreements, the operations manual table of contents, financial statements, state addenda, and the two receipt pages. The exhibits are usually longer than the items and are where the actual contractual language lives — Item 17 is a summary table pointing at clauses in the agreement, not the clauses themselves.
Anchor on numbers, because titles get reworded
The rule specifies the item headings, and in practice documents deviate: a franchisor writes “ITEM 6: OTHER FEES AND COSTS”, or sets the heading in a style that makes it hard to distinguish from a sub-heading, or splits an item across a section break. Meanwhile the item numbers are stable and are printed at the front of every heading, and the same numbers appear in the table of contents and in cross references throughout the body.
The segmentation therefore looks much like the one for a package insert: find candidate headings by the pattern “ITEM” followed by an integer from 1 to 23, reject candidates that break monotonic order — which removes cross references in body text — and take each item’s content as everything up to the next accepted heading. Then discard the table of contents block, identifiable because its entries carry page numbers and no body text.
One deviation to plan for is the item that appears twice: once in the body and once, abbreviated or amended, in a state addendum. That is not a segmentation error and the monotonic-order rule will reject the second occurrence, which is exactly the wrong outcome. Segment the body and the addenda as separate documents and reconcile them afterwards, which is the subject of two sections down.
The Item 20 tables, which have to foot
Item 20 is the item with real numbers in it, and the rule prescribes a set of tables covering the system’s outlets over the last three fiscal years: a systemwide summary, transfers to new owners, the status of franchised outlets, the status of company-owned outlets, and projected openings.
The status tables are the ones worth building a check on, because they are an accounting identity rather than a list. For each state and year, the outlets at the start of the year plus those opened, minus terminations, non-renewals, outlets reacquired by the franchisor and outlets that ceased operations for other reasons, must equal the outlets at the end of the year. And the end-of-year figure for one year must equal the start-of-year figure for the next.
function footItem20(rows) {
const issues = [];
for (const r of rows) {
const computed =
r.start + r.opened - r.terminated - r.non_renewed
- r.reacquired - r.ceased_other;
if (computed !== r.end) {
issues.push({ state: r.state, year: r.year, computed, printed: r.end,
kind: "row_does_not_foot" });
}
}
// Year-over-year continuity, per state.
const byState = groupBy(rows, (r) => r.state);
for (const [state, years] of byState) {
years.sort((a, b) => a.year - b.year);
for (let i = 0; i < years.length - 1; i++) {
if (years[i].end !== years[i + 1].start) {
issues.push({ state, kind: "year_discontinuity",
from: years[i].year, to: years[i + 1].year });
}
}
}
return issues;
}A row that does not foot is almost always an extraction fault rather than a franchisor’s arithmetic error — a column shifted on a page break, a state row merged with the one above, a totals row read as a data row. That is what makes the check valuable: it detects the table-alignment failures that no confidence score reports, on a document long enough that nobody will read every table.
Two extraction details make the check possible. Totals rows must be identified and excluded from the per-row loop, or every table reports a spurious failure. And the outlet tables are wide, so they break across pages with the header repeated or, worse, not repeated — the same continuation problem the certificate of analysis tables have, with the same requirement to align columns by position when the header is absent.
The state addenda amend the body
Several states regulate franchise sales and require their own disclosures, so an FDD circulated in those states carries addenda that modify specific items — most often Item 17, where state law limits what a franchise agreement may say about termination, non-renewal, transfer and dispute resolution, and Item 5 or Item 21 where a state has imposed a financial assurance condition.
For extraction this means an item has a body value and possibly a set of per-state overrides, and the correct answer to “what does Item 17 say” is “for which state?”. A pipeline that produces one Item 17 per document produces a value that is wrong wherever an addendum applies, and does so silently, because the body text reads perfectly.
Model it explicitly: each item carries body_text plus an amendments array of { jurisdiction, item_number, text, source_page }, populated from the addenda exhibit. Then a query for an item in a jurisdiction resolves body-then-override, and a query with no jurisdiction returns the body with a flag saying overrides exist. The addenda are usually clearly labelled with the state name and organised per state, so locating them is a matter of finding the exhibit rather than a difficult inference — but they sit two hundred pages from the item they change, which is why a body-only extraction never notices.
An absent disclosure is an answer
Item 19, financial performance representations, is optional. A franchisor may make one, and many do not — and where none is made, the item still appears, containing the prescribed statement that the franchisor does not make representations about financial performance.
The extraction consequence is that null is the wrong output. An Item 19 containing the negative statement is a positive finding: this franchisor makes no earnings claim. An Item 19 that is genuinely missing from the document is a defect in the document. And an Item 19 containing data with a defined subset of outlets, an accompanying explanatory note and a statement about substantiation is a third thing again. Model the item as a small enum — no_representation, representation_present, item_absent — before you attempt to pull any figures out of it.
The same reasoning applies to Item 18, which is present and negative for most franchisors, and to Item 10 where no financing is offered. On a document whose entire purpose is prescribed disclosure, the difference between “disclosed as none” and “not disclosed” is the difference between compliance and a violation, and an extraction that returns null for both cannot express it.