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

# Payment schedules

> Schedule payments for a later date or on a weekly, every-two-weeks, or monthly repeat, then edit, skip, pause, or cancel them.

Use a payment schedule when a payment should go out on a later date, or on a repeat, without a request from you on the day. A schedule made through the API is the same schedule your team sees on the dashboard, and changes made in either place show in both. To pay right away, use [`POST /payments`](https://help.trykarat.com/api-reference/payments/create-a-payment-batch) instead.

| Operation | Method and path |
| - | - |
| Create a schedule | `POST /payment-schedules` |
| List schedules | `GET /payment-schedules` |
| Get a schedule | `GET /payment-schedules/{id}` |
| Update a schedule | `PATCH /payment-schedules/{id}` |
| Pause, resume, or cancel | `PUT /payment-schedules/{id}/pause`, `/resume`, `/cancel` |
| Skip or restore one date | `PUT /payment-schedules/{id}/skip`, `/restore` |

Reads need the `payments.read` API key permission; every other operation needs `payments.write`. The **Payment Schedules** group in the API reference has the full request and response schemas.

## Create a schedule

Amounts are integer cents. Dates are `YYYY-MM-DD` calendar dates in US Eastern time. A one-time schedule's `sendOn` must be after today. A repeating schedule's `sendOn` can be today or earlier: its first send moves to the repeat's next upcoming date, and the returned `sendOn` shows that date. The first send must be within 12 months.

Each recipient takes exactly one of `recipientId` or `payeeEmail`; the email must belong to an existing recipient, so add new ones with [`POST /recipients`](https://help.trykarat.com/api-reference/recipients/add-recipients) first. Listing the same recipient twice returns `400 DUPLICATE_RECIPIENT`.

**One-time:** omit `recurrence`.

```json theme={null}
{
  "recipients": [{ "payeeEmail": "grace@example.com", "amount": 120000, "description": "November retainer" }],
  "sendOn": "2026-11-30"
}
```

**Repeating:** set `recurrence.frequency` to `weekly`, `biweekly` (every two weeks), or `monthly`. Weekly and every-two-weeks schedules send on `sendOn`'s weekday; monthly schedules send on its day of the month, and on the last day of months that are shorter. `endsOn` is optional and must be on or after `sendOn`; it is the last date a send can happen, and a send due on `endsOn` still goes out. Omit it to repeat until you cancel the schedule.

Schedules created in the dashboard can use repeat rules the API doesn't offer. Those schedules read back with `recurrence.frequency` set to `custom`.

```bash theme={null}
curl --request POST 'https://payouts.api.trykarat.com/payment-schedules' \
  --header "Authorization: Bearer $KARAT_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Weekly crew",
    "idempotencyKey": "0f8c2b1e-6a52-4c1e-9d0a-6f3b8a9d2c11",
    "recipients": [
      { "recipientId": "9b2d7c1a-3e4f-4a5b-8c6d-7e8f9a0b1c2d", "amount": 25000, "description": "Shift pay" },
      { "payeeEmail": "grace@example.com", "amount": 5000 }
    ],
    "sendOn": "2026-11-02",
    "recurrence": { "frequency": "weekly", "endsOn": "2027-03-01" }
  }'
```

A successful creation returns HTTP `201` with the schedule in `data`, including `status`, `totalAmount`, and `nextOccurrences` (the next 10 send dates and what each sends). Omit `sourceAccountId` to let Karat select the default account, or pass an account ID from [`GET /accounts`](https://help.trykarat.com/developers/account-transactions) to choose which checking account funds the schedule.

The `idempotencyKey` is a UUID and becomes the schedule's `id`. Retrying with the same key and body returns the same schedule; reusing the key with a different body returns `409 IDEMPOTENCY_KEY_REUSED`. If scheduled payments are not enabled for your organization, creation returns `403 SCHEDULES_NOT_ENABLED`; contact Karat.

## Update a schedule

`PATCH /payment-schedules/{id}` changes only the fields you send; include at least one of `name`, `sourceAccountId`, `recipients`, or `sendOn`. `recipients` replaces the whole list: recipients left out are removed, and a `description` left out is cleared. `sendOn` and `recurrence` must be sent together; `"recurrence": null` turns a repeating schedule into a one-time one.

If the schedule's recipients change between Karat reading the schedule and saving your update, for example when a teammate edits it on the dashboard, the update is refused with `409 SCHEDULE_CHANGED`. Read the schedule again with `GET /payment-schedules/{id}`, rebuild your change from what you read, and send it with a new `idempotencyKey`. Reuse the same key only to retry a request whose response you didn't receive. A teammate's change to a recipient's amount or description in between is not detected: your `recipients` list replaces it.

## Skip, pause, or cancel

* `PUT .../skip` with `{ "date": "2026-11-09" }` skips one send date for every recipient; `PUT .../restore` with the same body undoes it. A skipped date shows in `skippedDates` and as `"skipped": true` in `nextOccurrences`. The date must be one the schedule sends on, and a one-time schedule can't be skipped; cancel it instead. A date that has passed can't be restored, including today's once the 14:00 UTC send run has started.
* `PUT .../pause` stops all sends until `PUT .../resume`. A paused schedule has `status: "paused"`.
* `PUT .../cancel` stops the schedule for good and returns `data.status: "canceled"`, also when the schedule was already canceled. A canceled schedule is no longer listed or retrievable and can't be resumed.

Pause, resume, and cancel take no request body.

Each schedule has a `status`:

| `status` | Meaning |
| - | - |
| `active` | At least one recipient has upcoming sends. |
| `paused` | Every recipient is paused. |
| `ended` | No recipient has a send left. |
| `canceled` | The schedule was canceled and is no longer listed. |

## When payments send

Scheduled payments send once a day, on their date, at 14:00 UTC: 9:00 AM Eastern in standard time and 10:00 AM during daylight saving time. If the source account's balance can't cover a payment that day, that payment is not sent and is not retried, and Karat emails you (the user who created the schedule). Later dates still send.

Schedules send as the user whose API key created them. Revoking the key does not stop its schedules; pause or cancel them.

## Payments a schedule sent

Each recipient's payment on a send date is an ordinary payment: it fires the usual [`payout.updated`](https://help.trykarat.com/developers/webhooks) webhooks and appears in [`GET /payments`](https://help.trykarat.com/api-reference/payments/list-payments). Payments include a `schedule` field with the schedule's `id` and the `occurrenceDate` that sent it, or `null` for payments not sent by a schedule.

```bash theme={null}
curl 'https://payouts.api.trykarat.com/payments?scheduleId=0f8c2b1e-6a52-4c1e-9d0a-6f3b8a9d2c11' \
  --header "Authorization: Bearer $KARAT_API_KEY"
```

To match a `payout.updated` webhook to its schedule, fetch the payment with [`GET /payments/{payment_id}`](https://help.trykarat.com/api-reference/payments/get-a-payment) using the event's `payout_intent_id` and read `schedule`.

## Errors

Errors use the standard envelope from [Pagination and errors](https://help.trykarat.com/developers/pagination-and-errors.md), with `error.code` set for these cases:

| Status | `code` | When |
| - | - | - |
| 400 | `BAD_USER_INPUT` | A date is in the past or more than 12 months out, or a `recipientId` is unknown or archived. |
| 400 | `DUPLICATE_RECIPIENT` | A recipient is listed twice. |
| 403 | `SCHEDULES_NOT_ENABLED` | Scheduled payments aren't enabled for your organization, on create or when an update adds a recipient. Existing schedules can still be paused, skipped, or canceled. |
| 403 | `FORBIDDEN` | The user whose key made the request can't create payouts. |
| 404 | `SCHEDULE_NOT_FOUND` | No schedule has this ID, or it belongs to another organization. For a canceled schedule, every operation except cancel returns `404` with no `code`, as do `GET` and `PATCH` on any missing schedule. |
| 409 | `SCHEDULE_CHANGED` | The schedule changed during your update. Read it again and retry. |
| 409 | `IDEMPOTENCY_KEY_REUSED` | The key was already used for a different request. |

Invalid request bodies, and a `payeeEmail` that matches no recipient, return `400` with a message and no `code`. A key without the required permission returns `403` with a message such as `API key requires payments.write`.

*<sub>Karat is a fintech company, not an FDIC-insured bank. Banking and Card services provided by Grasshopper Bank, N.A., First Internet Bank of Indiana, and Cross River Bank, Members FDIC.</sub>*
