Underwriting

What it is

Underwriting is the credit and risk assessment. Initial underwriting decides whether to fund a new supplier at all and produces the inputs to the first offer. Daily underwriting re-evaluates an active supplier as new marketplace and bank data arrives.

The output that matters downstream is a risk rating, which drives the default progressive-pricing period table in the advance calculator.

Where it lives

Concern Location

Routes

/suppliers/:supplierKey/underwriting/* in suppliers/suppliers-routing.routes.ts

Feature root

suppliers/supplier/supplier-underwriting/

Beta rewrite

suppliers/supplier/supplier-underwriting-beta/ at /suppliers/:supplierKey/new-underwriting, behind FeatureFlagGuard

Service

supplier-underwriting/supplier-underwriting.service.ts — spans internalApiUrl and automaticMarketplaceApiUrl

Store

supplier-underwriting/store/ (the underwriting slice) and suppliers/store/background-reports/ (the backgroundReports slice)

Cross-supplier queue

underwriting/tracker/ — see UW tracker

Route structure and access

Route Screen

underwriting (parent)

SupplierUnderwritingComponent. Denies ADVANCE_AUDITORS only — restricted-UW users are allowed here (RESTRICTED_REST_UW_ACCESS_ONLY_CONFIG).

underwriting/initial

UnderwritingInitialComponent — the default child.

underwriting/daily

UnderwritingDailyComponent. Standard deny (REST_UW_ACCESS
ADVANCE_AUDITORS).

underwriting/supplier-information

SupplierInformationComponent.

underwriting/documentation

DocumentationComponent, flag-gated via FeatureFlagGuard.

underwriting/background-reports

BackgroundReportsComponent. Requires only: FINANCE — the only other allow-list clause in the app besides /admin.

underwriting/bank-transactions-report

UnderwritingReportTabComponent, data.reportId = ExternalReports.PLAID (PLAID-D).

underwriting/bank-transactions-categories

Same component, BANK_CATEGORIES (PL-BT). Flag: BANK_CATEGORIES.

underwriting/access-onboarding-predictions

Same component, ACCESS_ONBOARDING_PREDICTIONS (BR-AC-ON). Flag of the same name.

underwriting/access-daily-predictions

Same component, ACCESS_DAILY_PREDICTIONS (BR-AC-DL). Flag of the same name.

underwriting/ucc/{list,create,edit/:id,transUnion/:id}

UccMainComponent and children. Standard deny.

Four routes share UnderwritingReportTabComponent, differentiated only by data.reportId. That is the "one component, many routes" pattern — see routing.

Initial underwriting

underwriting-initial/ is decomposed into the assessment areas an underwriter works through:

Card Assesses

amazon-information/, amazon-api/

Amazon account identity and whether API access is healthy.

amazon-revenue/

Revenue history from the marketplace.

amazon-advertising/

Advertising spend as a signal.

amazon-lending/

Existing Amazon Lending loans — competing debt.

account-health/

Marketplace account-health metrics (see Amazon health rules).

credit-check/

Business credit.

personal-background-check/

Owner background checks (see background reports).

international-supplier/

Extra checks for non-US suppliers (incorporatedInTheUS === false).

agreement-review/

Contract/agreement review step.

underwriting-files/

Documents attached to the underwriting.

initial-underwriting-history/

Prior underwriting decisions.

review-final-offer-dialog/

The final confirm-before-offer dialog.

underwriting-initial-header/, -footer/

Status summary and the decision actions.

common/services/underwriting-initial.service.ts

Shared loading/persistence for the above.

Underwriting status vocabulary

suppliers/enums/uw-status.ts:

export enum UwStatus {
  VERIFIED          = 'verified',
  INCOMPLETE        = 'incomplete',
  PENDING           = 'pending',
  REJECTED          = 'rejected',
  SALES_RESOLUTION  = 'sales resolution',
}

SALES_RESOLUTION is the "blocked pending a sales conversation" state — not a credit decision. uwStatus is also a field on the advance calculator state, which is how an underwriting outcome reaches offer pricing.

The tools gate

The underwriting store slice holds only { toolsLoaded, tools: UwToolEntity[] }, loaded by UnderwritingFetchTools → …Success / …Failed (UNDERWRITING_ACTION — note this slice uses '[UNDERWRITING] …'-prefixed action strings, unlike the older slices).

The UNDERWRITING_CHECK_TOOLS_BEFORE_APPROVING feature flag makes the presence of these tool results a precondition for approval: suppliers/supplier/underwriting-tools/ renders them. With the flag off, approval is not blocked on tool checks.

Daily underwriting

underwriting-daily/ re-evaluates active suppliers. Structure:

  • underwrating-daily-table/ (sic) — the grid.

  • underwriting-daily-edit/ — editing a day’s assessment.

  • uw-daily-data-source.ts — the grid data source.

  • consts/underwriting-daily-columns.ts — the column definition.

Amazon health rules

Two endpoints on internalApiUrl:

GET suppliers/:key/amazon_health_rules      current rules and evaluations
PUT suppliers/:key/amazon_health_rules      update the rule configuration
GET suppliers/:key/amazon_health            the health snapshot (SupplierService)

The dedicated tab is /suppliers/:key/health (suppliers/supplier/supplier-health/), gated by the SUPPLIER_ACCOUNT_HEALTH feature flag. Account health also appears as a card inside initial underwriting.

Background reports

suppliers/supplier/supplier-underwriting/background-reports/ with suppliers/services/background-reports/background-reports.service.ts and the backgroundReports store slice (suppliers/store/background-reports/).

Note that the effects file for this slice is empty — the reducer is registered but loading happens through the service directly.

Report types are enumerated in supplier-underwriting/consts/all-background-report-types.ts and background-report-type.ts; content sources in content-source.ts / content-sources.ts. Access requires FINANCE.

External report tabs

Four underwriting tabs are thin wrappers over the internal API’s external report endpoints, identified by ExternalReports codes (shared/external-report/external-reports.enum.ts):

Enum Code Content

PLAID

PLAID-D

Bank transactions pulled via Plaid.

BANK_CATEGORIES

PL-BT

Categorised bank transactions.

ACCESS_ONBOARDING_PREDICTIONS

BR-AC-ON

ML predictions computed at onboarding.

ACCESS_DAILY_PREDICTIONS

BR-AC-DL

ML predictions recomputed daily.

Fetching goes through reports/service/external-reports.service.ts (reports/external_list, reports/external/:keys) and shared/external-report/. The ML prediction tabs are each behind a feature flag of the same name, so an environment can expose bank data without exposing model output.

Real-time validation

supplier-underwriting/real-time-validation/ provides input-level validation against the automatic underwriting tool (automaticMarketplaceApiUrl) while the underwriter types:

File Role

real-time-validator.service.ts (+ mock)

Calls the UW tool and returns validation verdicts.

real-time-input-factory.service.ts (+ mock)

Builds the validated input controls dynamically.

real-time-validator.interfaces.ts

The verdict contract.

base-underwriting-component/

Base class wiring a component into the validation pipeline.

uw-personal-checks-card/ + consts/personal-checks-column-names.ts

The personal-checks card that uses it.

Both the validator and the input factory have mock implementations and specs — this is one of the better-tested areas of the module.

UCC filings

A UCC filing records Payability’s lien against the supplier’s assets. Everything on internalApiUrl under suppliers/:key/ucc/:

Endpoint Use

GET ucc/filing

List filings (with query params).

GET ucc/filing/:uccKey

One filing.

POST ucc/filing

Create.

PUT ucc/filing/:uccKey

Update.

DELETE ucc/filing/:uccKey

Delete (with a body).

GET ucc/data, GET ucc/data/:uccKey

Supporting data for a filing.

Screens: ucc-list/, ucc-create/ (used for both create and edit/:id), ucc-trans-union/ at transUnion/:id — the TransUnion lookup used to populate or verify filing details. There is also a sibling trans-union/ folder at the underwriting root.

Model: ucc-main/ucc.model.ts.

Marqeta KYC in underwriting

supplier-underwriting.service.ts also owns the Marqeta compliance calls (suppliers/:key/marqeta_kyc_compliances and …/owners, GET and PUT). Marqeta requires its own KYC pass on both the business and each owner before a card can be issued — independent of Payability’s own KYC. See cards.

Compliance snapshots

compliance_snapshot/list and compliance_snapshot/:itemKey give point-in-time views of the compliance picture — used to show what was known at the time a decision was made, rather than the current state. Useful for audit questions.

The beta rewrite

supplier-underwriting-beta/ at /suppliers/:key/new-underwriting is a flag-gated reimplementation. Both are live. Before changing underwriting behaviour, determine which surface the change belongs to — and whether it needs to be made twice.

Gotchas

Issue Detail

Inverted access on the parent route

underwriting allows REST_UW_ACCESS (it only denies ADVANCE_AUDITORS), while most children deny both. A restricted-UW user can reach the parent and then be bounced from daily and ucc.

FINANCE is an allow-list

background-reports uses only: FINANCE, so every non-finance user is blocked, including super users unless they also hold FINANCE.

Empty effects file

background-reports.effects.ts is an empty file yet the slice is registered. Do not look for effects that are not there.

Folder typos

underwrating-daily-table and underwriting-initial sibling naming are inconsistent. Match existing spellings when importing.

Two automatic-UW hosts

supplier-underwriting.service.ts mixes internalApiUrl and automaticMarketplaceApiUrl in one class. Check which base URL a method uses before adding to it.

Flags stack

documentation, bank-transactions-categories, access-onboarding-predictions and access-daily-predictions are each independently flagged, so the visible tab set differs per environment. See the flag catalogue.