> ## 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.

# Upcoming API Changes

> Announced changes to the Create Customer contract, with migration guidance

## Summary

We are tightening the required-field contract on **`POST /customers`** and **`POST /customers/batch`**
so that every customer we board arrives with the data needed to contact them lawfully and verify their
identity. Two redundant fields are being removed at the same time.

Nothing on this page is enforced yet. Requests that are valid today remain valid until further notice.
We will give partners ample time to adjust before introducing any breaking changes — you will be
notified well in advance so you can migrate on your own schedule rather than in response to a failure.

Endpoints affected:

* `POST /customers`
* `POST /customers/batch` (same per-customer payload)

Endpoints **not** affected: `POST /customers/schedule/sync`, `PUT /customers/{id}`, and every loan,
call, text, and payment endpoint.

## What is changing

| Field                   | Enforced today                                                      | After the change                       |
| ----------------------- | ------------------------------------------------------------------- | -------------------------------------- |
| `firstName`             | Required                                                            | Required — no change                   |
| `lastName`              | Required                                                            | Required — no change                   |
| `email` / `phoneNumber` | `phoneNumber` always required; `email` required unless `sync: true` | **At least one of the two required**   |
| `birthday`              | Not validated                                                       | **Required**                           |
| `ssnLastFour`           | Not validated                                                       | **Required**                           |
| `timezone` / `zipCode`  | `timezone` always required; `zipCode` required unless `sync: true`  | **At least one of the two required**   |
| `state`                 | Required unless `sync: true`                                        | **Required in all cases**              |
| `test`                  | Accepted; marks the customer as a test record                       | **Removed** — send `isTest` instead    |
| `loanId`                | Accepted, but has no effect on this endpoint                        | **Removed** — send the ID in `loanIds` |

`id`, `taskType`, `streetAddress1`, and `city` are unchanged. `streetAddress2`, `doNotCall`,
`externalMetadata`, and `loanIds` remain optional.

## Why each change

**At least one of `email` or `phoneNumber`.** Every customer needs at least one channel we can reach
them on — a record with neither cannot be worked. Today `phoneNumber` is unconditionally required,
which turns away email-only accounts. Relaxing that while requiring one of the pair is both stricter
in effect and more flexible in practice.

**At least one of `timezone` or `zipCode`.** We may only place calls during hours permitted by the
borrower's local time, so TCPA compliance depends on knowing where they are. Supplying either is
sufficient: when `timezone` is omitted we derive it from `zipCode`. If the ZIP cannot be resolved to a
timezone, the request is rejected with `400` rather than silently defaulting — a wrong timezone means
calls placed outside legal hours.

**`state` always required.** State determines which lending and collections rules apply to the
account, and several states are restricted per-portfolio. We need it regardless of how the rest of the
record was populated.

**`birthday` and `ssnLastFour` required.** Both are used to verify a borrower's identity before
discussing account details on a call. Without them, an agent cannot complete verification and the
conversation cannot proceed.

**`test` → `isTest`.** The API already returns `isTest` on every customer response, but accepts `test`
on the way in. Aligning the write field to the read field removes a needless asymmetry.

**`loanId` removed.** On `POST /customers` this field is accepted and then ignored — it has never had
any effect. `loanIds` is the field that actually controls which loans are imported, so a single loan
ID belongs there as a one-element array.

## Migrating your payload

A request that is valid today:

```json theme={null}
{
  "id": "12345",
  "taskType": "Collections",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "phoneNumber": "+14155555555",
  "timezone": "America/Los_Angeles",
  "streetAddress1": "123 Main St",
  "city": "San Francisco",
  "state": "CA",
  "zipCode": "94102",
  "test": false,
  "loanId": "67890"
}
```

The same request under the new contract — `birthday` and `ssnLastFour` added, `test` renamed to
`isTest`, `loanId` moved into `loanIds`:

```json theme={null}
{
  "id": "12345",
  "taskType": "Collections",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "phoneNumber": "+14155555555",
  "birthday": "1985-06-15",
  "ssnLastFour": "1234",
  "timezone": "America/Los_Angeles",
  "streetAddress1": "123 Main St",
  "city": "San Francisco",
  "state": "CA",
  "zipCode": "94102",
  "isTest": false,
  "loanIds": [67890]
}
```

Note that `loanIds` is an array of **integers**, not strings.

Because `email`/`phoneNumber` and `timezone`/`zipCode` are at-least-one pairs, a minimal payload only
needs one of each:

```json theme={null}
{
  "id": "12345",
  "taskType": "Collections",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "birthday": "1985-06-15",
  "ssnLastFour": "1234",
  "streetAddress1": "123 Main St",
  "city": "San Francisco",
  "state": "CA",
  "zipCode": "94102"
}
```

## What you can do now vs. what to wait for

Read this section carefully if you plan to migrate early — some of the new fields are not yet wired up.

**Safe to do now:**

* Start sending `birthday`, `ssnLastFour`, and `state` on every request. All three are already accepted
  and stored today; they are simply not yet enforced.
* Move a single `loanId` value into `loanIds`. `loanIds` is already honored; `loanId` is already
  ignored on this endpoint, so there is no behavior to lose.

**Do not do yet:**

* **Do not switch `test` to `isTest` until we confirm the change is live.** `isTest` is not accepted on
  the request today and unknown fields are ignored, so a payload sending `isTest: true` would silently
  create a **non-test** customer. Keep sending `test` until you are notified.

**Only after the change takes effect:**

* Dropping `phoneNumber` (relying on `email` alone) or dropping `timezone` (relying on `zipCode`
  alone). Both are unconditionally required today and omitting either returns a `400` until the new
  contract takes effect.

## Error behavior

Missing required fields return `400 Bad Request` with the offending fields named, in the same shape
used today:

```json theme={null}
{
  "detail": "Missing required fields: birthday, ssnLastFour"
}
```

For the at-least-one pairs, the message names the pair rather than a single field. A `zipCode` that
cannot be resolved to a timezone also returns `400`.

## Questions

If any of these fields are not available in your system, or you need more time to adjust, contact your
Finosu account manager so we can plan around it.
