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

# Recording payments

> Mark invoices as paid in full or in part, and handle batches of any size

The [Mark Invoice as Paid API](/apis/mark-as-paid) records a payment against one or more invoices. This guide covers the behaviors that matter in a real integration. For the full field list, see the endpoint page.

<Warning>
  `x-idempotency-key` is required on this endpoint. Generate a new UUID for each payment. Sending the same key again within 5 minutes returns `409 Conflict` and is not processed. If a request times out, check the invoice with [Invoice Details](/apis/invoice-details) before sending the payment again. A new key on a retry can apply the same payment twice.
</Warning>

## Before you start

* The invoice must be in the `APPROVED` state.
* Your API key needs the **Payment** permission with **Create** enabled.
* The `slug` you send for each invoice is the invoice `id` returned by [Invoice Creation](/apis/invoice-creation), [Invoice List](/apis/invoice-list) and [Invoice Details](/apis/invoice-details).

## Partial payments

`amount` can be less than the invoice's remaining due amount. The invoice stays open for the balance. An `amount` above the remaining due returns `AMOUNT_EXCEEDS` for that invoice.

## Small batches (100 invoices or fewer)

These run synchronously. The response contains one result per invoice, in the same order as your request.

```json theme={null}
{
  "ok": true,
  "data": {
    "status": "DONE",
    "results": [
      { "slug": "invoice_1", "status": "DONE" },
      { "slug": "invoice_2", "status": "ERROR", "reason": "INVALID_STATE" }
    ]
  }
}
```

Each invoice is attempted independently, so one failure doesn't stop the rest. Check every entry, not just the HTTP status.

| Result | Reason | What to do |
| - | - | - |
| `DONE` | none | Nothing. The payment is recorded. |
| `ERROR` | `NOT_FOUND` | Check the `slug`. |
| `ERROR` | `INVALID_STATE` | The invoice isn't `APPROVED`. Approve it first. |
| `ERROR` | `PLATFORM_PAYMENT_IN_FLIGHT` | A payment through Pazy is already in progress. Wait for it to settle. |
| `ERROR` | `AMOUNT_EXCEEDS` | Lower `amount` to the remaining due. |
| `SKIPPED` | `LOCK_HELD` | Another payment action is running on this invoice. Nothing was recorded for this invoice. Retry shortly with a new idempotency key. |

<Note>
  If every invoice in the batch ends in `ERROR` or `SKIPPED`, the response status is `422` instead of `200`. The body shape is the same, so parse `results` either way.
</Note>

## Large batches (more than 100 invoices)

These run asynchronously. The response comes back immediately with `status: "PENDING"` and a `bulkActionSlug`.

<Steps>
  <Step title="Submit the batch">
    Send the request as usual. Save `data.bulkActionSlug` from the response.
  </Step>

  <Step title="Poll for progress">
    Call the [Batch Status API](/apis/mark-as-paid-batch-status) with the slug every 10 to 30 seconds.

    ```bash theme={null}
    curl https://api.pazy.io/v1.0/payment/mark-as-paid/batch/BULK_ACTION_SLUG \
      -H "Authorization: Api-Key YOUR_API_KEY"
    ```
  </Step>

  <Step title="Stop at a terminal status">
    * `COMPLETED`: every invoice was paid.
    * `PROCESSED`: finished, but some invoices were skipped. See `discardedCount`.
    * `PROCESSING`: still running. Keep polling.
  </Step>

  <Step title="Confirm skipped invoices">
    The status API only returns counts. To find out which invoices were skipped, check each one with [Invoice Details](/apis/invoice-details).
  </Step>
</Steps>

Only one async batch can run per organization at a time. This includes batches started from the Pazy web app. A second one returns `409 MAP_BATCH_IN_PROGRESS`. Wait for the first to finish, or split your work into batches of 100 or fewer, which aren't affected.

<Tip>
  To hear about payments made in Pazy, or synced from your accounting system, without polling, subscribe to the `payment.invoice.*` [webhook events](/guides/track-invoice-payments-with-webhooks).
</Tip>

## Accounting sync

Payments recorded through the API are not pushed to your connected accounting platform. Their sync state is always `SKIPPED`. If you need the payment in Tally, Zoho Books or another platform, sync it through that platform's own mechanism.


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