Contractor License Numbers in Loan Applications: Capture the Identifier Before Calling the API

September 11, 2026
September 11, 2026
15 Minutes Read
Business Verificationblog main image

Executive Summary: Contractor license numbers only become useful to a construction lender once the issuing state and the precise identifier are captured correctly on intake, before anyone attempts a lookup. This article sets out a field-level workflow for capturing that identifier, reconciling what a lookup returns against the application, and documenting the result as a customer-owned record rather than a credit decision.

Why Does A License Number Belong In Its Own Field?

What happens when the identifier lives in a free-text note instead?

Loan origination systems often treat contractor credentials as an afterthought, tucked into a comments box or a generic custom attribute alongside unrelated notes. Cobalt's documentation on collecting contractor license data recommends storing license numbers in a dedicated field rather than a generic custom attribute, because a dedicated field keeps formatting consistent and reduces friction when the number is later sent to a lookup service[1]. When the identifier sits inside a paragraph of intake notes, a reviewer has to re-key it before any lookup, and re-keying is where transposition errors creep in. California's own contractor number format illustrates the risk: CSLB explains that a California contractor license number contains no alphabetic characters and should not exceed eight digits, entered from the left position[2]. A number stored loosely as "Lic #7A1234" would fail a same-day lookup attempt against that state format.

Why does a dedicated field reduce downstream correction cycles?

A dedicated licenseNumber field paired with a dedicated state field gives operations a stable place to validate format before any external call is attempted. This lines up with the current contractor search contract, which requires licenseNumber, state, and callbackUrl as parameters[3]. If the state value is missing or the license number field is blank, the request itself will fail validation rather than return a false result, since the documented 400 response for a missing parameter states that license number, state, and callback URL are required[3]. Treating the intake field as the single source of truth for what was submitted, distinct from what a lookup source later returns, is consistent with how Cobalt's related guidance frames identity resolution: keep the submitted identity, the source record, the normalized fields, and the lender decision separate rather than overwriting one with another[4]. We recommend that any origination form field for a contractor license carry its own label, its own state selector, and its own validation rule, independent of any note field used for general underwriting commentary.

• Dedicated field. Store the license number separately from free-text notes to preserve formatting and reduce re-keying errors[1].

• Paired state selector. Capture the issuing state alongside the number since the lookup requires both together[3].

• Format awareness. A California contractor license number contains no alphabetic characters and should not exceed eight digits[2]. Confirm the expected format for every other supported state with that state's own licensing authority rather than assuming California's rule applies.

• Separate identity layers. Keep the submitted identity, the source record, the normalized fields, and the lender decision as four distinct records rather than one overwritten field[4].

• Callback awareness. Any request missing a required parameter returns a documented 400 error rather than a partial result[3].

What Does The Current ContractorSearch Contract Actually Require?

Which parameters must be present before a request can succeed?

The current contractorSearch API documentation specifies three required parameters: licenseNumber, state, and callbackUrl[3]. There is no parameter for a business name, a contractor's personal name, or any other identifier. A request that omits any of the three required values returns a 400 response with the message that license number, state, and callback URL are required[3]. This means an operations team cannot substitute a company name for a license number and expect the lookup to succeed. The documentation states this endpoint's coverage five times and does not agree with itself. Two parameter descriptions read ca, fl, ny, or, tx, while two further parameter descriptions and the endpoint's own summary sentence give four states, the summary putting it as supported states are ca, fl, ny and tx[3]. Whether that or is Oregon's code or a stray conjunction in a list carrying no other conjunction cannot be settled from the page. Treat the endpoint as documented for ca, fl, ny and tx, confirm Oregon directly before routing an Oregon file to it, and read the list as current coverage rather than a promise that every state or every trade is included[3].

How does the callback pattern change what "complete" means?

The response pattern is asynchronous. A successful request returns a status indicating the completed request will be sent to the callback URL, along with a requestId and callbackUrl in the immediate response body[3]. The actual contractor data, including fields like businessName, licenseNumber, licenseType, status, issueDate, expirationDate, bondCompany, and alternateNames, arrives later at the callback endpoint tied to that same requestId[3]. A reviewer who checks the file five minutes after submission and sees no result should not treat that as a failed lookup; the documented pattern is asynchronous completion, not an immediate synchronous answer. This distinguishes contractor license lookups from a simple form submission and has direct workflow implications: a queue or status field in the loan origination system needs a state for "request sent, awaiting callback" distinct from "result received" and "result reviewed." Cobalt's related material on contractor job-level data reinforces that even adjacent data, like permits or notices of commencement, is fragmented across state, county, and city sources, and is prioritized only where requirements are standardized[5]. That fragmentation is a separate limitation from the callback timing question, but both point to the same operating principle: document what was requested, what state covers it, and when a result actually arrived, rather than assuming completeness at submission time.

Why Not Just Search By Business Name At The API?

Does searching by company name save the borrower a step?

It is tempting to let the borrower submit only a business name, since a company name is what appears on invoices, insurance certificates, and the loan application cover sheet. But the current contractorSearch API contract does not accept a business name as an input parameter at all; it requires licenseNumber, state, and callbackUrl[3]. A name-only submission simply cannot be sent to this endpoint. Where a state licensing board offers its own name-based search, such as CSLB's business name and personnel name lookup tools[2], that lookup can help an operations team discover a candidate license number when the borrower did not supply one. CSLB's own search guidance describes trimming names, adjusting for "The" or "A" prefixes, and returning up to fifty or one hundred candidate matches depending on the field searched[2]. That is a distinct, manual discovery step, not a substitute for the identifier the API contract requires.

What should operations do when only a business name is on file?

We recommend a two-step sequence: first use the state board's own name search, where offered, to identify a candidate license number and confirm the associated entity name; then submit that discovered licenseNumber and state to the contractorSearch endpoint for a documented status and detail response[3]. This sequencing matters because a business name search against a board tool can return multiple candidates, including similarly named entities, and CSLB's own disclaimer on license detail pages notes there may be relevant information not yet entered into the license database[6]. Treating a name match as equivalent to a confirmed license number risks attaching the wrong contractor's credential history to the loan file. The governing principle here is straightforward:

A business name is a starting point for discovery, not an identifier the lookup API accepts; only a reconciled license number and state belong in the request.

Recording the discovery step separately from the lookup step, with a timestamp and the reviewer's initials, keeps the file auditable even when name search required several attempts.

How Should A Reviewer Reconcile A Returned Holder To The Application?

What fields actually need side-by-side comparison?

Once a callback result arrives, the reviewer's task shifts from "did the lookup work" to "does the returned holder match the application." The documented response body includes businessName, entity, dbaName, alternateNames, streetAddress, city, state, zip, status, licenseType, issueDate, expirationDate, bondCompany, bondAmount, and bondExpireDate[3]. A reviewer should compare the returned businessName and alternateNames against the borrower's stated legal name and any DBA on file, consistent with the practice of resolving DBA against legal entity before treating a match as settled[4]. The returned status field describes what the state record shows at that point, not whether the license fits the scope of the construction contract; a related discussion of this exact distinction notes that status answers a narrower question than project fit and should not be relabeled as a broader conclusion in the file[7].

How would the worked case for Borrower A and Entity B play out?

This is an illustrative example, not an actual transaction. Borrower A submits a construction loan application listing "Entity B Builders" as the general contractor, with no license number on the intake form. Operations searches the CSLB business name tool and finds a candidate match for "Entity B Builders Inc," which CSLB's search tips suggest may require trimming the name or adjusting for suffixes[2]. The candidate record shows a numeric California license number, referred to here as license-example-1 rather than a real number. Operations enters that number and state=ca into the intake system's dedicated fields and submits the contractorSearch request with a callback URL[3]. The callback later returns a businessName of "Entity B Builders Inc", a status of "Active", a licenseType describing a Class B general building license, and an expirationDate several months out. The reviewer compares the returned businessName against the application's "Entity B Builders" and notes the discrepancy (missing "Inc") as a formatting variance rather than a mismatch, then records the license type against CSLB's classification list, which separately defines Class B as General Building Contractor[8]. The file notes that classification, not just active status, was checked.

What Belongs In A Reviewer Worksheet For This Step?

Which fields should a worksheet force a reviewer to complete?

A usable worksheet should force explicit entries rather than a single checkbox for "license verified." We recommend the following proposed customer-owned worksheet structure, to be adapted to each lender's own loan origination system:

• Application identifier. The loan file or application-example number this worksheet is attached to, kept distinct from the license number itself.

• Submitted business name. Exactly what the borrower wrote on the application, unedited.

• Discovered license number and state. The number and issuing state found through board search or borrower documentation, entered into the dedicated fields[1].

• Request timestamp and requestId. The time the contractorSearch request was submitted and the requestId returned in the initial 200 response[3].

• Callback result fields. The returned businessName, status, licenseType, issueDate, expirationDate, and any alternateNames, copied verbatim from the callback body[3].

• Reconciliation note. A reviewer's written comparison of submitted name versus returned name, and returned classification versus the contracted scope of work[8].

• Reviewer decision label. A narrow label such as "credential status reviewed" rather than a broad label like "contractor cleared," preserving the boundary between source data and lender conclusion[7].

How should incomplete fields be flagged for follow-up?

Not every application will arrive with a license number, and not every board name search will resolve to a single confident match. A workable policy is to add a status value of "pending identifier" to the worksheet when no license number has been captured, distinct from "pending callback" once a request has been submitted, and distinct again from "reviewed" once the callback has been reconciled. This three-state tracking avoids the common failure where a file sits with an empty result field and no record of whether anyone attempted the lookup at all. Below is an illustrative customer-owned review record showing how these fields might be stored; it is a proposed control pattern, not a Cobalt request or response schema:

{
  "record_type": "illustrative customer-owned review record",
  "application_example_id": "application-example-1",
  "submitted_business_name": "Entity B Builders",
  "discovered_license_number": "license-example-1",
  "discovered_state": "ca",
  "request_timestamp": "not applicable in this illustrative record",
  "callback_status": "pending_identifier",
  "returned_business_name": null,
  "returned_license_type": null,
  "returned_status": null,
  "reconciliation_note": "Name search located candidate match; license number not yet confirmed against borrower documents",
  "reviewer_decision_label": "credential status not yet reviewed"
}

Which State Coverage Details Actually Matter Here?

Does the documented state list mean every trade is covered in those states?

The endpoint's documented state list, contested as described earlier, says which states the lookup currently supports, not which trades within those states are covered, and it is not a guarantee of coverage across every license classification a state issues. California alone maintains dozens of specialty classifications, from C-10 Electrical to C-36 Plumbing to C-53 Swimming Pool Contractor, each with its own published description under the Business and Professions Code classification article[8]. A returned licenseType field should be read against that classification list rather than assumed to match the trade named in the loan file. Related guidance on specialty trade verification makes the same point for HVAC, electrical, and plumbing work specifically, noting that these trades are licensed by different authorities and that coverage should not be treated as uniform across a single lookup source[9]. CSLB's own consumer guidance treats the question of which type of contractor a project needs as a separate step from checking a license, listing it alongside permit and contract questions rather than folding it into the status lookup[10]. A lender's review should keep the same separation.

What should operations do for a state or trade outside current coverage?

When the borrower's contractor is licensed in a state outside the documented list, or in Oregon while that entry stays unconfirmed, the contractorSearch endpoint is not the applicable tool for that file. Operations should fall back to the relevant state board's own public lookup, where one exists, and record that fallback explicitly on the worksheet rather than leaving the license field blank. CSLB's own public license check tool is one example of a direct-authority lookup that remains available regardless of any API's documented state coverage[2]. We recommend that the worksheet's "discovered license number and state" field always carry a note distinguishing "confirmed via API" from "confirmed via direct board lookup" from "confirmed via borrower-submitted document," since these three paths carry different documentation trails if a file is later audited.

What if the identifier is correct but the trade classification is wrong?

Here is a short contrasting case, again illustrative rather than observed. Suppose Borrower A's application correctly lists a license number and state, and the contractorSearch callback confirms an active, matching business name. On its face this looks like a clean result. But the returned licenseType shows a C-33 Painting and Decorating Contractor classification, while the loan file describes a contracted scope of structural framing work, which under CSLB's classification list falls under C-5 Framing and Rough Carpentry Contractor, a distinct classification[8]. In this exception, the identifier capture and lookup process worked exactly as documented, yet the file still requires a scope-classification review before proceeding, because an active and correctly matched license in the wrong classification does not establish that the contractor is licensed for the work described in the loan. This is the same boundary discussed in related material: status and classification are separate returned fields, and neither substitutes for a documented comparison against the actual contracted scope[7].

What Decision Table Should Guide Routing After A Result Arrives?

How should a reviewer route a file based on what came back?

A small routing table helps standardize what happens after a callback result or a board lookup returns.

ScenarioReturned or found dataRecommended routing
License number and state captured at intake, callback confirms matching business name and active statusstatus: Active, businessName matches applicationProceed to underwriting review of classification versus contracted scope[8]
Only business name on file, board search finds one strong candidateCandidate license number located via CSLB name search[2]Confirm candidate against borrower documents before submitting to contractorSearch
Business name search returns multiple similarly named candidatesFifty or more possible matches per CSLB tool behavior[2]Request license number directly from borrower rather than guessing among candidates
Contractor licensed outside ca, fl, ny and tx, Oregon included while contestedCoverage not documented for that state[3]Route to direct state board lookup and note fallback method on worksheet
Callback returns businessName differing from application, with matching license numberName mismatch, identifier matchInvestigate DBA versus legal entity distinction before proceeding[4]

Why does this table stop short of an automated decision?

Each row in the table produces a next step, not a final credit conclusion. Even a row showing an active status match still requires the reconciliation step described earlier, comparing classification to project scope, since an active credential does not by itself establish that the license fits the work described in the construction contract[7]. The routing table is meant to reduce ambiguity about what to do next, not to replace underwriting judgment about credit or project risk.

What does an inactive or mismatched result actually tell the lender?

A returned status other than active, or a returned businessName that does not reconcile with the application after DBA analysis, tells the lender that the credential record does not currently support the application as submitted. It does not, on its own, establish fraud, misrepresentation, or an automatic decline. CSLB's own license detail disclaimer notes that due to workload there may be relevant information not yet entered into the board's database, and that a license number search may return "License Number does not exist" for reasons unrelated to the contractor's actual standing[6]. A reviewer should treat a non-matching or inactive result as a trigger for a follow-up conversation with the borrower or a request for updated documentation, not as a final credit conclusion. The worksheet entry for that file should record the exact returned value, the date it was received, and the follow-up step assigned, so that a later reader can tell the difference between "the board record disagreed with the application" and "nobody finished the review."

Where Does This Fit Inside A Broader Origination Workflow?

Where does the license capture step sit relative to other intake tasks?

Contractor license capture is one narrow step inside a larger construction loan intake process that also covers borrower entity verification, project budget review, and draw schedule setup. It should not be treated as a gate that blocks every other intake task, since a license number may take additional time to discover through board search or borrower follow-up. We recommend running license identifier capture in parallel with other intake tasks, with the worksheet's "pending identifier" status flagging the file for follow-up rather than halting the entire application. This mirrors the asynchronous nature of the callback pattern itself: a request can be submitted and other work can continue while the result is awaited[3].

How does this narrow scope differ from job-level or entity-level checks?

License number capture and reconciliation is deliberately narrower than two adjacent tasks: confirming job-level data such as permits or notices of commencement, and resolving the borrower's legal entity against a submitted DBA. Job-level data is fragmented across state, county, and city sources, and Cobalt's own materials describe prioritizing only jurisdictions where such filings are standardized at the state level, naming states like Florida, Georgia, Iowa, Michigan, Ohio, and Utah as examples where some job-level filings exist at the state level[5]. Entity resolution, separately, requires comparing a submitted DBA against Secretary of State registration records before treating any match as settled[4]. Neither of those tasks should be folded into the license worksheet described in this article; each deserves its own dedicated record and its own reviewer sign-off, connected only through the shared application identifier.

If your team is refining how contractor license identifiers move from intake through lookup and reconciliation, bring your own scenario to a walkthrough on the Cobalt Intelligence demo page so the discussion can center on your current fields, your current states, and your current worksheet gaps.