Extracting Milestones and Budget From a Grant Award Letter
10 min read · updated August 11, 2026
The question somebody actually has about an award notice is when the next report is due. That date is almost never printed on it. It is computable from three fields that are, and the arithmetic is published.
The identifiers, and which one is the key
A federal award notice carries a set of identifiers that the uniform administrative requirements at 2 CFR part 200 require the awarding agency to state, and they are easy to conflate because several are numbers of similar shape on the same page.
- The federal award identification number — the FAIN. This is the identifier for the award itself and is the join key for everything downstream, including reporting and audit.
- The assistance listing number, the programme identifier formerly known as the CFDA number, printed as two digits, a dot and three more. It identifies the programme, not this award, so many awards share one.
- The recipient’s unique entity identifier, which identifies the organisation.
- An agency-internal document or notice number, often the most prominent number on the page and the least useful, since it identifies this piece of paper rather than the award.
Extract all of them with their labels and key on the FAIN. Keying on the notice number is the common error and it breaks at the first amendment, because an amended notice has a new document number and the same FAIN — which is precisely the relationship you need in order to know that the two documents describe one award.
Three date ranges that are not the same range
Award notices carry several date ranges and treating them as interchangeable produces a deadline calendar that is wrong by a year.
The period of performance is the full span over which the work may be carried out and costs incurred — potentially several years. The budget period is the interval within it for which funds are currently approved, commonly twelve months, and a multi-year award has a sequence of them. The award date is when the agency issued the notice, and it is frequently after the period of performance start, which surprises people and is normal.
Reporting attaches to different ones of these. A final report is due after the period of performance ends; annual reports typically track budget periods; interim financial reports track calendar or fiscal quarters. A schema with a single start_date and end_date cannot express this and will quietly attach every deadline to whichever pair it captured.
Deriving the reporting calendar
The uniform guidance sets the default timings. Under 2 CFR 200.328, for financial reporting, quarterly and semiannual reports are due no later than 30 calendar days after the end of the reporting period, annual reports no later than 90 calendar days after the reporting period, and the final report no later than 120 calendar days after the conclusion of the period of performance — with 90 days rather than 120 for a subrecipient reporting to a pass-through entity. Performance reporting under 2 CFR 200.329 follows the same shape, no less frequently than annually and no more frequently than quarterly except under specific conditions.
That is enough to generate the whole calendar from three extracted fields: the period start, the period end, and the stated frequency.
// Synthetic award. Every input below is a field read off the notice.
// period of performance: 2026-09-01 to 2029-08-31
// financial reporting frequency: quarterly
// recipient type: direct recipient (not a subrecipient)
//
// Quarterly periods run from the award start, so they end on
// 2026-11-30, 2027-02-28, 2027-05-31, 2027-08-31, ... each + 30 days:
// 2026-11-30 -> due 2026-12-30
// 2027-02-28 -> due 2027-03-30
// 2027-05-31 -> due 2027-06-30
// 2027-08-31 -> due 2027-09-30
//
// Final report: 120 calendar days after 2029-08-31
// +30 = 2029-09-30, +61 = 2029-10-31, +91 = 2029-11-30, +120 = 2029-12-29
function reportingCalendar(award) {
const out = [];
for (const end of periodEnds(award.pop_start, award.pop_end, award.frequency)) {
const days = award.frequency === "annual" ? 90 : 30;
out.push({ period_end: end, due: addDays(end, days),
basis: "2 CFR 200.328", derived: true });
}
const finalDays = award.recipient_type === "subrecipient" ? 90 : 120;
out.push({ period_end: award.pop_end, due: addDays(award.pop_end, finalDays),
kind: "final", basis: "2 CFR 200.328", derived: true });
return out;
}The derived: true flag is not decoration. A date your system computed and a date the notice printed have different standing: the first is your inference from a default rule, and the second is the agency’s instruction. When they disagree, the printed one wins and somebody needs to see the disagreement. Carrying the basis alongside — which rule or which sentence produced this date — is what lets a person check it in ten seconds instead of rereading the award.
Two arithmetic details. Add calendar days rather than months: 120 days is not four months and the difference moves a deadline by up to two days, which matters when the whole point is a deadline. And decide deliberately what happens when a derived date lands on a weekend or a holiday — the answer is agency-specific and is not something to guess, so the honest default is to report the raw computed date and flag that it falls on a non-business day.
Obligated, approved, and cost-shared
The money fields on an award notice are several numbers that are all correct and all different, and picking the largest is the usual error.
- Amount obligated this action — what this notice adds. On an amendment it can be zero, or negative.
- Total amount obligated to date — the cumulative federal commitment across all actions on this award.
- Total approved budget — federal share plus any required non-federal share, which is a larger number and is the one the budget in the application maps to.
- Cost sharing or matching requirement — the non-federal portion, which is an obligation on the recipient rather than money received, and which generates its own reporting.
Extract them as labelled fields and check the identity that must hold: federal share plus non-federal share equals the total approved budget. The same reasoning that makes a budget form self-checking applies here with far fewer numbers, so there is no reason not to.
The notice may also state an approved indirect cost rate and its base, which is worth capturing because it is the authoritative statement of what the recipient may charge — and because it is the input to the indirect-cost arithmetic on every subsequent financial report.
Terms incorporated by reference
An award notice is short because most of its content is elsewhere. It incorporates general terms and conditions by reference, adds agency-specific terms, and frequently attaches special conditions applying to this recipient — which can include a more frequent reporting requirement than the default, additional prior-approval requirements, or a restriction on drawing funds.
So the extracted record must be able to say “there is a term here I have not read”. Capture every referenced document as a structured pointer with its title, any version or date, and a URL where one is given, and mark the award as having unresolved terms until somebody has looked at them. An extraction that produces a clean reporting calendar from the defaults, on an award whose special conditions require monthly reporting, is confidently wrong in the one field the reader asked for.
Special conditions are usually in a clearly headed block, so detecting their presence is straightforward even when parsing their content is not. Presence detection is enough: a boolean saying special conditions exist, with their page reference, turns a silent error into a visible task. That is the same principle as flagging an unresolved reference on a document with state addenda — the extraction’s job includes reporting the parts of the document it did not resolve, and a field that is silently absent from the output is indistinguishable from a field that was not there.