It's been over half a year since I last posted about building a borrower funnel - but let’s get back into it.
The credit service is pretty straight forward - pull a credit report for given user information, parse it, and return the report as well as any other codes that the credit provider might have passed along.
First Name, Last Name, Middle Initial, Suffix, Address (Address1, Address2, City, State, Zip5/4), Date of Birth, SSN
I haven't seen a bureau that allows for a credit pull without first, last, and address. You could just use that, but it would cause a lot of issues.
Middle Initial, Suffix and Date of Birth all help to differentiate between Prescott Nasser Jr. and Prescott Nassser Sr. Usually Date of Birth is a required input, because the first thing your system will do at a higher level is check their age. If they are under 18, why bother even pulling credit and incurring the cost. So since you'll have it, might as well pass that into your credit pull request.
Address can also be broken down into important vs less important. Obviously address1, city, state, zip5 are the basics to pull credit, but using a City / State you can get a zip code. Alternatively using a zip code, you can get a city and state (mostly - there are a lot of edge cases for either method).
address2 is really only relevant if you ask users to enter an apartment number in a separate field. Otherwise if it's in address1, you're fine.
Additionally, you could use the address1, city, state, zip and pass it to a normalization service (USPS has a good, free, one) that will pass you back a normalized clean address, usually with zip4 included.
Finally, SSN is a tricky one, usually you don't want to ask your borrowers that up front, it's a huge friction point and will lose you a lot of potential borrowers before you can even see if they have good credit or they can see if you can give them a competitive rate (None of the top platforms will ask for SSN upfront). However, if you already have it (think returning borrowers), then you might as well pass it when getting a new credit report.
You can see in our user flows in part 2 we're only going to ask the user for just enough information that we need and the rest we'll do without.
Credit Freshness Check
Part of optimizing our credit system is knowing if we already have a fresh credit report on file that is recent. You can define recent based on your own criteria - I've seen 30 days (usually the bureaus update on a 30 day basis), so that makes sense.
You're probably tempted now to code up a quick check (pseudo ruby):
def get_credit_report(user_id) CR = CreditReport.where(:user_id => user_id, :created_at <= Date.current - FRESH_DAYS) return CR if CR.present? # Do logic to pull and parse a new credit report end
That's pretty basic, you might add a flag to ensure that the credit report was parsed correctly. Seems tempting? Sure does - but that will QUICKLY fall short of your use cases.
Consider a user changing their address because of a typo? Now your logic will return the same credit report that you just pulled. (Note: not found is a valid "credit report" response, given the same inputs, you shouldn't try to pull again if it's within your freshness timeline).
Did you know that users can put an SSN lock on their credit report? You'll need their SSN to get a credit report. Also, if you get a not found, it's common to tell the user something like "We're having trouble locating your credit report, please enter your SSN". In both cases, you're going to make a new credit request now with their SSN.
So, now you've got some creative solution to handle all of this for a user. Ok, but what about the situation where a user has forgotten their account information, or wants to just fill in a different application with a different email address (that's what most places use as their username). Now, tying credit requests to a UserId and trying to do some freshness check will fail to find that fresh credit report already on file. In truth, this is less of an issue for borrowers, more of an issue for the platform / security / efficiency.
Cool Story Bro: I have had a real world scenario where someone wrote a script to submit applications with the same exact data, with different email addresses. All of the sudden we were pulling 100's of credit reports for Joe Random. At the time our reporting was terrible, but Experian reached out to us to notify us of the situation (I don't know the level of reporting / response for the other Bureaus).
It would be great if given the same input information we get any fresh credit report on file. Might be nice to set some red flags if the same credit report is going to different users, but we'll handle that later.
So now, let’s update our pseudo ruby code:
def get_credit_report(user_id) user = User.find user_id CR = CreditReport.where(:address1 => user.address1, :city => user.city, :state => user.state, :zip => user.zip, :first_name => user.first_name, :last_name => user.last_name :date_of_birth => user.date_of_birth :created_at <= Date.current - FRESH_DAYS) return CR if CR.present? # Do logic to pull and parse a new credit report end
Alright that's much better. That query is a bit ugly, but it'll work for our pseudo purposes to illustrate the point.
Bureau reports mostly consist of the same information
- Name information, including alias's they may have on file
- Date of Birth on file (might differ from what you used to query on - red flag to check somewhere else)
- Trade lines (Open Mortgages, Bank accounts, credit cards, and personal loans
- Inquires (usually hard inquiries)
- Liens / Legal Judgements
- Attributes (Standard Attributes, Extended Decisioning Attributes, etc). These are things like "Number of 30+ days past due accounts in the last year", checkout Prospers Listing API for a really good list of the kind of attributes Experian provides
- FICO Score
In my experience, attributes and FICO score is most of the data used in credit models by platforms today. They'll incorporate past loan performance if you're a returning borrower as well.
Some of the more advanced credit models will be built using custom attributes. Many people consider auto trade lines to be very important, but the bureaus only provide broad attributes. Another obvious custom attribute is inquires. You can see at the link above, Equifax will provide inquiries last 6 months - the problem is that if someone is trying to get a loan now, they might have several recent inquires - that's to be expected for someone shopping for a loan.
To build custom attributes, you'll have to parse the trade lines yourself. Make sure to de-dupe those. Also, just a word of caution, there are a lot of regulations around what you can and can't count when building a custom attribute, so be extremely careful, it's easy to run afoul of FCBA and FCRA consumer protections.
Sometimes a FICO can't be provided
Sometimes for some reason a valid FICO score just can't be provided, even if there is a credit file found. If the person has no trade lines or happens to be dead, no FICO is provided. There are a number of other edge cases - usually it just gets translated into a No offer / decline decision up the service stack.
There are a lot of ways to slice and dice this data, but probably the most important real time data reporting will a chart showing the number of credit pulls, the number of outright failures, the number of no-hits, and the number of successful credit pulls (that returns a report).
If your provider is down, the outright failures is good to know ASAP - I've seen situations where password reset occurred (required by the bureau), and it was never changed on our end (oh those prod system admins).
Additionally, if something happens upstream and data isn't being mapped correctly - for example someone changed their system and now are no longer passing date of birth cough Prescott cough, you'll see a spike in no hits.
Passing all data required for a credit pull vs passing a UserId and letting the credit system pull together required data
This might seem like an easy decision at first, but there are a lot of caveats to it.
- If you pass in a UserId, then everyone must be a user of your system. Seems obvious, but it gets more complicated when you consider that as you scale, you will likely integrate with partners where they will pass you loan application information. This is workable, but now you have users who are not really users, you'll need a way to tag them, and of course they won't have set up a password, so you'll have to figure that flow out as well.
- You'll limit the use of your credit service in the future for other applications by tying it directly to the user system. If you wanted to pre approve a list of prospects based on credit information you'll need another way to do that
- Even if you just pass a UserId, you're going to want to log all of the data you pull from the user system for that freshness check.
The way I usually make a decision of push vs pull is how many inputs are required, where those inputs come from and how often they might change.
- There aren't too many inputs here, name, address, city, state, zip, dob, ssn
- I don't want to tie myself to the user system (making pulling much harder - where do I pull from and when)
- And the input won't really change. It's unlikely the bureaus are going to want to see loan amount, or their email address.
I usually opt to pass all the information in the request, some of it is of course optional to the credit system. It's made for a very clean standalone system.
Saving request and response XML unparsed
This is really for diagnostic purposes, if you are having issues you really want to have the raw data to dig into
Credit Request, Credit Report, and Raw XML Response
Because you might have many credit requests that return the same credit report (if it's fresh), you'll need to link the credit report provided in the response to the credit report. Additionally, you'll want to know if that request triggered a fresh pull, you want to link to the raw request / response. Finally, you want to have an easy way to link your credit report back to the raw credit response that was used to build it. (Follow all that?)
Translate bureau codes into your own codes
A no hit code (can't find), might be an "A" at one bureau and a "0" at another. I would recommend that you translate these to a single NoHit code in your system. This gives you the flexibility to drop out providers or categorize more many codes as no hit.
This should go without saying…