---
title: "List loan officers"
method: GET
path: "/loan-officers"
tags: ["Loan officers"]
---

# List loan officers

`GET /loan-officers`

## Request

- Base URL: `https://app.payna.com/api/v1`
- URL: `https://app.payna.com/api/v1/loan-officers`
- Auth: HTTP bearer

Returns a list of your loan officers, sorted by name. Each loan officer includes their licenses, sponsorships, onboarding status, CE hours and courses. See the **LoanOfficer** schema for every field.

Results are paginated, up to 200 per page (default 100). Use `meta.total` and the `page` parameter to fetch the rest.

## Query parameters

- `can_originate_in` string: Only officers who can originate in this state today.
- `state` string
- `include_departed` boolean
- `page` integer
- `limit` integer

## Response `200`

A page of loan officers.

- object
  - `data` LoanOfficer[]
    - `id` string, uuid: Payna's id for them.
    - `nmls_id` string, nullable: Their NMLS ID.
    - `name` string
    - `first_name` string, nullable
    - `last_name` string, nullable
    - `email` string, nullable: Their work email.
    - `title` string, nullable: Their job title, if your company set one.
    - `office` string, nullable: The office NMLS lists them at. "(Main Office)" when it is your main office.
    - `employment_status` 'active' | 'inactive' | 'terminated' | 'pending', nullable: `terminated` means they left. Those are only listed with `include_departed=true`.
    - `can_originate_in` string[]: States where they can originate for you today: the license is approved at NMLS and your company's sponsorship is accepted. Temporary Authority is not counted. Same rule as each license's `can_originate`, including its one exception.
    - `onboarding` LoanOfficerOnboarding: Where they are in joining your company. The same status your team sees on the People page in Payna.
      - `status` 'sponsored' | 'sponsoring' | 'awaiting_access' | 'missing_info' | 'not_started' | 'deficient', nullable: Payna's onboarding status, not an NMLS status. NMLS's own words are on each license: `nmls_status` and `sponsorship`. - `sponsored`: done. Every license your company set out to sponsor is accepted. - `sponsoring`: sponsorship is filed or about to be, for some of their licenses. - `awaiting_access`: the officer has to accept your company's access request in NMLS. - `missing_info`: Payna needs details from your company to file. - `not_started`: your company sponsors none of their licenses and nothing is filed. - `deficient`: NMLS marks one of their licenses, or a sponsorship of one, deficient. - `null`: not known yet.
      - `waiting_on` 'company' | 'officer' | 'payna' | 'state', nullable: Who has to act next. `company` is you. `state` is the regulator. Null when nobody needs to, or when NMLS does not say who: a `deficient` officer needs action from them or from you.
      - `start_date` string, date, nullable: The day they start with your company, while they are onboarding.
      - `start_date_confirmed` boolean, nullable: `true` when the start date is confirmed, `false` when it is an estimate.
      - `sponsored_in` string[]: States where your sponsorship is accepted.
      - `sponsorship_pending_in` string[]: States where sponsorship is filed and waiting on the state.
      - `sponsorship_deficient_in` string[]: States where NMLS calls the sponsorship Deficient: filed, and waiting on the officer or your company. A state can also be in `sponsored_in` when another license there is accepted. The officer's `onboarding.status` is then `deficient`.
      - `sponsorship_planned_in` string[]: States Payna will file sponsorship in next.
    - `licenses` LoanOfficerLicense[]: Every license NMLS lists for them, at any company, sorted by state.
      - `state` string
      - `license_name` string, nullable
      - `license_number` string, nullable
      - `nmls_status` string, nullable: The license's status, exactly as NMLS shows it: "Approved", "Approved - Inactive", "Pending - Incomplete", "Terminated - Expired"...
      - `can_originate` boolean: Approved at NMLS and sponsored by your company. Use this for lead routing. One exception: when Payna has not yet read an officer's sponsorships from NMLS (`sponsorship` is null on all their licenses), an approved license counts, because "not read" is not "not sponsored". To require confirmed sponsorship, also check `sponsorship` is `Accepted`.
      - `temporary_authority` boolean: `true` while they work under Temporary Authority. Not counted in `can_originate`.
      - `sponsorship` 'Accepted' | 'Requested' | 'Deficient' | 'Rejected' | 'Removed' | 'Not sponsored by this company', nullable: Your company's sponsorship of this license, in NMLS's own words: `Accepted` (approved), `Requested`, `Deficient` (awaiting action from the officer or your company), `Rejected`, `Removed` (withdrawn). `Requested` also covers a request Payna filed that NMLS does not show yet. `Not sponsored by this company` when NMLS lists no sponsorship of it by you. Null when NMLS has ended the license, or is ending it, and your company did not sponsor it. Also null when Payna has not read it from NMLS, or cannot tell which of two licenses in a state it belongs to.
      - `sponsorship_nmls_status` string, nullable: That sponsorship's status, exactly as NMLS shows it: "Accepted", "Requested", "Deficient", "Rejected", "Removed". Null when NMLS shows none.
      - `sponsored_since` string, date, nullable: The day NMLS accepted your sponsorship.
      - `sponsorship_requested_on` string, date, nullable: The day sponsorship was requested, kept after it is accepted. Known for sponsorships Payna filed; null for ones filed before Payna, because NMLS does not date them.
      - `open_items` object[]: What NMLS still wants for this license, oldest first. Empty when nothing is open.
        - `title` string
        - `since` string, date, nullable
      - `renewal_due` string, date, nullable: When the license must be renewed.
      - `renewed_through` integer, nullable: The last year it is renewed for.
      - `last_synced_at` string, date-time, nullable: When Payna last read this license from NMLS.
    - `checks` LoanOfficerChecks, nullable: Their SAFE tests, criminal background checks and credit reports, as their NMLS record shows them. Null until Payna has read them. Within it, a list that is null was not read, and an empty list was read and has nothing. Newest first.
      - `safe_tests` object[], nullable: Every SAFE test NMLS lists. Results only, never scores.
        - `test` string
        - `date` string, date, nullable
        - `result` string
      - `background_checks` object[], nullable: Every criminal background check NMLS lists.
        - `status` string
        - `status_date` string, date, nullable
        - `reason_closed` string, nullable
        - `method` string, nullable
        - `fingerprints_taken` string, date, nullable
      - `fingerprints_expire` string, nullable: When their fingerprint record expires: a date, or `expired`. Null when NMLS shows none.
      - `credit_reports` string[], nullable: When each credit report was pulled.
      - `as_of` string, date: When Payna last read them from NMLS.
    - `education` object: `ce_hours` is this year's CE from NMLS. `pe_hours` is their PE from NMLS. `courses` is what they are enrolled in at your course provider (Knowledge Coop).
      - `ce_hours` CeHours, nullable: This year's CE hours from their NMLS transcript. Null when none of their licenses needs CE.
        - `year` integer
        - `hours_required` number
        - `hours_completed` number
        - `complete` boolean: `true` when nothing is left.
        - `still_needed` object[]: What is left, and by when.
          - `for` string: `Core`, `Elective`, or a state like `TX` or `CA-DFPI`.
          - `hours` number
          - `due_date` string, date, nullable
      - `pe_hours` PeHours, nullable: Their pre-licensing education as their NMLS education record shows it: the federal 20 hours, and each license's own state PE. Licenses they no longer hold are left out. Null until Payna has read their NMLS record.
        - `federal` PeFederal, nullable: The 20 federal hours, done once. `hours_completed` is every PE hour NMLS applied, state PE included, so it can be more than 20. Null when NMLS prints no row.
          - `status` string: NMLS's own word: `Compliant`, `Pending`, `Not Required`.
          - `complete` boolean: `Compliant` or `Not Required`.
          - `hours_required` number, nullable
          - `hours_completed` number, nullable
          - `status_date` string, date, nullable: The date NMLS gives that status.
        - `states` PeState[]: One row per license NMLS lists a state PE requirement for, sorted by jurisdiction.
          - `state` string
          - `jurisdiction` string, nullable: Set when NMLS splits the state: `CA-DFPI`, `TX-SML`, `UT-DRE`.
          - `license_name` string
          - `status` string: NMLS's own word: `Compliant`, `Pending`, `Not Required`.
          - `complete` boolean: `Compliant` or `Not Required`.
          - `hours_required` number, nullable
          - `hours_completed` number, nullable
          - `status_date` string, date, nullable: The date NMLS gives that status.
      - `courses` Course[]
        - `type` 'PE' | 'CE': `PE` is pre-licensing education, for a state they are getting licensed in. `CE` is continuing education.
        - `state` string, nullable: Null for the federal 7-hour core.
        - `jurisdiction` string, nullable: Set when NMLS splits the state: `CA-DFPI`, `TX-SML`, `UT-DRE`.
        - `year` integer, nullable: The CE year. Null for PE.
        - `title` string
        - `provider` string
        - `status` 'not_started' | 'in_progress' | 'completed'
        - `percent_complete` integer, nullable: 0 to 100. Null when the provider does not say.
        - `due_date` string, date, nullable: When the provider says it is due.
        - `overdue` boolean: Past `due_date` and not completed.
        - `completed_on` string, date, nullable: The day they passed it.
    - `last_synced` object: How fresh their data is.
      - `nmls` string, date-time, nullable: The oldest NMLS read among their licenses, so all of them are at least this fresh. Null when they have no license, or one was never read.
      - `education` string, date-time, nullable: When Payna last read your course provider. Null when they have no account there.
  - `meta` Meta
    - `total` integer
    - `page` integer
    - `limit` integer
    - `synced_at` string, date-time, nullable

## Other responses

- `400`: Invalid query parameter. The message names it.
- `401`: Missing or invalid API key.
- `403`: The API key lacks the `individuals` capability.
- `429`: Rate limit exceeded. Retry after the seconds in `Retry-After`.
- `500`: Error on Payna's side. Retry the request.

## Changes

- **2026-11-03** `11ef1636c05a`: 6 warning, 7 info
  - added the new `Accepted` enum value to the `data/items/licenses/items/sponsorship` response property for the response status `200`
  - added the new `Deficient` enum value to the `data/items/licenses/items/sponsorship` response property for the response status `200`
  - added the new `Not sponsored by this company` enum value to the `data/items/licenses/items/sponsorship` response property for the response status `200`
  - added the new `Rejected` enum value to the `data/items/licenses/items/sponsorship` response property for the response status `200`
  - …9 more

[Operation history](https://skmtc.dev/payna/apis/payna-compliance-api/changes/loan-officers/get.md)

---

[API](https://skmtc.dev/payna/apis/payna-compliance-api.md) - [All operations](https://skmtc.dev/payna/apis/payna-compliance-api/llms.txt) - [OpenAPI document](https://skmtc.dev/payna/apis/payna-compliance-api/revisions/11ef1636c05a?raw)
