---
title: "Create several payment links at once"
method: POST
path: "/v1/payment-links/batch"
tags: ["Payment links"]
---

# Create several payment links at once

`POST /v1/payment-links/batch`

## Request

- Base URL: `https://api.getfinero.com/functions/v1/api`
- URL: `https://api.getfinero.com/functions/v1/api/v1/payment-links/batch`
- Auth: HTTP bearer

Splits one collectible invoice or installment into 1 to 12 payment links in ONE atomic call: every link is created or none is, so a split never stops half-way. Each payment names its amount and may name its due date; each row passes createPaymentLink's checks, counting the rows before it, and together they may not exceed what is outstanding. A refused row is named in the error details as `payments[<index>].amount_minor` (0-based); nothing was created, so fix it and send the whole request again. The email decision applies to each link and is carried out after the links exist; a failed email never undoes a link.

Requires an **admin** API key (has side effects).

## Headers

- `Idempotency-Key` string, required

## Request body

- object
  - `invoice_id` string, uuid, required: Invoice to collect.
  - `installment_id` string, uuid, nullable, required: Real installment id in installment mode; null in invoice-only mode. Read the invoice collection_mode first.
  - `integration_id` string, uuid: Optional: a specific connected payments integration. Defaults to the tenant's connected provider.
  - `payments` object[], required: The links to create, in order. The response lists them in the same order.
    - `amount_minor` integer, required: How much this link collects, in integer minor units (100 = USD 1.00). Required: a batch is a decided split. The amounts of these links plus every live link may not exceed the target's `collection_outstanding`. For a three-decimal currency (BHD, JOD, KWD, OMR, TND) each amount must be a multiple of 10.
    - `payment_due_date` string, date: Optional link-owned due date (YYYY-MM-DD). Omit it to snapshot the target's current due date. Past dates are accepted for already-overdue receivables.
  - `email_delivery_mode` 'workflow' | 'send_now' | 'schedule' | 'suppress': Optional email decision applied to EACH link. Omit or use workflow to preserve tenant workflow behaviour.
  - `email_scheduled_local` string: Tenant-local YYYY-MM-DDTHH:mm wall clock, the same for every link. Required only when email_delivery_mode is schedule.

## Response `201`

Success

- object
  - `data` object[], required: The created payment links, in the order the payments were given.
    - `id` string, uuid, required: Payment link id.
    - `invoice_id` string, uuid, required: Invoice the link collects.
    - `installment_id` string, uuid, nullable, required: Installment the link collects. An installment may carry SEVERAL live links (0280) — their amounts together may not exceed the installment's `collection_outstanding`, which the database enforces under a row lock. What Finero already collected is ALREADY netted off that figure; subtracting it a second time under-states the room.
    - `status` 'active' | 'processing' | 'paid' | 'inactive' | 'needs_replacement' | 'action_required', required: Lifecycle status. active = payable now; processing = a payment is being confirmed; paid = settled; inactive = deactivated or superseded; needs_replacement = the provider-side link stopped being usable (expired or removed at the provider), so a new link must be created; action_required = frozen pending a human decision — Finero will not act on it automatically. A payment whose provider report does not match the request is frozen in processing, not here.
    - `provider` 'stripe' | 'wix', required: Payment provider hosting the checkout.
    - `mode` 'test' | 'live', required: Provider environment. `test` means a TEST processor handled this money: it is not revenue and must never be reported as cash collected, however real the amount looks. `live` is real money.
    - `amount_minor` integer, required: Amount the link collects. Integer amount in the currency's MINOR unit (e.g. cents), as defined by the PAYMENT PROVIDER's own table, which is not always ISO 4217 — the two disagree on ISK and MGA among others, so converting with an ISO exponent misprices those. Invoice and installment amounts are exact decimal strings and are the safe basis for arithmetic and display.
    - `currency` string, required: Link currency. ISO 4217 alphabetic code, e.g. "USD".
    - `payment_due_date` string, date, nullable, required: Business due date owned by this payment link (YYYY-MM-DD). It is snapshotted at creation and does not change when the source installment changes. Null only for legacy links whose source had no date.
    - `url` string, nullable, required: Hosted customer-facing pay-page URL; null once the link is withdrawn (`inactive`). Treat as a capability: anyone with the URL can open the checkout.
    - `paid_at` string, date-time, nullable, required: When the link was paid (ISO 8601 UTC).
    - `paid_amount_minor` integer, nullable, required: Settled amount. Integer amount in the currency's MINOR unit (e.g. cents), as defined by the PAYMENT PROVIDER's own table, which is not always ISO 4217 — the two disagree on ISK and MGA among others, so converting with an ISO exponent misprices those. Invoice and installment amounts are exact decimal strings and are the safe basis for arithmetic and display.
    - `paid_currency` string, nullable, required: Settled currency. ISO 4217 alphabetic code, e.g. "USD".
    - `deactivated_at` string, date-time, nullable, required: When the link was deactivated (ISO 8601 UTC).
    - `created_at` string, date-time, required: Creation time (ISO 8601 UTC).
    - `updated_at` string, date-time, required: Last update time (ISO 8601 UTC).
    - `incident` object, nullable, required: A payment the provider reported on this link that Finero refused to record — null almost always. While it is open the link stays as it is (a frozen link reads `processing`), so the same money cannot be asked for twice. Read-only here: a workspace admin checks it at the provider and resolves it in the app's Payments view, which closes the link so a new one can be created.
      - `reason` 'amount_mismatch' | 'currency_mismatch' | 'mode_mismatch' | 'attempt_request_mismatch' | 'attempt_tenant_mismatch' | 'attempt_not_found' | 'duplicate_settlement', required: Why Finero refused to record the payment. duplicate_settlement = the link was already paid and a second payment arrived; the others = what the provider reported does not match what this link asked for.
      - `provider` 'stripe' | 'wix', required: Provider that reported the payment.
      - `reported_amount_minor` integer, required: Amount the provider reported. Integer amount in the currency's MINOR unit (e.g. cents), as defined by the PAYMENT PROVIDER's own table, which is not always ISO 4217 — the two disagree on ISK and MGA among others, so converting with an ISO exponent misprices those. Invoice and installment amounts are exact decimal strings and are the safe basis for arithmetic and display.
      - `reported_currency` string, required: Currency the provider reported. ISO 4217 alphabetic code, e.g. "USD".
      - `recorded_at` string, date-time, required: When Finero recorded the incident (ISO 8601 UTC).

## Other responses

- `400`: `idempotency_key_required` — This operation requires the Idempotency-Key header (1–255 characters). `validation_failed` — The request failed schema validation. See error.details for field messages.
- `401`: `invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.
- `403`: `feature_unavailable` — The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key's permission does not allow this operation (readonly keys cannot call admin-only operations).
- `404`: `not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.
- `409`: `conflict` — State conflict — e.g. live payment links already cover the installment, or a concurrent identical request is in flight. `idempotency_conflict` — The Idempotency-Key was already used with a DIFFERENT request body. Use a fresh key for a new request.
- `413`: `payload_too_large` — The request body exceeds the 64 KiB limit.
- `422`: `amount_exceeds_available` — The installment is collectible, but the amount asked for is more than is still available on it. The available amount is the installment's collection_outstanding minus the amounts of its live payment links; what Finero already collected is netted off collection_outstanding and must not be subtracted twice. Ask for that amount or less, or omit amount_minor to take exactly what is left. `currency_not_supported` — The selected payment provider cannot collect the invoice's currency (single-currency providers can only charge in their store currency). Use a provider that supports it, or collect the invoice outside Finero. `installment_not_collectible` — The invoice or installment target cannot be collected right now — it is already paid, excluded from collections, has no outstanding balance, its invoice is not collection-ready, or its invoice is no longer present in your ERP. GET /v1/invoices/{invoiceId} reports the invoice's collection_status and each installment's collectible flag. `integration_unavailable` — The tenant has no connected payments integration able to mint this link, or the one selected is not ready. Connect or repair a payment provider in the Finero app, then retry.
- `429`: `rate_limit_exceeded` — Too many requests. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.
- `500`: `internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/getfinero/apis/finero-public-api.md) · [All operations](https://skmtc.dev/getfinero/apis/finero-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getfinero/apis/finero-public-api/revisions/3d456e222775?raw)
