Skip to main content

What is income verification?

Income verification is the process of confirming a borrower’s income and employment status to assess their ability to repay a mortgage loan. Lenders must verify that borrowers have sufficient, stable income to make monthly mortgage payments. Traditionally, income verification has been a manual, time-consuming process requiring borrowers to:
  • Upload pay stubs
  • Provide employment letters
  • Log into employer HRIS systems
  • Submit tax returns and W-2s
  • Manually enter income information
Pylon automates this entire process using Day 1 Certainty (D1C) and Automated Income Model (AIM) approved services that verify income and employment data automatically, reducing manual documentation and speeding up loan processing.

Why D1C/AIM income verification matters

Day 1 Certainty (D1C) and Automated Income Model (AIM) are programs from Fannie Mae and Freddie Mac that allow lenders to use automated verification sources to reduce conditions and speed up loan processing. When income data comes from D1C/AIM approved sources, it’s considered a “verified” source, meaning no additional documentation is typically required, underwriting conditions are reduced, and loan processing is faster.

Pylon’s income verification integrations

Pylon has integrations with two D1C/AIM approved income verification vendors:
  • Truv - Account connection-based income verification (requires borrower opt-in and your API call)
  • The Work Number (TWN) - SSN-based income verification (fully automatic)

Truv

Truv is an account connection-based income verification service that allows borrowers to connect their bank accounts to automatically verify income and employment data. Pylon is a reseller of Truv and will create an account and API credentials on your behalf.

How Truv works

1

Request access token from Pylon

You request a public/access token from Pylon for the borrower. Pylon provides you with a token to initialize the Truv Bridge component.
2

Initialize Truv Bridge component

Use the token provided by Pylon to initialize the Truv Bridge component for that borrower in your application.
3

Borrower opts in and connects accounts

The borrower opts in and connects their payroll or asset accounts through Truv’s secure connection flow. This allows Truv to access payroll deposit information.
4

Pylon handles data mapping

Pylon automatically handles all the mapping of data entities-including variable income, commission, bonus, salary, and other income types-to the appropriate Pylon fields. No work is required on your end for data mapping.
5

Pylon uses verified data

Verified income and employment data is automatically stored in the loan file. Pylon uses this verified data for underwriting and pricing calculations.
6

No additional documentation needed

Since the data comes from Truv (a D1C and AIM approved source), no additional income documentation is typically required. Pylon’s underwriting system accepts the verified data without additional documentation.

Truv characteristics

  • Client opt-in required: Borrowers must opt in and connect their accounts through Truv’s connection flow
  • You must call it: Request a token from Pylon and initialize the Truv Bridge component-it does not happen automatically
  • >95% coverage: Truv has over 95% coverage of employers and payroll providers
  • Lower production costs: Cheaper production costs compared to The Work Number
  • Pylon handles data mapping: Pylon automatically maps all income types (variable, commission, bonus, salary, etc.) to appropriate Pylon fields-no work required on your end

The Work Number (TWN)

The Work Number (TWN) is a comprehensive employment and income verification service that maintains a database of verified employment and payroll data from thousands of employers and payroll providers across the United States.

How The Work Number works

1

Credit pull collects SSN

When you pull credit for a borrower, you collect their Social Security Number (SSN) as part of the credit pull process. This SSN is the only information needed for income verification through The Work Number.
2

Pylon automatically triggers verification

Pylon automatically triggers income verification through The Work Number in the background. No customer opt-in or flow is required to initialize-it happens automatically once the SSN is available.
3

Pylon queries The Work Number

Pylon uses the borrower’s SSN to query The Work Number’s database for verified income and employment data. This happens automatically-no API calls or requests from you are needed.
4

Pylon uses verified data

If the borrower’s employer is covered by The Work Number, verified income and employment data is automatically retrieved and stored in the loan file. Pylon uses this verified data for underwriting and pricing calculations.
5

No additional documentation needed

Since the data comes from The Work Number (a D1C and AIM approved source), no additional income documentation is typically required. Pylon’s underwriting system accepts the verified data without additional documentation.

The Work Number characteristics

  • SSN-based: Only requires the borrower’s Social Security Number (collected during credit pull)
  • Fully automatic: Pylon automatically triggers verification-no borrower opt-in, no flow to initialize, and no API calls required from you
  • Higher production costs: Much higher production costs compared to Truv (in the range of 5x)
  • Charged on re-pull: Costs are incurred on each verification pull, including re-pulls

Programmatic configuration and cost considerations

Cost impact: The Work Number is approximately 5x more expensive than other verification sources like Truv. Given this cost difference, it’s important to be thoughtful about application-to-funded loan ratios when configuring The Work Number programmatically.
Reach out to your account manager to discuss programmatic configuration of The Work Number. Because The Work Number is charged on each verification pull (including re-pulls), and Pylon covers the cost of unfunded loans in the closed per loan fee, high application-to-funded loan ratios can significantly impact your per-funded-loan underwriting fee. Key considerations:
  • Application-to-funded ratio: If you have a high dropout rate (many applications that don’t result in funded loans), the cost of The Work Number verifications per funded loan increases substantially
  • Per-funded-loan impact: Since Pylon covers unfunded loan costs in the closed per loan fee, using The Work Number when you have a low application-to-funded ratio may require adjusting your per-funded-loan underwriting fee
  • Vendor waterfall strategy: Consider implementing a vendor waterfall that prioritizes lower-cost options (like Truv) before falling back to The Work Number, especially if your application-to-funded ratio is low
Your account manager can help you:
  • Configure when The Work Number should be triggered programmatically
  • Understand the cost implications based on your application-to-funded ratio
  • Implement vendor waterfall strategies to optimize costs
  • Adjust pricing structures if needed

Checking income verification status

After income verification is initiated (automatically for The Work Number, or after borrower opt-in for Truv), check the verifiedAmount field on the Income entity to see if verification was successful. When verifiedAmount has a value, income has been verified through Truv or The Work Number, and Pylon will use this verified data for underwriting without requiring additional documentation. For complete details on querying income data, understanding income states (stated, qualified, verified), and all income fields, see the Incomes entity guide.

GraphQL query example

Poll Pylon’s GraphQL API to check for verified income values. The verifiedAmount field will be populated when income has been successfully verified through Truv or The Work Number:
query GetVerifiedIncomes($loanId: ID!) {
  loan(id: $loanId) {
    borrowers(first: 10) {
      edges {
        node {
          id
          firstName
          lastName
          incomes(first: 100) {
            edges {
              node {
                id
                statedMonthlyAmount
                verifiedAmount
                incomeType
                ... on StandardEmploymentIncome {
                  employment {
                    ... on StandardEmployment {
                      employer {
                        name
                      }
                    }
                  }
                }
              }
            }
            pageInfo {
              hasNextPage
              endCursor
            }
          }
        }
      }
    }
  }
}
Key fields:
  • verifiedAmount: The verified monthly income amount. null if not yet verified, or a number if verified through Truv or The Work Number
  • statedMonthlyAmount: The borrower’s stated monthly income amount
  • incomeType: The type of income (e.g., STANDARD_EMPLOYMENT, SELF_EMPLOYMENT)
Verification status:
  • verifiedAmount === null: Income has not been verified yet, or verification is not available
  • verifiedAmount > 0: Income has been successfully verified through D1C/AIM approved sources

Node.js/TypeScript polling example

Here’s a complete example of polling for verified income values using Node.js and TypeScript:
import { GraphQLClient } from 'graphql-request';

const GET_VERIFIED_INCOMES = `
  query GetVerifiedIncomes($loanId: ID!) {
    loan(id: $loanId) {
      borrowers(first: 10) {
        edges {
          node {
            id
            firstName
            lastName
            incomes(first: 100) {
              edges {
                node {
                  id
                  statedMonthlyAmount
                  verifiedAmount
                  incomeType
                  ... on StandardEmploymentIncome {
                    employment {
                      ... on StandardEmployment {
                        employer {
                          name
                        }
                      }
                    }
                  }
                }
              }
              pageInfo {
                hasNextPage
                endCursor
              }
            }
          }
        }
      }
    }
  }
`;

interface IncomeNode {
  id: string;
  statedMonthlyAmount: number | null;
  verifiedAmount: number | null;
  incomeType: string;
  employment?: {
    employer?: {
      name: string;
    };
  };
}

interface BorrowerNode {
  id: string;
  firstName: string;
  lastName: string;
  incomes: {
    edges: Array<{
      node: IncomeNode;
    }>;
    pageInfo: {
      hasNextPage: boolean;
      endCursor: string | null;
    };
  };
}

interface LoanData {
  loan: {
    borrowers: {
      edges: Array<{
        node: BorrowerNode;
      }>;
    };
  };
}

/**
 * Polls for verified income values for a given loan
 * @param client - GraphQL client configured with your Pylon API endpoint and auth
 * @param loanId - The ID of the loan to check
 * @param maxAttempts - Maximum number of polling attempts (default: 30)
 * @param intervalMs - Polling interval in milliseconds (default: 2000)
 * @returns Promise resolving to verified incomes or null if verification not available
 */
async function pollForVerifiedIncomes(
  client: GraphQLClient,
  loanId: string,
  maxAttempts: number = 30,
  intervalMs: number = 2000
): Promise<IncomeNode[] | null> {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const data = await client.request<LoanData>(GET_VERIFIED_INCOMES, {
      loanId,
    });

    const allIncomes: IncomeNode[] = [];
    
    // Collect all incomes from all borrowers
    for (const borrowerEdge of data.loan.borrowers.edges) {
      const borrower = borrowerEdge.node;
      
      for (const incomeEdge of borrower.incomes.edges) {
        const income = incomeEdge.node;
        allIncomes.push(income);
        
        // Check if this income has been verified
        if (income.verifiedAmount !== null && income.verifiedAmount > 0) {
          console.log(
            `✓ Income verified for ${borrower.firstName} ${borrower.lastName}: ` +
            `$${income.verifiedAmount}/month (${income.incomeType})`
          );
        }
      }
    }

    // Check if any incomes have been verified
    const verifiedIncomes = allIncomes.filter(
      (income) => income.verifiedAmount !== null && income.verifiedAmount > 0
    );

    if (verifiedIncomes.length > 0) {
      return verifiedIncomes;
    }

    // If no verified incomes yet, wait before next attempt
    if (attempt < maxAttempts - 1) {
      console.log(
        `No verified incomes yet (attempt ${attempt + 1}/${maxAttempts}). ` +
        `Polling again in ${intervalMs}ms...`
      );
      await new Promise((resolve) => setTimeout(resolve, intervalMs));
    }
  }

  console.log('Income verification not available after maximum attempts');
  return null;
}

// Example usage
const client = new GraphQLClient('https://api.pylon.mortgage/graphql', {
  headers: {
    Authorization: `Bearer ${process.env.PYLON_API_KEY}`,
  },
});

// Poll for verified incomes
pollForVerifiedIncomes(client, 'loan-123')
  .then((verifiedIncomes) => {
    if (verifiedIncomes) {
      console.log(`Found ${verifiedIncomes.length} verified income(s)`);
      // Use verified income data for your application logic
    } else {
      console.log('Income verification not available - fallback to traditional methods');
      // Fallback to requesting pay stubs, W-2s, etc.
    }
  })
  .catch((error) => {
    console.error('Error polling for verified incomes:', error);
  });
Key implementation details:
  • Polling strategy: The function polls at regular intervals (default: every 2 seconds) until verified incomes are found or max attempts are reached
  • Verification check: Checks if verifiedAmount is not null and greater than 0
  • Error handling: Includes error handling and fallback logic for when verification isn’t available
  • Multiple borrowers: Handles loans with multiple borrowers and collects all incomes
  • Pagination: The example shows pagination structure, though you may need to implement cursor-based pagination for loans with many incomes

Benefits

For borrowers

  • No document upload: No need to upload pay stubs, employment letters, or tax documents when verification is successful
  • No HRIS login: Borrowers don’t need to log into their employer’s HRIS system or payroll portal
  • Faster processing: Automated verification is faster than manual document review

For lenders

  • Reduced conditions: Pylon’s underwriting system generates fewer conditions when income is verified through D1C/AIM sources
  • Fewer borrower tasks: Pylon creates fewer tasks for borrowers when verification is successful
  • Faster closes: Pylon’s automated processing accelerates loan processing and reduces time to close
  • Better accuracy: Verified data from Truv or The Work Number is more accurate than manual entry or document review
  • Cost savings: Reduced manual processing and document review costs (with Truv offering lower production costs)

Limitations and fallbacks

When automated verification doesn’t work

Income verification through Truv or The Work Number may not be available if:
  • Truv opt-in not completed: The borrower didn’t opt in to Truv or didn’t complete the account connection process
  • Employer not covered: The borrower’s employer doesn’t participate in The Work Number
  • Self-employed borrowers: Both Truv and The Work Number are primarily for W-2 employees. Self-employed borrowers require different verification methods (tax returns, profit & loss statements, etc.)
  • Recent employment changes: If a borrower recently changed employers, verification services may not have the latest information yet
  • Contractor or gig workers: Non-traditional employment arrangements may not be covered

Fallback to traditional methods

When automatic verification isn’t available, Pylon will request traditional income verification documentation from borrowers:
  • Pay stubs: Recent pay stubs (typically 2-3 months)
  • Employment letters: Employment verification letters from employers
  • Tax returns: For self-employed borrowers, tax returns and profit & loss statements
  • W-2s and 1099s: Tax documents for income verification
  • Bank statements: Bank statements to verify income deposits
Always have a fallback: Don’t assume all borrowers will have income verified through Truv or The Work Number. Truv requires borrower opt-in, and The Work Number may not cover all employers. Always be prepared to use traditional verification methods when automated verification isn’t available.

Best practices

Monitoring verification status

  • Poll regularly: Check income verification status as part of your loan status polling workflow
  • For Truv: Request access tokens from Pylon and initialize the Truv Bridge component for borrowers who opt in
  • For The Work Number: No action required-verification happens automatically once SSN is available
  • Handle failures gracefully: If verification fails or isn’t available, immediately switch to traditional verification methods

Using verified data

  • Combine with other verifications: Use Truv or The Work Number alongside other D1C/AIM approved sources (like Plaid for assets) for maximum benefit
  • Pylon updates pricing automatically: Once income is verified, Pylon automatically recalculates pricing and eligibility, as verified income may affect loan terms
  • Incomes - Complete guide to the Income entity, including all income types, states (stated, qualified, verified), and fields
  • Day 1 Certainty & AIM Overview - Learn about D1C and AIM programs and their benefits
  • Asset Verification - Learn about Plaid for automatic asset verification
  • Credit Pulls - Understand credit pull requirements (SSN is collected here for income verification)
  • Order-Outs Overview - Understand when order-outs and verifications are triggered