Skip to main contentSkip to content

Updated Aug 19, 2026

Application detail — Offers tab

Last updated: 2026–05-27

The Offers tab (labeled Offers in the main tab bar) shows which offer is selected for the application, all eligible offers or manual offer history, and optional amortization schedules from your pricing or LMS integrations. Use it after underwriting to confirm what the applicant was offered, to add or adjust a manual offer, and to inspect payment schedules.

Open it from Agent Dashboard → application → Offers.

[NEEDS SCREENSHOT: Offers tab with Selected Offer, Eligible Offers, and Amortization sections]


Who can do what (roles)

ActionWho sees itBehavior
View all accordionsUsers with application detail accessIncludes Auditor (read-only).
Add Offer buttonAdmin (tenant admin, manager, sub-tenant admin, super admin) or Analyst when underwriting produced offer variablesOpens Modify Offer modal.
Modify Offer — change valuesAdmin and Analyst (when modal is open)Analysts edit values only.
Modify Offer — add/remove variables, change variable dropdownAdmin onlyAnalysts: variable select and trash disabled.
Add Variable (inside modal)Admin only
Header Update Status, Request OTPNot AuditorAuditors can view offers but cannot use those header actions.

Auditor summary: Auditors can read Selected Offer, tables, and amortization; they do not see Add Offer (not admin/analyst path) and cannot update manual offers through this UI.


Layout overview

SectionAlways visible?Purpose
Selected OfferYesHighlighted card for the active offer (engine-selected or latest manual).
Eligible OffersYesTable of alternatives or manual offer rows.
Amortization DataOnly when amortization payload existsSchedule rows (LendAPI, Totality, LoanPro, etc.).

While data loads, you see an input skeleton and table skeleton.

Each section is a collapsible accordion (chevron on the section title). Selected Offer can also expose Add Offer on the accordion header when your role allows.

[NEEDS SCREENSHOT: Offers tab three accordions expanded]


Selected Offer accordion

Header card (when data exists)

AreaContent
TitleManual Offer if the latest manual offer is active; otherwise offer name / title from engine.
SubtitleManual: Added by {user}; Engine: {lender_name}.
BadgeSelected (success/green).
Timestampupdated_at for the selected offer.
Field gridLabeled metrics (amount, rate, term, payment, etc.)—labels come from server keys (title-cased). Long values truncate with tooltip on hover.

Values are formatted using workspace variable display types when variables are registered (e.g. currency as $X.XX, percentages with %).

[NEEDS SCREENSHOT: Selected Offer card with Manual Offer badge and field grid]

Add Offer (secondary button on accordion)

Label: Add Offer

Visibility:

  • Admin: Button shown on Selected Offer accordion (when accordion header actions are enabled).
  • Analyst: Shown only if underwriting Offer Table produced at least one output object (variables to seed the modal).
  • Others (e.g. Auditor, CSR without analyst): Button hidden.

Action: Opens the Modify Offer modal (title in UI: Modify Offer).

Empty states — Selected Offer

ConditionWhat you see
No selected_offer_data at allNo information to show on this tab
selected_offer_data exists but no display fields after filtering metadataSame empty message inside the accordion

Metadata keys stripped from the card: updated_by, updated_at, selected_card_header, selected_offer_widget_data.


Modify Offer modal (Add Offer)

Opened from Add Offer on the Selected Offer accordion.

ControlDescription
TitleModify Offer
DescriptionUpdate offer for the application.
Variable rowsEach row: variable selector + value input + remove (trash).
Add VariableAdmin only; appends a blank row.
CancelCloses without saving.
UpdateSaves manual offer via API; disabled until form is dirty, all rows have variable + value, and at least one row exists. Shows loading while saving.

Initial values:

  • If a manual offer is already selected, rows load from current selected_offer_data.
  • Otherwise rows seed from the first Offer Table underwriting result (app_decision.offer_data[0].output[0]).

Analyst vs Admin in the modal:

CapabilityAdminAnalyst
Edit valuesYesYes
Change which variable a row usesYesNo (selector disabled)
Remove rowYesNo
Add VariableYesNo

On success: toast Offer updated successfully; modal closes; Selected Offer, Eligible Offers, and related queries refresh.

[NEEDS SCREENSHOT: Modify Offer modal with variable rows and Update button]

Use cases — Manual offer

ScenarioSteps
Engine offer wrong; policy allows manual overrideAdmin or Analyst (with seed data) → Add Offer → adjust values → Update.
Ad-hoc counterofferAdmin → Add OfferAdd Variable for extra fields → Update.
Tweak one field on existing manual offerAdd Offer → change value → Update (must change something—Update stays disabled if pristine).

Troubleshooting — Modify Offer

ProblemCauseFix
Update disabledNo changes, missing variable name, or empty valueFill all rows; change at least one value.
Add Offer not visibleRole or no underwriting offer outputAdmin: should still see button; Analyst: run decision first.
Failed to update offer toastAPI / validation errorCheck required variables; confirm app not locked by stage.
Analyst cannot add fieldsBy designAsk admin to add variables or perform update.

Eligible Offers accordion

Shows alternatives so you can compare paths that were not selected (or manual offer history when a manual offer is active).

Mode 1 — Engine / multi-lender offers (default)

When: No active manual offer selection (manual timestamp not driving selection).

Table behavior:

FeatureBehavior
ColumnsLender Name (if multi-offer payload); plus dynamic columns from all offer output keys.
SortingColumn headers sortable.
SearchGlobal filter enabled.
Row selectionCheckboxes enabled (for export/copy patterns in the table component).
Cell displayLong text tooltips; currency/percent formatting via variable display types.

Data sources (first match wins in code):

  1. lender_multi_offer.multi_offer_data when multi-lender offers exist.
  2. Otherwise offer_data array on the application offers response.

[NEEDS SCREENSHOT: Eligible Offers table with lender column and sortable headers]

Mode 2 — Manual offer history

When: A manual offer is the selected offer (updated_at on manual row wins in selection logic).

Table behavior:

FeatureBehavior
ColumnsKeys from manual offer objects (title-cased headers).
SortingOff
SearchOff
Rowsmanual_offer_data reversed so most recent first

Use this to audit prior manual versions, not to select a row in the UI (selection is informational).

Empty states — Eligible Offers

ConditionMessage
Manual mode, no manual rowsNo information to show on this tab
Engine mode, no column dataSame
Engine mode, data but zero table rows after flatteningSame

Amortization Data accordion

When it appears: Only if the API returns amortization under amortization_data (tried in order: LendAPI non_autopay, LendAPI root, Totality, LoanPro—normalized to row arrays).

Table behavior:

FeatureBehavior
ColumnsDynamic keys from schedule rows (payment number, principal, interest, balance, etc.—depends on integration).
SortingEnabled
SearchEnabled
PaginationDisabled (all rows in one scrollable table)
Row selectionEnabled

When hidden: If no amortization array exists after normalization, the entire accordion is not rendered (not the same as an empty message).

[NEEDS SCREENSHOT: Amortization Data table with multiple columns]

Empty state inside accordion

If amortization exists but flattens to zero rows: No information to show on this tab.

Use cases — Amortization

ScenarioWhat to do
Verify payment schedule shown to applicantOpen Amortization Data; sort by period; compare to Selected Offer payment field.
Totality / LoanPro boardingConfirm schedule imported—columns reflect partner field names.
No accordionOffer may not have generated amortization yet; check pricing engine or integration jobs.

How “selected offer” is determined

The UI picks one offer object for the Selected Offer card using this logic:

  1. If manual offer data has entries and the latest manual row has updated_at, treat as manual offer (most recent manual row wins).
  2. Otherwise use engine selected_offer_data from multi-offer payload or top-level selected_offer_data[0].
  3. Display name, lender, updated_by / updated_at, and a map of fields for the grid (excluding internal metadata keys).

Implication: after you Update a manual offer, Eligible Offers switches to manual history mode and Selected Offer shows Manual Offer.


Value formatting

Offer and amortization tables format cell values using workspace variables (engine_name match):

Display typePresentation
Currency (type 2)$X.XX for numbers
Percent (type 3)X.XX% (stored decimal × 100)
Other / defaultRaw string or number
Empty--
Booleantrue / false

Workflows (end-to-end)

Workflow A — Confirm engine offer after decision

  1. Complete underwriting; verify UnderwritingOffer Table has results.
  2. Open OffersSelected Offer shows engine name, lender, and terms.
  3. Review Eligible Offers for alternate lenders/products.
  4. If amortization generated, validate Amortization Data against compliance disclosures.

Workflow B — Manual counteroffer (Analyst)

  1. Ensure decision ran so underwriting offer output exists (seeds modal).
  2. Add Offer → adjust allowed values → Update.
  3. Selected Offer shows Manual Offer and Added by.
  4. Eligible Offers lists manual history rows.

Workflow C — Manual offer (Admin, extra variables)

  1. Add OfferAdd Variable → pick variable from tenant catalog → enter value.
  2. Repeat for all required fields → Update.
  3. Share PDF or applicant portal link from other tabs as per process.

Workflow D — Auditor review

  1. Open Offers (no Add Offer).
  2. Read Selected Offer and Eligible Offers / amortization.
  3. Cross-check UnderwritingOffer Table trace for how numbers were produced.

Examples

Example 1 — Multi-lender eligibility
Selected Offer shows Lender A’s 60-month term. Eligible Offers lists Lender A and Lender B with rate/term columns; underwriter confirms applicant was presented Lender A only. Amortization Data shows 60 rows matching APR.

Example 2 — Analyst adjustment
Underwriting output included loan_amount and apr. Analyst opens Add Offer, changes loan_amount, cannot add origination_fee (selector disabled). Admin later adds fee via Add Variable.

Example 3 — Empty eligible table
Decision approved but pricing returned no rows—Eligible Offers shows No information to show on this tab while Selected Offer may still show widget data if backend sent selected_offer_data only. Escalate to pricing engine configuration.


Troubleshooting (quick reference)

SymptomCheck
Everything empty on Offers tabApplication session / API; whether offers endpoint returned data.
Add Offer missing for AnalystUnderwriting Offer Table must have produced output variables first.
Selected card empty but button existedFiltered fields—all keys may be metadata-only.
Wrong offer labeled selectedManual updated_at may override engine; review manual history table.
Amortization columns look oddPartner-specific field names (Totality/LoanPro/LendAPI).
Currency looks unformattedVariable not registered in workspace catalog for that engine_name.

LendAPI Field Guide.