Locking a Loan Without LOS
This guide covers the full lock lifecycle when you are not using a Loan Origination System (LOS) like Encompass. Instead of pulling loan data from an LOS, you create and manage price quotes directly through the API.
Prerequisites
Before integrating, make sure the following are set up for your company:
- Pricing -- pricing must be configured for the company (the Lender Price pricing team can help with this).
- User Group -- at least one user group must exist in the Workflow tab.
- User -- a user assigned to that user group. This user's username and password are used to obtain the JWT token for all API calls.
- API Secret -- generated on the company profile.
- Lock Policy -- a lock policy created for the company's needs, and the user's group assigned to it. The lock policy drives which statuses and transitions are available.
Locking Policy
The locking policy is the lock schema that is determined by the lender company of the loan. The locking policy is the flow of locking procedures. It determines allowable locking terms, the procedural flow of lock steps, allowable edits to the lock, and other related factors. It is not determined by Lender Price.
Depending on the lender's locking policy, when a user locks a loan, they might lock the loan automatically, or send a lock request that must be approved by the lender before the lock is complete. The Lender Price API allows companies to create their own locking workflow.
Workflow Overview
Lock With Scenario is the step that creates the Lock and its first event (Priced). Its response provides the lockId (currentLock.id) and lockEventId (currentLock.lockEvents[last].id) used by every later call.
Post-lock operations include: Reprice, Extension, Post-Lock Price Concession, Relock (Worst Case), Cancel Lock, Allow Current Market Pricing, and Auto Reprice.
Lock Status Flow
The names above are illustrative -- actual status names are configured per lender. To identify statuses programmatically, use the logic fields in the next section.
Identifying Lock Statuses
Status names (for example "Lock Requested" or "Request Denied (Pre-Lock)") are configured per lender in the lock policy, so they vary from company to company. Do not match on names. Instead, identify each status returned by Get Next Status by its logic fields:
| Event | Identify by |
|---|---|
| Price (root) | status.root == true -- the initial event; can repeat (price many times without locking) |
| Lock request | lockStatusLogic == "Requested" (typically with requireReprice == true) |
| Locked | lockStatusLogic == "Locked" |
| Denied / rejected | lockStatusLogic == "Rejected" |
| Cancelled | lockStatusLogic == "CanceledOrWithdrawn" |
| Expired | lockStatusLogic == "Expired" -- created by Lender Price's daily job |
| Reprice request | lockStatusLogic == "ModificationRequested" and requireReprice == true |
| Extension request | lockExtender.enable == true and lockExtender.type == "Request" |
| Extension grant | lockExtender.enable == true and lockExtender.type == "Response" -- always auto-status to Locked |
| Concession request | lockException.enable == true and lockException.requestType == "Request" |
| Concession grant | lockException.enable == true and lockException.requestType == "Grant" -- always auto-status to Locked |
| Relock (worst case) | worseCase == true and requireReprice == true -- available after Cancelled or Expired |
| Allow current market pricing | lockStatusLogic == "None" -- empty event triggered by the lock desk |
A status can also be configured with autoStatus == true and autoStatusLockStatusId pointing to a follow-up status (for example, a lock request that locks automatically).
How Values Flow Between Calls
Most integration problems come from passing a stale or wrong ID between calls. This section shows exactly where every value is born and which calls consume it.
ID Reference
| Value | Where it comes from | Changes? | Used by |
|---|---|---|---|
accessToken | /oauth/token response | Expires -- refresh as needed | Every call (Authorization: Bearer) |
userId | Your user setup (see Prerequisites and Getting Started) | Never | userIdToUse, userIdToUseWorseCase |
search | The search payload you build for Get Pricing | When criteria change | Create Price Quote, Set Scenario, Lock With Scenario |
lineResult | Get Pricing response -- the line the user selects | Each selection | Set Scenario, Lock With Scenario, SellSide |
priceQuoteId | Create Price Quote response id | Never for a loan | Almost every call |
integrationRef.DUMMY | Create Price Quote response | Never | Updating the same loan via price-quote/create |
lockId | Lock With Scenario response currentLock.id | Never for a loan | Get Next Status, desk-hours check, every addLockEvent |
lockEventId | currentLock.lockEvents[last].id | After every event | Get Next Status, every addLockEvent |
scenario | currentLock.lockEvents[last].scenario (or Set Scenario) | After reprice / relock | Lock request, reprice, and relock events only |
lockStatusId | Get Next Status response -- select by logic fields | Per action | Every addLockEvent, both validation checks |
exceptionDataId | You generate it: priceQuoteId + a random string | Per concession | Concession payload, then Edit/Remove Exception |
queueItemId | Every addLockEvent response (except root pricing events) | Per event | /rest/queue/{queueItemId} |
Common Pitfalls
- Stale
lockEventId-- it changes after every event. Always re-fetch the price quote and takecurrentLock.lockEvents[last].idimmediately before eachaddLockEvent. - Hardcoded
lockStatusId-- status ids belong to your lender's lock policy and can change. Always call Get Next Status and select bylockStatusLogic/lockExtender/lockExceptionfields, never by id or name. scenariosent where it must be null -- lock request, reprice, and relock events carry a scenario; approve, deny, extension, and concession events must send"scenario": null.
Guide Pages
This workflow is split across the following pages:
- Submit a Lock Request -- Create a price quote, set the scenario, create the lock, run validations, and submit the lock request (with or without SellSide).
- Approve or Reject a Lock -- Approve a lock, deny a lock request, and lock with a price concession.
- Post-Lock Operations -- Reprice, extension requests, relock (worst case), post-lock price concessions, cancel lock, allow current market pricing, edit/remove exceptions, the lock event queue, and auto reprice.
Key Endpoints
All endpoints use the Authorization: Bearer {{accessToken}} header (see Authentication).
| Operation | Endpoint | Method |
|---|---|---|
| Get Pricing | /rest/v1/lp-ppe-api/pricing/search?userIdToUse={userId} | POST |
| Pricing With Existing Lock | /rest/v1/lp-ppe-api/price-quote/{priceQuoteId}/search?userIdToUse={userId}&lockStatusId={nextLockStatusId} | POST |
| Create / Update Price Quote | /rest/v1/lp-ppe-api/price-quote/create | POST |
| Get Price Quote | /rest/v1/lp-ppe-api/los/price-quote/get/{priceQuoteId} | POST |
| Set Scenario From Result (unlocked) | /rest/v1/lp-ppe-api/price-quote/setScenarioFromResult/EllieMaeQuoteRequest | POST |
| Set Scenario From Result (locked) | /rest/v1/lp-ppe-api/price-quote/setScenarioFromResult/EllieMaeBuyRequest | POST |
| Lock With Scenario | /rest/v1/lp-ppe-api/price-quote/lockWithScenario | POST |
| Get Next Actions | /rest/v1/lp-ppe-api/price-quote/getNextLockStatus/{lockId}/{lockEventId} | GET |
| Add Lock Event | /rest/v1/lp-ppe-api/price-quote/addLockEvent | POST |
| Check Desk Hours | /rest/v1/lp-ppe-api/checkLockStatus/{lockId}/{statusId} | GET |
| Check Rate Period | /rest/v1/lp-ppe-api/isRatePeriodStillActive/{priceQuoteId}/{statusId} | GET |
| Auto Reprice | /rest/v1/lp-ppe-api/price-quote/auto-reprice | POST |
| Edit / Remove Exception | /rest/v1/event/fee-change/{priceQuoteId} | POST |
| Check Queue Item | /rest/queue/{queueItemId} | GET |
| Queue Items by Loan | /rest/v1/integration/ellie/loanLock/ellieQueueDataNotProcessed?priceQuoteId={priceQuoteId} | GET |
Several endpoint URLs contain EllieMae or ellie for historical reasons. They do not involve EllieMae in the no-LOS flow.
Authentication
Endpoints use the Authorization: Bearer {{accessToken}} header for authentication. See Submit a Lock Request for how to obtain the token.