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

# Send an ACH Payment with Cleo Pay

> Learn how to send ACH payments via the Cleo Pay API — pay an existing invoice or send directly to a contact with one API call.

The Cleo Pay API gives you two ways to send an ACH payment: you can pay against an existing payable (invoice), or send funds directly to a contact without referencing an invoice. Both paths use the same `POST /v1/payments` endpoint — the only difference is which identifier you supply.

<Warning>
  `payableId` and `contactId` are **mutually exclusive**. Include one or the other in a single request — never both. Sending both fields returns a `400 Bad Request`.
</Warning>

***

## Path A — Pay an existing payable (invoice)

Use this path when you have an outstanding invoice or payable tracked in Cleo Pay and want to apply a payment against it.

<Steps>
  <Step title="Get your payable's ID">
    Retrieve the payable you want to pay. You can find its ID either from the response when the payable was created (`POST /v1/payables`) or by listing your payables (`GET /v1/payables`).

    The `id` field on the payable object is the value you'll pass as `payableId`.
  </Step>

  <Step title="Submit the payment">
    Call `POST /v1/payments` with the `payableId` and the `bankAccountId` of the account you want to fund the payment from.

    ```bash title="Pay a payable in full" theme={null}
    curl --request POST \
      --url https://api.cleo-pay.com/v1/payments \
      --header 'Authorization: Bearer <token>' \
      --header 'Content-Type: application/json' \
      --data '{
        "payableId": "payable_01hwxyz123",
        "bankAccountId": "ba_01hwxyz456"
      }'
    ```
  </Step>

  <Step title="Make a partial payment (optional)">
    By default, the payment amount equals the **remaining balance** on the payable. To pay only part of it, include `amountCents` in your request body. The minimum value is `1` (one cent).

    ```bash title="Partial payment of $250.00" theme={null}
    curl --request POST \
      --url https://api.cleo-pay.com/v1/payments \
      --header 'Authorization: Bearer <token>' \
      --header 'Content-Type: application/json' \
      --data '{
        "payableId": "payable_01hwxyz123",
        "bankAccountId": "ba_01hwxyz456",
        "amountCents": 25000
      }'
    ```
  </Step>

  <Step title="Check the returned payment status">
    The response includes the `payableId` and a `payments` array. Each payment object has a `status` field. Immediately after creation the status will be `pending` (or `scheduled` if you supplied `scheduledAt`).

    ```json title="Payment response" theme={null}
    {
      "payableId": "payable_01hwxyz123",
      "payments": [
        {
          "id": "pmt_01hwxyz789",
          "status": "pending",
          "amountCents": 25000,
          "bankAccountId": "ba_01hwxyz456",
          "createdAt": "2024-06-01T14:23:00Z"
        }
      ]
    }
    ```
  </Step>
</Steps>

***

## Path B — Send directly to a contact

Use this path when you want to push funds to a vendor or payee without attaching the payment to a specific invoice.

<Steps>
  <Step title="Confirm the contact is payment-ready">
    Before sending, ensure the contact's `paymentReady` flag is `true`. A contact becomes payment-ready once it has a verified bank account on file.

    If `paymentReady` is `false`, the payment will be created with a **`held`** status and will not process until the contact adds a bank account.

    <Note>
      A payment in `held` status means the contact has no bank account yet. The payment will release automatically once the contact links and verifies their account.
    </Note>
  </Step>

  <Step title="Submit the payment">
    Call `POST /v1/payments` with `contactId`, `amountCents`, and `bankAccountId`. You can optionally add a human-readable `description` and your own reference number via `customNumber` (maximum 21 characters).

    ```bash title="Send $500.00 directly to a contact" theme={null}
    curl --request POST \
      --url https://api.cleo-pay.com/v1/payments \
      --header 'Authorization: Bearer <token>' \
      --header 'Content-Type: application/json' \
      --data '{
        "contactId": "contact_01hwxyz111",
        "amountCents": 50000,
        "bankAccountId": "ba_01hwxyz456",
        "description": "Consulting services — May 2024",
        "customNumber": "INV-2024-0045"
      }'
    ```
  </Step>

  <Step title="Check the returned payment status">
    The response mirrors Path A — a `payments` array with the new payment object. When no `payableId` is involved, the `payableId` field in the response will be `null`.

    ```json title="Direct contact payment response" theme={null}
    {
      "payableId": null,
      "payments": [
        {
          "id": "pmt_01hwxyz999",
          "status": "pending",
          "amountCents": 50000,
          "bankAccountId": "ba_01hwxyz456",
          "description": "Consulting services — May 2024",
          "customNumber": "INV-2024-0045",
          "createdAt": "2024-06-01T14:30:00Z"
        }
      ]
    }
    ```
  </Step>
</Steps>

***

## Payment status flow

After you create a payment, it moves through the following statuses:

| Status       | Meaning                                                                             |
| ------------ | ----------------------------------------------------------------------------------- |
| `pending`    | Payment has been created and is queued for processing.                              |
| `scheduled`  | Payment is set for a future date and has not yet been submitted to the ACH network. |
| `processing` | Payment has been submitted to the ACH network.                                      |
| `held`       | Payment is waiting for the contact to link a bank account.                          |
| `completed`  | Funds have been successfully delivered.                                             |
| `failed`     | The payment was rejected or returned. Check the failure reason.                     |
| `cancelled`  | The payment was cancelled before processing.                                        |
| `refunded`   | A completed payment was reversed.                                                   |

The typical happy-path progression is:

```
pending → processing → completed
```

For scheduled payments the flow starts with `scheduled` before entering `pending` on the execution date:

```
scheduled → pending → processing → completed
```
