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

# Collected payments

> Record payments your organization collected outside Monterey, and reverse the ones you recorded.

Use this flow when your organization takes a payment itself (cash at the counter, a check, a money order, or a card run on your own terminal) and you need it applied to the borrower's account. Monterey does not move any money. You tell us what you collected, and we post it to the account.

> [!NOTE]
>
> These endpoints record payments you already collected. To charge a borrower's stored payment method, use the payment-method endpoints instead.

## Before you call

* An API key that can mint an access token
* The `external_payments:write` scope to record and reverse payments
* The `external_payments:read` scope to read them back
* `curl` and `jq`

## Choose the payment method

| You collected... | Send `payment_method` | Also send |
| - | - | - |
| Cash | `cash` | — |
| A personal check | `check` | `check_number` |
| A money order | `money_order` | `check_number` (the money order's serial number) |
| A cashier's check | `cashiers_check` | `check_number` |
| A card payment on your own terminal | `card` | `card_brand`: `visa`, `mastercard`, `amex`, `discover`, or `diners` |

Send `check_number` only for the check-type methods, and `card_brand` only for `card`. Any other combination returns `422`.

## Record a payment

Every request needs an `Idempotency-Key`. Use a new key for each payment you record, and reuse the same key only when you retry that same request.

```bash theme={null}
set -euo pipefail

ACCESS_TOKEN=$(curl --fail-with-body -sS -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "mfs_AbCd1234",
    "client_secret": "mk_your_key_here"
  }' \
  https://api.montereyfinancial.app/v1/oauth/token | jq -er .access_token)

curl --fail-with-body -sS -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: 5d0c6a8e-3f4b-4a1e-9b6c-2f8e1a7d4c90" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_cents": 2500,
    "payment_method": "check",
    "check_number": 1042,
    "external_reference": "POS-88123"
  }' \
  https://api.montereyfinancial.app/v1/accounts/01J.../payments
```

The endpoint reference lists every request field and its limits. Two behaviors it can't show:

* Send an `external_reference` (your receipt or POS number) on every payment. It lets you find the payment again and blocks an accidental duplicate. See **Use `external_reference` as a second guard** below.
* Payments always post as of the servicing system's current business day, so the request has no effective date field. The `effective_date` in the response tells you which day was used.

A successful response looks like this:

```json theme={null}
{
  "payment_id": "01K...",
  "account_id": "01J...",
  "amount_cents": 2500,
  "payment_method": "check",
  "check_number": 1042,
  "status": "posted",
  "effective_date": "2026-10-01",
  "external_reference": "POS-88123",
  "memo": null,
  "reference_number": 875839,
  "posted_at": "2026-10-01T21:08:42Z",
  "reversal": null
}
```

## Read the result

| Status | HTTP | What it means | What to do |
| - | - | - | - |
| `posted` | `201` | The payment is on the account. | Store `payment_id` and `reference_number`. |
| `pending_confirmation` | `202` | We sent the payment but couldn't confirm the outcome. | Poll `GET /v1/payments/{payment_id}` until the status changes, usually within a few minutes. Don't send a new request. |
| — | `422` `omega_rejected` | The servicing system refused the payment, and nothing was posted. | Read `reason`, then correct the request and retry with a new key. |
| — | `422` `account_not_payable` | The account can't take payments right now. | Contact Monterey. |
| — | `422` `amount_exceeds_payoff` | `amount_cents` is more than the account's payoff amount. | Correct the amount and retry with a new key. |
| — | `422` (validation) | The body is invalid, for example a missing `check_number` or `card_brand`. | Fix the body and retry with a new key. |
| — | `404` `account_not_found` | The account isn't visible to your API key. | Check the account id and your key's organizations. |
| — | `409` | See **Retry safely** below. | — |
| — | `503` `omega_unavailable` | Payment posting is temporarily unavailable, and nothing was posted. | Retry later with the same key. |

A key without `external_payments:write` gets `403` `insufficient_scope` on every write.

## Retry safely

Retrying with the same `Idempotency-Key` and the same body returns the original response. It never records the payment a second time, even after the original succeeded.

* The same key with a **different** body returns `409` `idempotency_conflict`.
* A retry while we still can't confirm the outcome of the original returns `409` `idempotency_in_progress`. Keep polling `GET /v1/payments/{payment_id}`, or contact Monterey. Don't switch to a new key: a new key is a new payment.

### Use `external_reference` as a second guard

Each `external_reference` identifies at most one payment per organization. If you send a reference that one of your payments already holds, Monterey returns `409` `duplicate_external_reference` with that payment's `payment_id` and records nothing:

```json theme={null}
{"detail": {"error_code": "duplicate_external_reference", "payment_id": "01K..."}}
```

This protects you even if a retry goes out under a new `Idempotency-Key`. A payment that definitely failed (`422` `omega_rejected`, or `503`) doesn't hold its reference, so you can correct the request and retry under the same reference.

## Reverse a payment

You can reverse only payments your organization recorded through this API. Payments from any other source return `404`.

```bash theme={null}
curl --fail-with-body -sS -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: 9a1f3c2e-7b5d-4e8a-a0c4-6d2b9e1f8a37" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Check returned by bank", "is_nsf": true}' \
  https://api.montereyfinancial.app/v1/payments/01K.../reversal
```

Set `is_nsf` to `true` when the reversal is for non-sufficient funds.

| Status | HTTP | What it means |
| - | - | - |
| `reversed` | `201` | The reversal is applied. `reversal.reference_number` identifies it. |
| `reversal_pending` | `202` | We sent the reversal but couldn't confirm the outcome. Poll the payment. |
| — | `409` `payment_not_reversible` | The payment isn't in the `posted` state. `status` in the error body tells you its current state. |
| — | `404` `payment_not_found` | The payment wasn't recorded by your organization through this API. |
| — | `422` `omega_rejected` | The servicing system refused the reversal. The payment is still `posted`. |
| — | `503` `omega_unavailable` | Reversal is temporarily unavailable, and the payment is still `posted`. Retry later with the same key. |

`reason` is required, up to 200 characters. Reversal requests follow the same `Idempotency-Key` rules as posting.

## Find a payment by your reference

If you lost the `payment_id` (for example, your system stopped before saving our response), look the payment up by the `external_reference` you sent:

```bash theme={null}
curl --fail-with-body -sS -G \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  --data-urlencode "external_reference=POS-88123" \
  https://api.montereyfinancial.app/v1/payments
```

The response is a page of your organization's payments with that reference, newest first, paginated with `cursor` and `limit`. At most one of them holds the reference: the payment that posted, is pending, or was reversed. Any others are earlier attempts that definitely failed. To reverse the payment, take its `payment_id` and call the reversal endpoint.

Reading payments needs the `external_payments:read` scope. A tampered or expired `cursor` returns `400` `invalid_cursor`.

## List your recorded payments

`GET /v1/accounts/{account_id}/payments` returns only the payments your organization recorded through this API, newest first, paginated with `cursor` and `limit`. To see every transaction on the account, including payments from other sources, use the transactions endpoints.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.