Demo only. An independent civic-tech concept — not operated by, endorsed by, or affiliated with the City & County of San Francisco or MOHCD.
Demo Prototype · Not Affiliated With the CitySF Income Verification — Concept
A civic-tech demo · privacy-first

Prove your income once. Apply to any SF affordable home.

Today, applicants upload pay stubs, W-2s and bank statements to every property manager. This demo shows a different path — one Plaid check, one reference number, zero raw documents shared. Landlords only ever see your income bracket.

This is a demo. Not affiliated with MOHCD or the City of San Francisco. Listings shown are mirrored from the public DAHLIA portal. No real verification has been performed.

Why this exists

Built around three constraints

Each one rules out an entire class of risk that today's pay-stub-upload flow creates.

Hashed-only storage

Your email is stored as a one-way SHA-256 with a server pepper. Your exact income is computed in memory, used to pick a bracket, then discarded. We can't leak what we never wrote.

One Plaid check, no SSN

Income is read once from your payroll provider via Plaid's audited OAuth flow. No bank credentials touch this server. No SSN. No document scans. No pay stubs.

Brackets, not figures

Property managers see only a bracket like $50,000 – $79,999. They never see the exact figure, your employer, or your pay frequency.

The city case

Affordable housing is expensive to administer. This is one lever.

Every affordable unit in San Francisco is fought for. Once an applicant is in front of a lottery, the bottleneck stops being supply and becomes paperwork — pay stubs, W-2s, bank statements, asked for again at every property manager. That paperwork has a price. Below is what a centralized layer would change, framed as back-of-envelope ranges, not city accounting.

Order of magnitude
~$10–35M / yr

Estimated annual administrative cost of decentralized income verification across SF's affordable-housing portfolio.

Back-of-envelope: tens of thousands of DAHLIA applications per year × several hundred dollars in staff time per applicant for document collection, review, and re-verification across multiple property managers. Range reflects published HUD compliance time studies and SF MOHCD annual reporting.

Applicant friction
3–6weeks

Typical lag between submitting an application and verification clearing under the status quo.

Applicants upload the same documents to each property manager separately; PMs serially review. Based on applicant testimony in public SF Board of Supervisors housing committee hearings and reporting from the SF Standard and Mission Local.

Marginal cost
~$3–5each

Per-verification cost using a centralized Plaid Income check at scale.

Order-of-magnitude estimate from Plaid's published Income Verification pricing for large-volume contracts. The whole portfolio at this unit cost is small relative to current administrative spend.

Today's hidden cost

What the status quo silently spends

  • Duplicate review. Every property manager re-collects and re-verifies the same pay stubs. The same applicant's documents are reviewed five, ten, fifteen times across a single lottery cycle.
  • Applicant abandonment. Applicants give up after the third or fourth property manager asks for the same W-2. The unit goes to whoever has the patience for paperwork, not whoever fits the AMI.
  • Document liability. Pay stubs, SSN-bearing W-2s, and bank statements sit on hundreds of property-manager laptops and shared drives — a sprawling audit and breach surface.
  • Equity gap. Applicants without a scanner, a printer, or steady access to digital tools disproportionately drop off. Document burden is a regressive cost.
What a centralized layer unlocks

What changes when verification is shared

  • One check, many landlords. A single Plaid payroll read becomes a reusable reference. PMs query an endpoint; applicants stop re-uploading the same documents.
  • Aggregate telemetry, zero PII. MOHCD (or any oversight body) can see real-time bracket distributions across listings — useful policy signal without storing personal information.
  • Liability collapses. Pay-stub documents stop accumulating on PM laptops. The verification record at rest is a hashed bracket, not a document trove.
  • Faster lease-up. Verification clears in minutes, not weeks. Units stop sitting vacant while paperwork circulates — every vacant week of an affordable unit is an opportunity cost in rent and welfare.
The flow

Four steps, about three minutes

No accounts, no passwords. You receive a reference number; that's the whole credential.

  1. Pick a listing

    Pulled hourly from SF.gov DAHLIA. Every listing has a "Verify income" link that opens this demo.

  2. Consent

    One screen explains what's shared and what isn't, with a version-stamped checkbox. Stored as audit evidence.

  3. Plaid payroll check

    Plaid's income_verification product reads your last 90 days of payroll without a bank login. In mock mode, you can pick any bracket.

  4. Get your reference

    A 12-character ID like K4P9XQ2YR7M3. Valid 90 days. Hand it to as many landlords as you like — they query a read-only eligibility endpoint.

Mirrored from DAHLIA

Available affordable listings

Pulled from SF.gov's public housing feed every six hours. Click any listing to start a demo verification.

See all listings →

The Rise Hayes Valley Units 308 508 210 315 408

1699 Market St, San Francisco, CA

Units available
5
Application due
Apr 30, 2024
Type
Re-rental
Listing #a0W4U000Verify income →

Isle House

39 Bruton St, San Francisco, CA

Units available
24
Application due
Jul 17, 2024
Type
New rental
Listing #a0W4U000Verify income →

EVE Community Village

1108 Connecticut St, San Francisco, CA

Units available
38
Application due
Feb 1, 2025
Type
New rental
Listing #a0W7y000Verify income →

363 6th Street Unit 401 602 312

363 6th St, San Francisco, CA

Units available
3
Application due
Feb 1, 2025
Type
Re-rental
Listing #a0W7y000Verify income →

Quincy

555 Bryant St, San Francisco, CA

Units available
71
Application due
Feb 22, 2025
Type
New rental
Listing #a0W4U000Verify income →

4742 Mission

4742 Mission St, San Francisco, CA

Units available
12
Application due
Mar 22, 2025
Type
New rental
Listing #a0W4U000Verify income →
Questions

What people ask about this demo

The short version: no, your bank password isn't going anywhere; and yes, the bracket really is the only thing landlords see.

Does my actual income ever leave Plaid?

Plaid returns it once, encrypted, to this Worker. The Worker rounds it to a bracket (e.g. $50,000 – $79,999), stores the bracket, and discards the raw number. The exact figure is never written to disk and never logged.

Why do you store my email at all?

Only as a one-way SHA-256 hash with a server pepper. We need the hash so that — if you later use "Get my data" or "Delete my data" — we can prove the records belong to you. We cannot reverse the hash to recover your address.

What does the property manager see?

A reference like K4P9XQ2YR7M3, the income bracket, the verification date, and the expiration date. They do not see your name, email, exact income, employer, or pay frequency.

How long is data kept?

90 days from verification, then a nightly cron permanently deletes it. You can also request immediate deletion via Delete my data.

Is this an official City of SF service?

No. This is an independent civic-tech prototype. It is not affiliated with, endorsed by, or operated by MOHCD or the City & County of San Francisco. The listings shown are pulled from the public DAHLIA API as a demonstration.

Can my landlord see my real bank balance?

No. Plaid Income reads payroll provider records (Workday, ADP, Gusto, etc.). It does not read your bank balance or transactions. We never get bank account or routing numbers.

For property managers

Query eligibility, never PII

In a real deployment, registered property managers receive a JWT and call one endpoint. The response is a single line: eligible / not eligible, and a bracket.

POST /api/pm/login
GET  /api/pm/eligibility/:applicantRefId?listingId=...

{
  "eligible": true,
  "bracket":  "50_80K",
  "verified_at": "2026-04-12T14:32:11Z",
  "expires_at":  "2026-07-11T14:32:11Z"
}

Every PM query is rate-limited and audit-logged. The applicant's reference is the only identifier; no email or document ever transits this endpoint.