> ## Documentation Index
> Fetch the complete documentation index at: https://docs.finosu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Enroll an Application

> Create or reuse a customer and enroll an application in its configured outreach cadence.

Enroll an application in an automated origination call cadence by sending
`scheduleMode: "enroll"` to the existing schedule-sync endpoint. Send one
application per request here. For a whole list, use [batch enrollment](/api-reference/origination/batch). The company associated with your `X-API-Key` determines
which task types and cadences are available; contact your account manager to
confirm the supported modes.

Omitting `scheduleMode`, or setting it to `"replace"`, retains the existing
[Customer Schedule Sync](/api-reference/endpoint/customers/schedule-sync) behavior.
Enrollment does not cancel other calls, emails, or SMS. Conflicting applications,
paused schedules, or other active call schedules return `409` for review.

This guide covers enrollment only. For the shared request/response schema and API
playground, see [Customer Schedule Sync](/api-reference/endpoint/customers/schedule-sync).
For complete requests for each mode, see [Origination Examples](/api-reference/origination/examples).

## Supported task types

Origination enrollment (`scheduleMode: "enroll"`) supports exactly two
`taskType` values. Use the case-sensitive values shown below.

Use [List Task Types](/api-reference/endpoint/task-types/list) (`GET /origination/task-types`)
to retrieve these values programmatically.

| `taskType` | Purpose |
| - | - |
| `PendingLoanNewLead` | New application outreach: enrolls a new application in an automated outreach cadence. |
| `PendingLoanReturning` | Returning borrower outreach: enrolls a returning borrower in the configured outreach cadence. |

The selected task type must be enabled for the company associated with your
`X-API-Key`. A supported task type that is not enabled for your company returns
`400`. Contact your account manager to confirm availability.

For a single returning-borrower call, use `PendingLoanReturning` with
`oneOff: true`; this is an option, not a separate task type. See
[Cadences](#cadences) for scheduling behavior.

## Example

```bash theme={null}
curl --request POST 'https://api.finosu.com/customers/schedule/sync' \
  --header 'X-API-Key: YOUR_API_KEY' \
  --header 'Idempotency-Key: application-1001-enrollment-1' \
  --header 'Content-Type: application/json' \
  --data '{
    "scheduleMode": "enroll",
    "id": "customer-1001",
    "loanId": "application-1001",
    "taskType": "PendingLoanNewLead",
    "firstName": "Jane",
    "lastName": "Doe",
    "birthday": "1985-06-15",
    "ssnLastFour": "1234",
    "phoneNumber": "+15125550100",
    "mobilePhone": "+15125550101",
    "zipCode": "78701",
    "timezone": "America/Chicago",
    "daysInApplied": 3
  }'
```

Successful enrollment saves the customer, application, and call schedule in one
transaction. The normal scheduler starts outreach when calling hours, business-day
rules, contact restrictions, and capacity allow. A successful API response means
the schedule is saved; it does not mean a call has connected.

## Fields

Use strings for customer references, application references, ZIP codes, and SSN
suffixes to preserve leading zeroes. Send birthdays as `YYYY-MM-DD`. Only the last
four SSN digits are needed; do not send a full SSN.

| API field | Spreadsheet column | Enrollment requirement |
| - | - | - |
| `scheduleMode` | — | Required: `"enroll"` |
| `taskType` | Selected upload/cadence type | Required: `PendingLoanNewLead` or `PendingLoanReturning`, when enabled for your company |
| `id` | `ReferenceNumber` | Required customer reference |
| `loanId` | `ref_loan_id` | Required application reference |
| `firstName` | `FirstName` | Required |
| `lastName` | `LastName` | Required |
| `birthday` | `DOB` | Required date in the past; convert spreadsheet dates to `YYYY-MM-DD` |
| `ssnLastFour` | `SSN` | Required; exactly four digits |
| `phoneNumber` | `HomePhone` | At least one of `phoneNumber` and `mobilePhone` is required |
| `mobilePhone` | `MobilePhone` | Optional second number, or the only number when `phoneNumber` is absent |
| `middleName` | `MiddleName` | Optional |
| `streetAddress1` | `Address` | Optional |
| `streetAddress2` | — | Optional |
| `city` | `City` | Optional |
| `state` | `State` | Optional two-letter uppercase code |
| `zipCode` | `ZIPCode` | Optional; used to derive the timezone when `timezone` is omitted |
| `timezone` | — | Required unless `zipCode` provides a known timezone; valid IANA timezone name only |
| `email` | `Email` | Optional valid email |
| `daysInApplied` | `days_in_applied` | Optional integer from 0 to 36,500; new leads only |
| `daysSincePaidOff` | `DaysSincePaidOff` | Optional integer from 0 to 36,500; returning borrowers only |
| `oneOff` | Selected one-off upload type | Optional boolean; returning borrowers only; default `false` |
| `isTest` | — | Optional boolean; default `false`; must match an existing customer's test setting |
| `test` | — | **Deprecated**, removed on 25 October 2026 — send `isTest`. Still accepted and still honored |

Omit optional values you do not have. Optional contact fields and the two
cadence-age fields accept `null`; `oneOff`, `test`, `isTest`, and `sync` do not. Send the
age fields as JSON integers, not strings, fractions, or booleans. Unrecognized
fields are rejected rather than ignored.

`test` and `isTest` mean the same thing and either is honored until the removal date. Sending both
with different values is rejected with `422` and code `conflicting_test_flags`; nothing is enrolled.
Because this endpoint rejects unrecognized fields outright, there is no risk of a payload quietly
enrolling as production after the removal date — it will fail loudly. See [the sync and enrollment
window](/api-reference/upcoming-changes#the-sync-and-enrollment-window).

Both supplied phone numbers must have 10 digits, or 11 digits beginning with
`1`; the API normalizes them to `+1…`. This validates format, not whether a number
is assigned or reachable. An invalid mobile number produces an error instead of
silently changing the call plan. `sync: true` and nonempty `externalMetadata` are
not supported in enrollment mode; send the explicit fields above.

Enrollment preserves existing contact restrictions. It cannot remove a Do Not
Call restriction through `doNotCall: false`; `doNotCall: true` is also rejected.

## Cadences

| Request | Result |
| - | - |
| `taskType: "PendingLoanNewLead"` | Nine calls over five business days, with phone rotation when two distinct numbers are provided. `daysInApplied` is recorded as context; it does not skip cadence steps. |
| `taskType: "PendingLoanReturning"` with `daysSincePaidOff` | Returning-borrower cadence based on days since payoff, with future calls through the configured horizon. |
| `taskType: "PendingLoanReturning"` without `daysSincePaidOff` | One initial call using the regular returning-borrower flow, which may include a mobile fallback. |
| `taskType: "PendingLoanReturning", oneOff: true` | Exactly one scheduled call, even with `daysSincePaidOff`; no mobile fallback. |

A matching active cadence with a pending or in-progress call is returned as
`already_scheduled` without changing borrower data or resetting its progress.
The application reference, customer, company, and outreach mode must match;
switching between returning and one-off is not an update to an existing cadence.
A finished, cancelled, or paused application cadence cannot be restarted by
changing the idempotency key. Coordinate an intentional restart with your
account manager; this enrollment operation does not provide it.

Use [Update Customer](/api-reference/endpoint/customers/update) for supported
profile changes such as the primary `phoneNumber`, email, or timezone. That
operation does not update `mobilePhone`, cadence-age fields, or `oneOff`, and it
does not rebuild the call plan. Coordinate changes to those fields separately.

## Retries

`Idempotency-Key` is required only for `scheduleMode: "enroll"`. Use a stable,
non-sensitive identifier of 1–128 characters: letters, digits, `.`, `_`, `:`, or
`-`. Keep the same key and payload when retrying after a timeout or network error.

* The same company, key, and normalized payload return the saved enrollment
  receipt, even after the cadence finishes or is cancelled.
* Reusing a key with a different payload returns `409`.
* A different key for the same active application returns `already_scheduled`.
* An in-progress request returns `409` with `Retry-After`; retry the original key.
* Keys are scoped to the company associated with the API key.

Receipt replays still require a valid request, an enabled company/mode, and an
available quota. They return the original scheduling result; they do not
re-evaluate the current customer's eligibility or start another cadence.

Enrollment is limited to 60 requests per minute per company. Honor `Retry-After`
on `429` and `503` responses and retry with exponential backoff and jitter.

## Response

Responses use HTTP `200`. The top-level customer fields are built from the
normalized request, with two exceptions: `phoneNumber` falls back to `mobilePhone`
when no primary number is supplied, and `doNotCall` is always returned as `false`.
These fields are **not a read of the current customer record**. In particular,
`already_scheduled` does not persist submitted contact changes, and `doNotCall`
does not prove the customer is currently callable, including on a replay. Use
[Get Customer](/api-reference/endpoint/customers/get) for the customer fields that
operation exposes, and scheduled-call status for current scheduling state.

Enrollment also returns `mobilePhone`, `middleName`, the schedule counters, and
an `origination` receipt. Optional response fields can be `null`. The following
is the **scheduling portion** of the response, not the complete customer response:

```json theme={null}
{
  "scheduleCreated": true,
  "tasksCreated": 9,
  "existingTasksRemoved": 0,
  "origination": {
    "id": "24ed9f67-d86f-450b-8d9a-0d67dbd89187",
    "status": "scheduled",
    "customerId": "customer-1001",
    "loanId": "application-1001",
    "scheduleId": "d514bcc6-b635-485e-a48a-695aec492c59",
    "scheduledCallId": "a584fa4a-c99d-4a84-ae43-39b119b3f152",
    "scheduledTime": "2026-09-21T16:00:00Z",
    "scheduledCallCount": 9
  }
}
```

`Idempotency-Replayed: true` identifies a replay. Its counters describe the
original enrollment, not newly created calls. A newly submitted key returning
`already_scheduled` has `scheduleCreated: false` and `tasksCreated: 0`.
`scheduledCallCount` is the total number of nondeleted calls in the saved cadence,
not the number of remaining attempts.

`Location` points to `/scheduled-calls/{scheduledCallId}`. Use the [scheduled
call detail endpoint](/api-reference/endpoint/scheduled-calls/get-by-id) to
track current status; the enrollment receipt is a snapshot.

## Errors

Enrollment validation, conflict, and quota errors have a structured `detail`:

```json theme={null}
{
  "detail": {
    "code": "origination_conflict",
    "message": "Idempotency-Key was already used with a different request"
  }
}
```

Use `detail.code` to distinguish `request_in_progress` from other `409` conflicts.
Authentication errors may instead contain a plain-string `detail`; do not assume
every error has the enrollment shape. Invalid fields return sanitized messages
without echoing the submitted identity values.

| HTTP status | Meaning |
| - | - |
| `200` | Saved enrollment or existing cadence/receipt |
| `400` | Task type is not enabled for your company |
| `401` | Missing, empty, invalid, or inactive API key |
| `409` | Conflicting application, contact restriction, finished/paused cadence, reused key with changed data, or request still in progress |
| `422` | Missing/invalid input or missing/invalid idempotency key |
| `429` | Per-company request quota exceeded |
| `503` | Scheduling temporarily unavailable; retry the same key and payload |
