Help CenterHOA & CDFI Verticals
    Verticals22 min read

    HOA & CDFI Verticals

    One guide, two adjacent verticals: HOA dues reporting for property managers, and the CDFI Nonprofit furnishing tier for 501(c)(3) lenders, CDFIs, and tribal-lending programs. They share infrastructure (industry templates, validation profiles) but serve very different audiences.

    Overview

    Metro2 ships two new verticals on a single platform release: HOA dues reporting for community-association management companies, and the CDFI Nonprofit furnishing tier for community development financial institutions, 501(c)(3) lenders, and tribal-lending programs. We bundled them in one guide because they share the same plumbing under the hood — the industry-templates system, validation profiles, and per-company industry_slug gating — even though the audiences and workflows look nothing alike.

    Read whichever half applies to you:

    • HOA section (below) — property managers, HOA boards, condo associations, CINC / Vantaca / FrontSteps / AppFolio / Buildium operators.
    • CDFI section (further down) — community development financial institutions, nonprofit CDFI loan funds, credit-builder programs, tribal-lending programs, and 501(c)(3) consumer lenders.

    Both verticals are opt-in per company — you select one via the industry_slug field on your company profile (hoa-management or cdfi-nonprofit). If your company has neither slug set, none of the gates or extra workflows described here apply — your existing pipeline is unchanged.

    Three principles run through the implementation:

    • Shared infrastructure, distinct surfaces. The industry-templates engine, validation-profile system, and the generate-file endpoint know about both verticals, but each gets its own marketing page, help guide, training content, and API namespace.
    • Non-breaking by default. All gates are scoped to the relevant industry_slug. A non-HOA company never sees the disclosure-verify gate; a non-CDFI company never sees the nonprofit-verification flow. Existing customers get zero behavior change.
    • Backend-first. Phase A is API + content. The dashboard UI components (AssociationManager, DisclosureVerifyDialog, HomeownerConsentImporter, training viewer) are scaffolded but deferred to Phase C. The APIs are fully usable today via curl or your own UI.

    HOA: dues reporting overview

    The HOA vertical lets a property-management company (or, less commonly, an HOA board running its own books) furnish monthly dues assessments to the credit bureaus as a tradeline. The idea is the same as rent-reporting: paying your HOA dues on time becomes a positive item on the homeowner's credit file, and the association gets a non-confrontational lever for delinquency collection.

    Account Type 89 (default)

    We default HOA dues to Account Type 89 — Rental Agreement. This is the closest CDIA-defined account type to a recurring residential payment obligation that isn't a mortgage. It is a deliberate, documented choice — recorded in DECISIONS.md — pending CDIA confirmation of a dedicated HOA account type.

    Account Type 89 is enforced as a soft warning in the validator, not a hard error. If your bureau contact tells you they're accepting a different code (some accept Type 47 — Credit Line, others have informal guidance to use Type 22 — Secured Loan), you can override per-association without us pushing back at file-generation time. The warning persists in the validation response so the choice stays visible to your QA workflow.

    Portfolio Type O

    HOA tradelines use Portfolio Type O — Open Account. This pairs naturally with Account Type 89: dues are an ongoing, recurring obligation with no fixed payoff date, which is exactly what Portfolio Type O describes. Bureaus that bucket files by portfolio type will route the file to the same lane they use for other open-account furnishers (utilities, telecoms, rent reporters).

    Homeowner opt-in

    HOA reporting is opt-in by homeowner, not opt-out. A homeowner must have a signed consent record in hoa_homeowner_consents before any record for their address is emitted. The disclosure-verify gate (covered below) operates at the association level — it asks "does the association as a whole permit credit reporting" — but the consent table operates at the homeowner level. You need both.

    Positive-only is typical (but not enforced)

    Most HOAs that report start out positive-only — they report the current-status homeowners and quietly omit the delinquent ones. This is by far the gentlest legal posture for the early months of a program, and it's the default we recommend in the marketing page. Metro2 does not enforce positive-only at the file level — if your board has explicitly authorized adverse reporting and you have the per-homeowner consent and the state notice periods to back it up, you can report delinquency status codes the same way any other furnisher would.

    HOA: state-by-state compliance

    Every U.S. state has its own statute (or patchwork of statutes) governing what an HOA or condo association is allowed to do when a homeowner falls behind on dues. We seed 10 high-density HOA states with detailed rules and leave the remaining 41 jurisdictions (40 states + DC) as "contact us" placeholders pending paralegal review.

    The 10 detailed states

    These are the states with the largest HOA footprint by housing-unit count, and where we have direct statutory citations and recent (2024–2026) legislative updates in the seed. The rule summaries live in the hoa_state_compliance database table and are mirrored statically in src/content/metro2/hoa-state-compliance.ts so marketing pages render without a Supabase round-trip.

    StateCC&R disclosure requiredHomeowner consent requiredNotice periodKey statute
    California (CA)YesYes30 daysCivil Code 4950–5450 (Davis-Stirling), SB 410 / SB 625
    New York (NY)YesYes30 daysReal Property Law §339, NPCL §602
    Florida (FL)YesNo45 daysChapter 720 (HOA Act), Chapter 718 (Condo Act)
    Texas (TX)YesNo30 daysProperty Code Chapter 209
    Arizona (AZ)YesNo30 daysA.R.S. §33-1801, §33-1241
    Nevada (NV)YesYes60 daysNRS Chapter 116 (Common-Interest Ownership Act)
    Colorado (CO)YesYes30 daysCCIOA C.R.S. §38-33.3-101, HB 22-1137
    Washington (WA)YesNo30 daysRCW Chapter 64.34 / 64.38
    Georgia (GA)YesNo30 daysO.C.G.A. §44-3-220 (POAA)
    North Carolina (NC)YesYes30 daysNCGS Chapter 47F, 47C

    The full rule summary, citation URL, and last-reviewed date for each state is available on metro2.switchlabs.dev/metro2/hoa-compliance. That page is the authoritative customer-facing surface — keep it bookmarked.

    The other 41 jurisdictions

    Every other state and DC has a placeholder row in the compliance table. The placeholder defaults to the most conservative posture (CC&R disclosure required + homeowner consent required) so customers in those states don't accidentally under-comply while we're still finalizing the legal review. The rule_summary field on those rows says "Contact us — state-specific HOA disclosure requirements have not been verified in this jurisdiction yet. Email support@metro2.switchlabs.dev for a compliance check."

    We're working through these in order of customer demand. If you're in a placeholder state and ready to launch, email support and we'll prioritize the paralegal review for that jurisdiction.

    What the is_seeded flag means

    Each row carries an is_seeded boolean:

    • is_seeded: true — the rule summary is paralegal-reviewed and current. Safe to surface in customer-facing UI as the authoritative answer.
    • is_seeded: false — this is a placeholder row. The defaults (consent required, disclosure required) are conservative but unverified. UI should render this as a "contact us" card rather than a definitive ruling.

    HOA: the CC&R disclosure-verify gate

    Before any HOA file gets emitted, every association referenced by records in the file must have an explicit boolean attestation on record that the association's CC&Rs (Covenants, Conditions & Restrictions) permit credit reporting. We call this the disclosure-verify gate, and it's wired directly into the generate-file endpoint.

    Why this gate exists

    The legal foundation for HOA credit reporting is the association's governing documents. If the CC&Rs were recorded in 1997 and don't mention credit reporting, the board can't just start reporting tradelines next month — they need a CC&R amendment (or, in some states, a board resolution backed by recorded notice). Forgetting this step is the #1 way an HOA program gets a homeowner-side FCRA complaint, and the #1 way a property-management company ends up named in that complaint.

    The gate makes the attestation explicit and impossible to forget: you can't generate a file without it, and once you check the box, we record when it was checked, who checked it, and a free-text note (typically a reference to the recorded amendment or board resolution).

    Exact gate semantics

    From src/lib/hoa-gate/index.ts, the rules are:

    1. Scoped to HOA companies only. The gate only fires when the calling company's industry_slug = 'hoa-management'. For every other vertical it's a no-op — non-HOA companies see zero behavioral change.
    2. Per-association check. If the company is hoa-management and has any associations registered, every association referenced by records in the current file generation must have a non-null ccr_disclosure_verified_at timestamp AND must have reporting_paused = false.
    3. Soft-pass for HOA companies with no associations yet. If the company has the HOA slug but hasn't populated hoa_associations at all, generation is allowed with a warning. This is the "you're evaluating the vertical" mode.
    4. Unassigned records are a warning, not an error. Records with no metadata.hoa_association_id are emitted unchanged in v1 with a count surfaced in the warnings array. This lets customers adopt the workflow incrementally.

    What you see when the gate fails

    If any association referenced by the file is missing disclosure verification, the /api/metro2-records/generate-file endpoint returns HTTP 422 Unprocessable Entity with a JSON body like:

    {
      "ok": false,
      "error": "HOA disclosure gate failed: one or more associations are not cleared for reporting. Association \"Maplewood Heights HOA\" (a1b2c3d4-...) is missing CC&R disclosure verification. POST /api/hoa/associations/a1b2c3d4-.../disclosure-verify with a board-resolution note to clear.",
      "blocked_association_ids": ["a1b2c3d4-...", "e5f6g7h8-..."],
      "warnings": []
    }

    The blocked_association_ids array makes it cheap to build a UI that shows the customer exactly which associations are blocking the file and offers a one-click clear-the-gate flow.

    Clearing the gate

    To clear the gate for an association, POST to /api/hoa/associations/{id}/disclosure-verify with a board-resolution note in the body:

    curl -X POST https://metro2.switchlabs.dev/api/hoa/associations/a1b2c3d4-.../disclosure-verify \
      -H "Authorization: Bearer <API_KEY>" \
      -H "Content-Type: application/json" \
      -d '{
        "note": "CC&R Amendment #2025-04 recorded with Maricopa County on 2025-08-12, Doc #20250812-0094127. Board resolution attached.",
        "verified_by_user_id": "..."
      }'

    The endpoint sets ccr_disclosure_verified_at = now() on the row and records the note in ccr_disclosure_verification_note. That's it — the next generate-file call sees the timestamp and lets the file through.

    If reporting needs to be paused later (homeowner complaint, board decision, legal hold), set reporting_paused = true on the association via the regular PATCH endpoint and the gate will block again.

    Failure modes that are not blocked

    The gate is deliberately permissive about a few things so existing customers can adopt the vertical incrementally:

    • Read error on the company row — if Supabase returns an error trying to read companies.industry_slug, the gate returns ok: true with a warning and lets the file through. We do not hard-fail on infrastructure errors.
    • Read error on the associations table — same posture: warn, don't block.
    • HOA company with zero associations — generation is allowed (this is the "previewing the vertical" case).
    • Records with no hoa_association_id — emitted unchanged with a warning count.

    HOA: data model

    hoa_associations

    One row per HOA managed by your company. In v1 this table is metadata-only — there are no sub-tenant users attached to an association. The property-management company has full access to all of its associations through its own admin users; we don't (yet) support sub-accounts for individual board members.

    ColumnTypeNotes
    iduuid (pk)Server-generated.
    company_iduuid (fk → companies)Owning property-management company. Required.
    nametextDisplay name (e.g. "Maplewood Heights HOA").
    state_codetext (2 chars)For routing to the right state-compliance row.
    ccr_disclosure_verified_attimestamptzNull until the disclosure-verify gate is cleared. Set by POST .../disclosure-verify.
    ccr_disclosure_verification_notetextFree-text note: recorded amendment ID, resolution date, etc.
    reporting_pausedbooleanIf true, gate blocks even with a valid timestamp.
    account_type_overridetext (2 chars)Optional override of the default Account Type 89.
    created_at / updated_attimestamptzAudit fields.

    hoa_homeowner_consents

    One row per homeowner who has consented to having their dues tradeline reported. Supports bulk import — most management companies onboard hundreds or thousands of homeowner consents collected via the annual meeting / portal sign-up flow.

    ColumnTypeNotes
    iduuid (pk)Server-generated.
    company_iduuid (fk)Owning company. Required.
    association_iduuid (fk → hoa_associations)The HOA the homeowner belongs to.
    homeowner_idtextYour internal homeowner ID — primary join key for CSV imports.
    addresstextThe property address (single-line or multi-line, both accepted).
    signed_attimestamptzWhen the homeowner signed the consent form.
    signature_capture_urltextURL to the signed PDF / image (DocuSign envelope, S3 link, etc.).
    revoked_attimestamptzNull = active. Non-null = consent withdrawn; record is excluded from future files.
    revocation_reasontextOptional free-text reason.
    created_at / updated_attimestamptzAudit fields.

    hoa_state_compliance

    Reference table — one row per state + DC. The runtime source of truth for the /api/hoa/state-compliance endpoint and the seed mirror in src/content/metro2/hoa-state-compliance.ts for SEO surfaces. The 10 seeded states have detailed rule_summary + citation_url; the other 41 are placeholders with is_seeded = false.

    The 4 new columns on companies

    Both verticals share four new columns on the existing companies table:

    • industry_slug — controls which vertical's gates and templates apply. Valid values include hoa-management, cdfi-nonprofit, and the existing slugs for other verticals.
    • cdfi_grant_funded — boolean stub for the future grant-funded billing tier. Visible in admin, no billing logic wired up in v1 (see CDFI section below).
    • Two adjacent metadata columns (industry_metadata JSONB and a regulator-tier hint) used by the industry-templates engine to render the right marketing copy and validation profile for each vertical.

    HOA: bulk consent import

    The most common onboarding pattern for an HOA program is to collect homeowner consents at the annual meeting (or via the portal launch email) and then bulk-import them in one shot before flipping on reporting. The /api/hoa/homeowner-consents/bulk-import endpoint accepts a CSV upload and creates one consent row per data line.

    CSV schema

    Four columns, in order:

    • homeowner_id — your internal ID for the homeowner. This is the join key — if the same (association_id, homeowner_id) pair already exists, the import updates the existing row instead of creating a duplicate.
    • address — single-line or multi-line address. We don't parse it; we store it verbatim and use it for UI display.
    • signed_at — ISO 8601 timestamp (2026-04-15T10:30:00-07:00) or date-only (2026-04-15, parsed at noon UTC).
    • signature_capture_url — URL to the signed evidence. Typically a DocuSign envelope ID, an S3 link, or a Drive link. Required even if it's a placeholder — we want every consent row to point to something auditable.

    Example CSV

    homeowner_id,address,signed_at,signature_capture_url
    H-00012,"1234 Maple Way, Phoenix, AZ 85031",2026-03-12,https://docs.example.com/consents/H-00012.pdf
    H-00013,"1236 Maple Way, Phoenix, AZ 85031",2026-03-12,https://docs.example.com/consents/H-00013.pdf
    H-00014,"1238 Maple Way, Phoenix, AZ 85031",2026-03-14,https://docs.example.com/consents/H-00014.pdf
    H-00015,"1240 Maple Way, Phoenix, AZ 85031",2026-03-14,https://docs.example.com/consents/H-00015.pdf
    H-00016,"1242 Maple Way, Phoenix, AZ 85031",2026-03-15,https://docs.example.com/consents/H-00016.pdf

    What the import does

    1. Parses the CSV with PapaParse (auto-detects delimiter — comma, semicolon, tab, pipe).
    2. Validates each row: homeowner_id non-empty, address non-empty, signed_at parseable, signature_capture_url a valid URL.
    3. Upserts on (company_id, association_id, homeowner_id) — re-running the same CSV is idempotent.
    4. Returns a JSON summary: created, updated, skipped, errors[] with row numbers and reasons.

    Example curl

    curl -X POST https://metro2.switchlabs.dev/api/hoa/homeowner-consents/bulk-import \
      -H "Authorization: Bearer <API_KEY>" \
      -F "association_id=a1b2c3d4-..." \
      -F "file=@consents.csv"

    Revoking a consent

    Homeowners can revoke consent at any time. Set revoked_at on the row via PATCH and the homeowner's records will be excluded from future files automatically. Already-emitted tradelines stay on the bureau side until the next monthly file (use the consumer portal's deletion flow if you need to accelerate that).

    HOA: marketing page

    The vertical's sales pitch lives at metro2.switchlabs.dev/metro2/industry/hoa-dues. It covers the value proposition for property managers, screenshots of the workflow, a state-coverage map, and the opt-in homeowner positioning. Send prospects here rather than improvising the pitch in email — it's the authoritative customer-facing surface and we keep it current as the vertical evolves.

    The page also doubles as the SEO landing for "HOA credit reporting" and adjacent queries. If you want to refer a prospect with a UTM-tagged link for attribution, use:

    https://metro2.switchlabs.dev/metro2/industry/hoa-dues?utm_source=referral&utm_medium=email&utm_campaign=<your_name>

    HOA: state compliance reference

    The full state matrix is at metro2.switchlabs.dev/metro2/hoa-compliance. Every row is rendered with:

    • State name and 2-letter code
    • CC&R disclosure required (yes / no)
    • Homeowner consent required (yes / no)
    • Notice period in days (or "not specified")
    • Full statutory citation + URL (where seeded)
    • Rule summary
    • A "contact us" CTA on placeholder rows (the 41 non-seeded jurisdictions)

    The runtime data comes from the hoa_state_compliance table via the /api/hoa/state-compliance endpoint; the static mirror in src/content/metro2/hoa-state-compliance.ts is used for ISR-friendly rendering and preview deploys where the migration may not be applied yet.

    If you spot a stale rule (a new statute was passed, a court ruling shifted the standard) — email support@metro2.switchlabs.dev with the citation and we'll get it routed to the paralegal review queue.

    HOA: PMS integration stubs

    We've scaffolded UI for five property-management-software (PMS) integrations. These are stubs in v1 — the auth fields and connect buttons render, but the actual data sync is gated behind our partner program. Each integration is currently marked "coming soon" with a notify-me CTA.

    PMSAuth fields surfacedStatus
    CINC SystemsAPI key, Tenant IDPartner program — coming soon
    VantacaOAuth client ID + secret, account subdomainPartner program — coming soon
    FrontStepsAPI key, community IDPartner program — coming soon
    AppFolio HOAOAuth, property-manager subdomainPartner program — coming soon
    Buildium HOAAPI key, association IDPartner program — coming soon

    The reason these are partner-gated rather than just "we'll build them" is that each PMS vendor controls who gets API access, and most of them require a formal partnership agreement before they'll issue production credentials. If you're actively using one of these and want us to prioritize the integration, click the notify-me on the integration card or email support — customer demand is what drives the partner-program conversation.

    In the meantime, the bulk-import CSV flow (covered above) works with exports from any of these PMSes — you just have to map their column names to ours during the export step.

    HOA: API reference

    All HOA endpoints are namespaced under /api/hoa/. Auth is the same as the rest of Metro2 — API key in the Authorization header, or session cookie if you're calling from a browser context.

    Associations — /api/hoa/associations

    • GET /api/hoa/associations — list all associations for the calling company.
    • POST /api/hoa/associations — create a new association.
    • GET /api/hoa/associations/{id} — fetch one by ID.
    • PATCH /api/hoa/associations/{id} — update (rename, change state, set reporting_paused, set account_type_override).
    • DELETE /api/hoa/associations/{id} — soft-delete.
    • POST /api/hoa/associations/{id}/disclosure-verify — clear the CC&R disclosure-verify gate. Body: { "note": "...", "verified_by_user_id": "..." }.

    Homeowner consents — /api/hoa/homeowner-consents

    • GET /api/hoa/homeowner-consents?association_id={id} — list consents, filterable by association.
    • POST /api/hoa/homeowner-consents — create a single consent row.
    • GET /api/hoa/homeowner-consents/{id} — fetch one.
    • PATCH /api/hoa/homeowner-consents/{id} — update (revoke by setting revoked_at + a revocation_reason).
    • DELETE /api/hoa/homeowner-consents/{id} — hard-delete (use only for data-entry errors; for genuine revocations, use the PATCH flow so the audit trail is preserved).
    • POST /api/hoa/homeowner-consents/bulk-import — CSV upload (multipart form). See the CSV schema above.

    State compliance — /api/hoa/state-compliance

    • GET /api/hoa/state-compliance — return all rows (51 total). The static mirror in src/content/metro2/hoa-state-compliance.ts is the same data; the API endpoint is for cases where you need the latest seeded review (the static mirror updates with each deploy).
    • GET /api/hoa/state-compliance/{state_code} — fetch one state by 2-letter code (e.g. /api/hoa/state-compliance/CA).

    Example: end-to-end with curl

    # 1. Create the association.
    curl -X POST https://metro2.switchlabs.dev/api/hoa/associations \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Maplewood Heights HOA",
        "state_code": "AZ"
      }'
    
    # Response: { "id": "a1b2c3d4-...", ... }
    
    # 2. Clear the disclosure-verify gate.
    curl -X POST https://metro2.switchlabs.dev/api/hoa/associations/a1b2c3d4-.../disclosure-verify \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "note": "CC&R Amendment #2025-04, Doc #20250812-0094127. Recorded with Maricopa County 2025-08-12."
      }'
    
    # 3. Bulk-import homeowner consents.
    curl -X POST https://metro2.switchlabs.dev/api/hoa/homeowner-consents/bulk-import \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -F "association_id=a1b2c3d4-..." \
      -F "file=@consents.csv"
    
    # 4. Generate the file (this is where the gate fires).
    curl -X POST https://metro2.switchlabs.dev/api/metro2-records/generate-file \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "environment": "live" }'

    HOA: common questions

    Why Account Type 89 (Rental Agreement) by default?

    Per DECISIONS.md, Account Type 89 is the closest CDIA-defined account type to a recurring residential payment obligation that isn't a mortgage. It maps cleanly to the legal posture of HOA dues (recurring, non-secured, ongoing) and aligns with how rent reporters are treated by the bureaus. We treat it as the default pending CDIA confirmation of a dedicated HOA account type; if and when that confirmation lands, we'll migrate existing furnishers via a one-time per-association flip.

    The default is a soft warning, not a hard error — if your bureau contact has approved a different code for your program, you can override per-association via the account_type_override column without us blocking the file.

    What if my state isn't in the 10 detailed?

    Use the state compliance page to find your state — every jurisdiction has at least a placeholder row. The placeholder defaults to the most conservative posture (CC&R disclosure required + homeowner consent required), so following it puts you in a safe legal position while we finish the paralegal review.

    Email support@metro2.switchlabs.dev to bump your jurisdiction up the review queue. Customer demand is how we prioritize.

    Can homeowners opt out?

    Yes — two paths:

    • Via the homeowner consents endpoint. PATCH the consent row with revoked_at + a revocation_reason. The homeowner will be excluded from the next monthly file.
    • Via the consumer portal. Homeowners can revoke consent themselves through the consumer portal — see the consumer portal guide for the homeowner-facing flow. Revocations flow back into hoa_homeowner_consents with the same revoked_at mechanism.

    For already-emitted tradelines, the revocation only affects future files — if you need to actively delete an already-furnished tradeline from the bureau side, use the consumer portal's deletion request flow.

    Do I need to register every association before I can report anything?

    No — the gate is permissive about HOA companies with zero associations registered (it returns a warning, not an error). This is the "evaluating the vertical" mode. But you do need to register an association before you can run a file that references that association via metadata.hoa_association_id — referencing a non-existent association is a hard block.

    Can one company manage multiple HOAs?

    Yes — that's the standard property-management case. Each HOA is a row in hoa_associations, all owned by the same company_id. The disclosure-verify gate fires per association: you can have HOA A cleared and HOA B blocked, and the file generator will only emit records for HOA A's homeowners (records for HOA B's homeowners will be cited in the 422 response).

    CDFI: Nonprofit furnishing tier overview

    The CDFI Nonprofit tier is designed for 501(c)(3) lenders, certified CDFIs, community development credit unions, tribal-lending programs, and nonprofit credit-builder programs. It bundles two verification flows (IRS Tax Exempt Org Search + CDFI Fund certified-list sync), a training-modules library, and a concessionary pricing posture that positions Metro2 as a viable alternative — or, more commonly, a complement — to CBA Reporter.

    Who this is for

    • U.S. Treasury-certified CDFIs — loan funds, credit unions, banks, and venture funds that show up on the CDFI Fund's certified list.
    • 501(c)(3) consumer lenders — nonprofit credit-builder loan funds, community-based microlenders, faith-based loan programs.
    • Tribal-lending programs — both standalone tribal entities and CDFI-certified Native CDFIs.
    • Hybrid programs — university revolving loan funds, hospital community-benefit lending programs, etc.

    Positioning vs CBA Reporter

    The big existing player in this space is the Credit Builders Alliance (CBA) Reporter, which runs roughly $2,895–$3,895 per year depending on your member tier. CBA Reporter is purpose-built for nonprofit lenders, has deep training resources, and is the de facto standard.

    We're positioned as a complement, not a replacement. Per DECISIONS.md, most CDFIs using Metro2 will keep their CBA membership for the training and peer-network access, and use Metro2 for the actual file generation, validation, and bureau submission workflow — where our pipeline is faster, more programmable, and integrates directly with their loan-origination system. Some smaller CDFIs will use Metro2 standalone; that's fine, but it's not our pitch.

    Pricing for the CDFI tier is concessionary — billing is handled via Wix Plans (the same billing system the rest of Metro2 uses) with a dedicated CDFI tier set up at significantly less than our standard SaaS pricing. The exact dollar figure isn't baked into the codebase — it's managed in the Wix plan configuration so finance can adjust it without a deploy.

    CDFI: verifying nonprofit status

    To enroll in the CDFI Nonprofit tier, a company has to prove it's actually a nonprofit lender. Two verification paths are supported, and most customers will use both:

    IRS Tax Exempt Org Search

    The /api/nonprofit-verification route is a proxy in front of the IRS Tax Exempt Organization Search API. You POST an EIN and the route returns the IRS's record: legal name, status, exemption type, NTEE code, and effective date.

    curl -X POST https://metro2.switchlabs.dev/api/nonprofit-verification \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "ein": "13-1234567" }'
    
    # Response (live mode):
    # {
    #   "ok": true,
    #   "ein": "13-1234567",
    #   "legal_name": "Example Community Lending Fund Inc.",
    #   "status": "Active",
    #   "exemption_type": "501(c)(3)",
    #   "ntee_code": "S20",
    #   "effective_date": "1998-04-01",
    #   "source": "irs-tax-exempt-org-search"
    # }

    The route is env-gated: if the IRS API credentials aren't configured for the current environment (the case in preview deploys and CI), the route returns a stub response with a source: "stub" field so your client can detect it. This means your integration code can be written once and work in both live and stub mode without branching.

    The IRS Tax Exempt API can be slow (multi-second tail latencies are common) and has rate limits — don't put it on a hot path. We cache verified results for 90 days; re-verifications within that window return cached data without hitting the IRS.

    CDFI Fund certified-list sync

    The CDFI Fund (within U.S. Treasury) publishes a certified-list of every entity that holds active CDFI certification. We sync this list nightly via the /api/cron/cdfi-sync route, which is wired to a Vercel cron schedule.

    Once synced, the certified list is queryable via /api/certification-lookup:

    curl https://metro2.switchlabs.dev/api/certification-lookup?ein=13-1234567 \
      -H "Authorization: Bearer $METRO2_API_KEY"
    
    # Response:
    # {
    #   "ok": true,
    #   "found": true,
    #   "ein": "13-1234567",
    #   "name": "Example Community Lending Fund Inc.",
    #   "cert_id": "CDFI-12345",
    #   "cert_type": "Loan Fund",
    #   "cert_date": "2014-06-12",
    #   "state": "NY",
    #   "last_synced_at": "2026-05-18T03:00:00Z"
    # }

    Like the IRS endpoint, the CDFI Fund sync is env-gated. If the download URL isn't configured for the current environment, the cron route logs a no-op and the lookup endpoint falls back to a stub response with source: "stub". Production runs the full sync nightly; preview deploys use the stub.

    Why two paths? Some lenders are nonprofit but not CDFI-certified (faith-based microlenders, university revolving loan funds, recently founded 501(c)(3)s that haven't applied for CDFI yet). Some are CDFI-certified but not 501(c)(3) (CDFI-certified for-profit loan funds with a CDFI mission). The two-path verification covers both cases.

    CDFI: grant-funded flag

    The companies.cdfi_grant_funded column is a boolean stub. It exists so we can flag grant-funded CDFIs in the admin UI today and have the data ready when the grant-tier billing logic ships in a future phase, but it currently does not drive any billing or feature gating — it's strictly visible-in-admin metadata.

    What it's for, eventually

    Many CDFIs receive grant funding from the CDFI Fund's financial assistance (FA) or technical assistance (TA) programs, from state-level CDFI programs, or from private grantmakers (Wells Fargo NEXT Awards, JPMorgan PRO Neighborhoods, etc.). Several of these grants specifically subsidize tech-stack spending for the grantee, which means a grant-funded CDFI may qualify for a different pricing tier entirely (often free or heavily discounted, billed back to the granting institution rather than the CDFI).

    When the grant-tier billing ships, we'll add eligibility rules, grant-institution attribution, and the billing-bypass logic. Until then, the flag just makes the population visible to our team so we can have one-off conversations with grant-funded customers about pricing.

    Setting the flag

    Admin-only. Toggle via the company-edit form in the admin panel, or PATCH directly:

    curl -X PATCH https://metro2.switchlabs.dev/api/admin/companies/<id> \
      -H "Authorization: Bearer $METRO2_ADMIN_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "cdfi_grant_funded": true }'

    Self-serve is not exposed — we want a brief conversation with the customer before flipping the flag.

    CDFI: training modules

    The training library at metro2.switchlabs.dev/metro2/cdfi-training is modeled after the CBA Training Institute's curriculum structure. Eight modules cover the full lifecycle of nonprofit credit reporting, from "what is Metro 2" through dispute escalation. The intent is to make CDFI Metro2 self-serviceable for staff who don't come from a banking-tech background.

    #ModuleAudience
    1Metro 2 basics — file format, RDW, base segment, trailersEveryone new to credit reporting
    2Furnisher onboarding — getting an Equifax / Experian / TransUnion / Innovis subscriber codeCompliance lead
    3CDFI-specific account types — installment, secured, credit-builder, microloan handlingLoan ops
    4Status codes & the Payment History ProfileLoan ops
    5Compliance Condition Codes (CCC) — DA, dispute, bankruptcy, deceasedCompliance lead
    6Dispute handling — receiving a CDV/ACDV, the 30-day response clockCompliance lead, customer-facing staff
    7Dispute escalation — when to escalate, working with the bureau's e-OSCAR processCompliance lead
    8Member-protection rules — FCRA accuracy, ECOA, SCRA, the CDFI lender's heightened duty of careCompliance lead, board reporting

    Each module has a written explainer, a short knowledge-check quiz, and a downloadable summary PDF. Completion is tracked per user so program managers can see who's through which module.

    The training-viewer UI shipped in Phase B; new modules are added quarterly. Customers can't author their own modules in v1 — let us know if that's a feature you'd use and we'll wire it in.

    CDFI: marketing page

    The CDFI Nonprofit pitch page is at metro2.switchlabs.dev/metro2/industry/cdfi-nonprofit. Refer prospects here for the value proposition, the CBA comparison framing, the training-library teaser, and the verification-flow walkthrough. The page is the SEO landing for "CDFI Metro 2 reporting" and "nonprofit credit furnisher" queries.

    UTM template for attribution:

    https://metro2.switchlabs.dev/metro2/industry/cdfi-nonprofit?utm_source=referral&utm_medium=email&utm_campaign=<your_name>

    CDFI: API reference

    The CDFI-tier endpoints share auth with the rest of Metro2 — API key in the Authorization header, or session cookie from a browser context.

    Nonprofit verification — /api/nonprofit-verification

    • POST /api/nonprofit-verification — verify an EIN against the IRS Tax Exempt Org Search. Body: { "ein": "13-1234567" }.
    • GET /api/nonprofit-verification?ein=... — fetch a previously cached result without re-querying the IRS (returns the cached row + last-verified timestamp).

    Env-gated. Returns source: "stub" when IRS credentials aren't configured.

    Certification lookup — /api/certification-lookup

    • GET /api/certification-lookup?ein=... — look up an EIN in the cached CDFI Fund certified list.
    • GET /api/certification-lookup?name=... — fuzzy lookup by legal name (returns top 10 matches by edit distance).
    • POST /api/certification-lookup/manual-upload — attach a manually uploaded CDFI certification document (PDF of your CDFI Fund certification letter) for customers whose EIN can't be found in the synced list (recent certifications take a few months to propagate to the published list).

    CDFI Fund sync — /api/cron/cdfi-sync

    • POST /api/cron/cdfi-sync — triggered by Vercel cron nightly. Downloads the latest CDFI Fund certified list, diffs it against the cached version, and updates the lookup table. Returns a summary: { added: N, updated: N, removed: N }.

    Env-gated. Authenticated via the Vercel cron secret; can be called manually with the admin key for testing.

    Example: verifying a new CDFI customer

    # 1. Verify their EIN against IRS.
    curl -X POST https://metro2.switchlabs.dev/api/nonprofit-verification \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "ein": "13-1234567" }'
    
    # 2. Check the CDFI Fund certified list.
    curl "https://metro2.switchlabs.dev/api/certification-lookup?ein=13-1234567" \
      -H "Authorization: Bearer $METRO2_API_KEY"
    
    # 3. If CDFI lookup misses (recent certification), accept manual upload.
    curl -X POST https://metro2.switchlabs.dev/api/certification-lookup/manual-upload \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -F "ein=13-1234567" \
      -F "cert_file=@cdfi-cert-letter.pdf"

    CDFI: common questions

    Do you replace CBA Reporter?

    Per DECISIONS.md, no — Metro2 complements CBA Reporter. Most CDFIs we work with keep their CBA membership for the training, peer network, and the institutional-knowledge access, and use Metro2 for the file generation, validation, dispute workflow, and bureau submission pipeline. The two systems coexist well: CBA Reporter is the brand-of-record in the nonprofit-lending world, Metro2 is the deeper engineering platform.

    That said, smaller CDFIs sometimes use Metro2 standalone — if you don't need the CBA training and you have an internal credit-reporting expert already, Metro2 can be the only system you need. We don't push either configuration; whatever works for your program.

    Is there a grant tier?

    Not yet — the cdfi_grant_funded flag is in place but the grant-billing tier isn't wired up. We track which customers self-identify as grant-funded so we can have a targeted conversation about pricing once the tier ships. If you're grant-funded and ready to talk pricing now, email support@metro2.switchlabs.dev and we'll work out a per-customer arrangement.

    Can I upload my CDFI cert manually?

    Yes — POST to /api/certification-lookup/manual-upload with your CDFI Fund certification letter as a PDF. We'll attach it to your company record and treat it as authoritative until the next nightly sync finds your EIN in the published list (usually a few months after a fresh certification).

    Which account type should I use for credit-builder loans?

    The standard answer is Account Type 04 (Home Improvement / Personal Loan) for unsecured credit-builder loans and Account Type 12 (Education) for education-related loans. Module 3 in the training library ("CDFI-specific account types") walks through the full mapping, including the edge cases for shared secured credit-builder products and revolving microloans.

    Does the IRS verification work for newly registered 501(c)(3)s?

    It works as soon as the IRS posts your determination letter to the Tax Exempt Org Search public dataset, which is typically 2–6 weeks after the determination is issued. If you've been newly approved and the verification is returning "not found," email support with a copy of your determination letter and we'll flip your record to verified manually while the public dataset catches up.

    What if I'm a CDFI and a 501(c)(3)?

    Both verifications run independently. The CDFI Nonprofit tier treats either one as sufficient — you don't need both to enroll. Most certified CDFIs that are loan funds are also 501(c)(3); most CDFI-certified credit unions are not. Either way, one passing verification unlocks the tier.

    What's not yet shipped (Phase C+)

    Phase A is API + content. Three things were intentionally deferred to Phase C and beyond:

    • Dashboard React components. The full UI for association management (AssociationManager), disclosure-verify (DisclosureVerifyDialog), and homeowner-consent import (HomeownerConsentImporter) is scaffolded but not wired into the dashboard yet. The backend APIs (covered above) are fully usable today — you can build your own UI on top, or call them with curl for one-off ops. Phase C ships the polished dashboard surface.
    • Live PMS connectors. All five PMS integration cards (CINC, Vantaca, FrontSteps, AppFolio HOA, Buildium HOA) are stubs gated behind our partner program. Each PMS vendor controls API access on their side, and most require a formal partnership before issuing production credentials. We're actively working the partner-program conversation; in the meantime, CSV bulk-import works with exports from any of them.
    • CAI / OFN distribution. The Community Associations Institute (CAI) for HOA and the Opportunity Finance Network (OFN) for CDFI are the trade associations we're targeting for distribution partnerships. This is non-engineering work (sponsorships, conference presence, co-marketing) and doesn't affect anything in the codebase, but it's on the roadmap for the verticals to graduate from "shipped" to "adopted at scale."

    Related guides

    • Industry templates — the shared infra that drives per-vertical marketing copy, validation profiles, and template defaults. Both HOA and CDFI plug into this system.
    • Multi-portfolio routing — how to split a company's records across multiple portfolios when (e.g.) one HOA management company services both Portfolio Type O (HOA dues) and Portfolio Type I (installment) accounts.
    • Consumer portal — the homeowner-facing / borrower-facing portal where consumers can view their reported tradelines, submit disputes, and revoke consent. Both verticals route consumer-facing flows through the same portal.

    Still stuck? Contact support or browse the Help Center.