Ledger API (2026-09-01)

Download OpenAPI specification:

Ledger API Support: api-support@ledgerapi.dev URL: https://docs.ledgerapi.dev/support License: MIT

Invoicing, payment capture, and refunds for B2B billing.

The Ledger API is a resource-oriented HTTP API for issuing invoices, capturing payments against them, and refunding captured funds.

Conventions

  • Transport. HTTPS only. Requests over HTTP are rejected at the edge; no redirect is issued, because redirecting a request that carries a credential would leak that credential to the plaintext hop.
  • Encoding. Request and response bodies are UTF-8 encoded JSON.
  • Time. All timestamps are RFC 3339 date-time values in UTC with a Z offset (for example, 2026-09-01T14:32:07Z).
  • Money. All monetary values are represented by a Money object holding an integer amount in the currency's minor unit and an ISO 4217 currency code. See the Money schema for the zero-decimal and three-decimal currency rules.
  • Identifiers. Resource identifiers are opaque, prefixed strings (inv_, pay_, ref_, cus_). Do not parse them, infer ordering from them, or assume a fixed length beyond the documented maximum.

Versioning

The major version is pinned in the path (/v1). Backward-incompatible changes ship as a new dated release, which an integration opts into with the Ledger-Version header. Omitting the header pins the request to the release that was current when the account's first API call was made, so an unmaintained integration never breaks silently.

Adding an optional request field, a response field, an enum member to a response, or a new operation or webhook is not treated as breaking. Additive changes reach every pinned release on the day they ship. Clients must ignore unrecognized response fields and must tolerate unrecognized enum members rather than failing closed.

Errors

Errors use application/problem+json as defined in RFC 9457. A declined payment is not an HTTP error; see the Payment schema.

Invoices

An invoice is a request for payment. It moves through an explicit state machine: draft -> open -> paid | void | uncollectible. Only a draft invoice is mutable.

An invoice moved from draft to open Webhook

Sent when an invoice is finalized. Verify the signature before parsing the body; see the WebhookEvent schema.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

An invoice was paid in full Webhook

Sent when the sum of succeeded payments against an invoice reaches its total. This event is the correct trigger for provisioning; do not provision on createPayment returning processing.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

An open invoice was voided Webhook

Sent when an invoice moves from open to void. The invoice in data carries void_reason.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

List invoices

Returns a page of invoices for the authenticated account, newest first by created_at. Pagination is cursor based; see the cursor parameter.

Authorizations:
secretKey
query Parameters
limit
integer <int32> [ 1 .. 100 ]
Default: 20

Maximum number of records to return in one page.

cursor
string <= 512 characters

An opaque cursor from the next_cursor field of a previous response. Cursors are opaque by contract: do not decode, construct, or store them beyond the life of a pagination loop.

Cursor pagination is used instead of numeric offsets so that records created during a long walk neither shift results nor cause a record to be skipped.

status
string (InvoiceStatus)
Enum: "draft" "open" "paid" "void" "uncollectible"

Return only invoices in this state. Omit to return all states.

customer_id
string <= 64 characters ^cus_[A-Za-z0-9]{1,58}$

Return only invoices billed to this customer.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": true,
  • "next_cursor": "string"
}

Create a draft invoice

Creates an invoice in the draft state. A draft invoice is not visible to the customer, accrues no dunning, and can be edited with updateInvoice or removed with deleteInvoice. Call finalizeInvoice to issue it.

This operation is not idempotent by HTTP method, so an Idempotency-Key header is required. See the header description for replay and conflict semantics.

Authorizations:
secretKey
header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Idempotency-Key
required
string [ 16 .. 255 ] characters
Example: 8f14e45f-ea1b-4c2d-9a35-7b1d6e0c3a92

A client-generated unique key that makes this POST safe to retry. Use a UUIDv4 or another value with at least 128 bits of entropy, and generate it once per logical operation, not once per HTTP attempt.

Semantics:

  • First use. The request is processed and the response is stored against the key for 24 hours.
  • Replay with an identical body. The stored response is returned verbatim, with the original status code and an Idempotent-Replay: true header. No new resource is created.
  • Replay while the original is still in flight. 409 with the idempotency_in_progress error code. Retry after a short delay.
  • Reuse with a different body. 409 with idempotency_key_reused. Nothing is processed. This is a bug in the caller: the same key is being used for two different operations.
  • After 24 hours. The key is forgotten and a request bearing it is treated as new. An integration that retries on a schedule longer than 24 hours must not rely on this header for deduplication.

The key is scoped to the account and the endpoint. The same key may be used on createInvoice and on createPayment without collision.

Request Body schema: application/json
required
customer_id
required
string^cus_[A-Za-z0-9]{1,58}$

The customer to bill.

currency
required
string^[A-Z]{3}$

ISO 4217 code. Cannot be changed after creation.

due_at
string or null <date-time>

When payment falls due. Must be in the future at finalization time, not at creation time; a draft may sit past its own due date.

required
Array of objects (InvoiceLineItem) [ 1 .. 250 ] items

Responses

Request samples

Content type
application/json
{
  • "customer_id": "cus_9ZQx4Kp2Lm",
  • "currency": "INR",
  • "due_at": "2026-10-01T00:00:00Z",
  • "line_items": [
    ]
}

Response samples

Content type
application/json
{
  • "id": "inv_7TgH2ReQvA",
  • "object": "invoice",
  • "status": "draft",
  • "invoice_number": "INV-2026-004182",
  • "customer_id": "cus_9ZQx4Kp2Lm",
  • "currency": "string",
  • "total": {
    },
  • "amount_paid": {
    },
  • "amount_remaining": {
    },
  • "line_items": [
    ],
  • "due_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "finalized_at": "2019-08-24T14:15:22Z",
  • "voided_at": "2019-08-24T14:15:22Z",
  • "void_reason": "duplicate",
  • "amount_due": {
    }
}

Retrieve an invoice

Returns a single invoice. The response carries an ETag, which you must supply as If-Match when updating the invoice.

Authorizations:
secretKey
path Parameters
invoice_id
required
string <= 64 characters ^inv_[A-Za-z0-9]{1,58}$

The invoice identifier, for example inv_7TgH2ReQvA.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Responses

Response samples

Content type
application/json
{
  • "id": "inv_7TgH2ReQvA",
  • "object": "invoice",
  • "status": "draft",
  • "invoice_number": "INV-2026-004182",
  • "customer_id": "cus_9ZQx4Kp2Lm",
  • "currency": "string",
  • "total": {
    },
  • "amount_paid": {
    },
  • "amount_remaining": {
    },
  • "line_items": [
    ],
  • "due_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "finalized_at": "2019-08-24T14:15:22Z",
  • "voided_at": "2019-08-24T14:15:22Z",
  • "void_reason": "duplicate",
  • "amount_due": {
    }
}

Update a draft invoice

Applies a partial update to a draft invoice. Attempting to update an invoice in any other state returns 422 with the invoice_not_editable error code.

If-Match is required. Send the ETag from your most recent read of the invoice. If the invoice has changed since that read, the request is rejected with 412 and nothing is written. This makes concurrent edits from two operators fail loudly instead of silently overwriting one another.

Authorizations:
secretKey
path Parameters
invoice_id
required
string <= 64 characters ^inv_[A-Za-z0-9]{1,58}$

The invoice identifier, for example inv_7TgH2ReQvA.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

If-Match
required
string
Example: "a7f3c1e9b2d4"

The ETag from your most recent read of the resource. Required. A request without it is rejected with 428; a request with a stale value is rejected with 412.

Request Body schema: application/json
required
non-empty
due_at
string or null <date-time>
Array of objects (InvoiceLineItem) [ 1 .. 250 ] items

Responses

Request samples

Content type
application/json
{
  • "due_at": "2026-10-15T00:00:00Z"
}

Response samples

Content type
application/json
{
  • "id": "inv_7TgH2ReQvA",
  • "object": "invoice",
  • "status": "draft",
  • "invoice_number": "INV-2026-004182",
  • "customer_id": "cus_9ZQx4Kp2Lm",
  • "currency": "string",
  • "total": {
    },
  • "amount_paid": {
    },
  • "amount_remaining": {
    },
  • "line_items": [
    ],
  • "due_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "finalized_at": "2019-08-24T14:15:22Z",
  • "voided_at": "2019-08-24T14:15:22Z",
  • "void_reason": "duplicate",
  • "amount_due": {
    }
}

Delete a draft invoice

Permanently deletes a draft invoice. A draft has no invoice_number, so deleting it leaves no gap in the sequence.

Only a draft can be deleted. An invoice in any other state returns 422 with invoice_not_editable; cancel an open invoice with voidInvoice instead, which keeps it on record.

Deleting is idempotent by method. Repeating the call after a successful delete returns 404, which a retry loop should treat as confirmation rather than failure.

Authorizations:
secretKey
path Parameters
invoice_id
required
string <= 64 characters ^inv_[A-Za-z0-9]{1,58}$

The invoice identifier, for example inv_7TgH2ReQvA.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Responses

Response samples

Content type
application/problem+json
{}

Finalize a draft invoice

Transitions an invoice from draft to open, freezing its line items and assigning a sequential, gapless invoice_number for the account's current financial year.

Finalizing is the point at which the invoice becomes a legal document in most jurisdictions. After this call the line items cannot be edited; correct a finalized invoice by voiding it and issuing a new one, or by issuing a credit note.

This operation is effectively idempotent by state: calling it on an invoice that is already open returns 200 with the unchanged invoice rather than an error. Calling it on a paid, void, or uncollectible invoice returns 422 with invoice_not_editable.

Authorizations:
secretKey
path Parameters
invoice_id
required
string <= 64 characters ^inv_[A-Za-z0-9]{1,58}$

The invoice identifier, for example inv_7TgH2ReQvA.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Idempotency-Key
string [ 16 .. 255 ] characters

Optional here, because this operation is already idempotent by state: repeating it against a resource that has already made the transition returns the current resource unchanged. Supply a key anyway if you want the stored-response guarantee described on createInvoice.

Responses

Response samples

Content type
application/json
{
  • "id": "inv_7TgH2ReQvA",
  • "object": "invoice",
  • "status": "draft",
  • "invoice_number": "INV-2026-004182",
  • "customer_id": "cus_9ZQx4Kp2Lm",
  • "currency": "string",
  • "total": {
    },
  • "amount_paid": {
    },
  • "amount_remaining": {
    },
  • "line_items": [
    ],
  • "due_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "finalized_at": "2019-08-24T14:15:22Z",
  • "voided_at": "2019-08-24T14:15:22Z",
  • "void_reason": "duplicate",
  • "amount_due": {
    }
}

Void an open invoice

Transitions an open invoice to void. A voided invoice is cancelled and will not be collected, but it is never deleted and its invoice_number is never reused, because reusing or removing a number would break the gapless sequence that auditors rely on.

An invoice with any succeeded payment against it cannot be voided; refund the payments first. That attempt returns 422 with invoice_has_payments.

Authorizations:
secretKey
path Parameters
invoice_id
required
string <= 64 characters ^inv_[A-Za-z0-9]{1,58}$

The invoice identifier, for example inv_7TgH2ReQvA.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Idempotency-Key
string [ 16 .. 255 ] characters

Optional here, because this operation is already idempotent by state: repeating it against a resource that has already made the transition returns the current resource unchanged. Supply a key anyway if you want the stored-response guarantee described on createInvoice.

Request Body schema: application/json
optional
reason
string
Enum: "duplicate" "fraudulent" "issued_in_error" "other"

Why the invoice is being voided. Returned as void_reason on the invoice, including in the invoice.voided event.

Responses

Request samples

Content type
application/json
{
  • "reason": "duplicate"
}

Response samples

Content type
application/json
{
  • "id": "inv_7TgH2ReQvA",
  • "object": "invoice",
  • "status": "draft",
  • "invoice_number": "INV-2026-004182",
  • "customer_id": "cus_9ZQx4Kp2Lm",
  • "currency": "string",
  • "total": {
    },
  • "amount_paid": {
    },
  • "amount_remaining": {
    },
  • "line_items": [
    ],
  • "due_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "finalized_at": "2019-08-24T14:15:22Z",
  • "voided_at": "2019-08-24T14:15:22Z",
  • "void_reason": "duplicate",
  • "amount_due": {
    }
}

Payments

A payment is an attempt to move funds against an open invoice. Creating a payment always succeeds at the HTTP layer if the request is well formed; the outcome of the attempt is carried in Payment.status.

A payment attempt captured funds Webhook

Sent when a payment reaches succeeded, whether it was created in that state or left processing later. A payment that completes its invoice also produces invoice.paid; provision on that event, not on this one.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

A payment attempt reached a terminal failure Webhook

Sent when a payment reaches failed, whether it was created in that state or left processing later. The invoice remains open and collectible.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

Create a payment against an invoice

Attempts to capture funds against an open invoice.

A declined payment is a 201, not a 4xx. The HTTP status describes whether the API understood and accepted your request; it does not describe the commercial outcome of the attempt. A card decline, an insufficient-funds response, or a bank rejection all produce a Payment resource in the failed state with failure_code and failure_message populated. Branch on status, never on the status code, to decide what to tell the payer.

Some payment methods settle asynchronously. Those return a payment in the processing state; the terminal outcome arrives on the payment.succeeded or payment.failed webhook. Build on the webhooks, and use getPayment to reconcile a payment whose event you suspect you missed, not as a polling loop.

Authorizations:
secretKey
header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Idempotency-Key
required
string [ 16 .. 255 ] characters
Example: 8f14e45f-ea1b-4c2d-9a35-7b1d6e0c3a92

A client-generated unique key that makes this POST safe to retry. Use a UUIDv4 or another value with at least 128 bits of entropy, and generate it once per logical operation, not once per HTTP attempt.

Semantics:

  • First use. The request is processed and the response is stored against the key for 24 hours.
  • Replay with an identical body. The stored response is returned verbatim, with the original status code and an Idempotent-Replay: true header. No new resource is created.
  • Replay while the original is still in flight. 409 with the idempotency_in_progress error code. Retry after a short delay.
  • Reuse with a different body. 409 with idempotency_key_reused. Nothing is processed. This is a bug in the caller: the same key is being used for two different operations.
  • After 24 hours. The key is forgotten and a request bearing it is treated as new. An integration that retries on a schedule longer than 24 hours must not rely on this header for deduplication.

The key is scoped to the account and the endpoint. The same key may be used on createInvoice and on createPayment without collision.

Request Body schema: application/json
required
invoice_id
required
string^inv_[A-Za-z0-9]{1,58}$

The open invoice to pay.

required
object (Money)

A monetary value.

amount is an integer in the minor unit of currency, never a decimal. Floating-point types cannot represent every decimal fraction exactly, so accumulating them across a ledger drifts; integers do not.

The minor unit depends on the currency, and there is no safe default:

  • Two-decimal currencies (INR, USD, EUR): amount is in paise or cents. 450000 INR is Rs 4,500.00.
  • Zero-decimal currencies (JPY, KRW, VND): amount is the whole unit. 4500 JPY is Yen 4,500.
  • Three-decimal currencies (KWD, BHD, TND): amount is thousandths. 4500 KWD is KD 4.500.

Do not hardcode a divisor of 100 when formatting for display.

payment_method_id
required
string^pm_[A-Za-z0-9]{1,58}$

A stored payment method belonging to the invoice customer.

Responses

Request samples

Content type
application/json
{
  • "invoice_id": "inv_7TgH2ReQvA",
  • "amount": {
    },
  • "payment_method_id": "pm_4KdN8xWs1B"
}

Response samples

Content type
application/json
{
  • "id": "pay_3MvB9kLd7Q",
  • "object": "payment",
  • "status": "processing",
  • "invoice_id": "string",
  • "amount": {
    },
  • "amount_refunded": {
    },
  • "amount_refund_pending": {
    },
  • "failure_code": "insufficient_funds",
  • "failure_message": "string",
  • "created_at": "2019-08-24T14:15:22Z"
}

Retrieve a payment

Returns a single payment, including its refund totals. Read amount_refunded and amount_refund_pending here before deciding whether a further refund fits.

Payment outcomes are delivered by webhook. Retrieve a payment to reconcile one whose event you suspect you missed, not in a polling loop.

Authorizations:
secretKey
path Parameters
payment_id
required
string <= 64 characters ^pay_[A-Za-z0-9]{1,58}$

The payment identifier, for example pay_3MvB9kLd7Q.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Responses

Response samples

Content type
application/json
{
  • "id": "pay_3MvB9kLd7Q",
  • "object": "payment",
  • "status": "processing",
  • "invoice_id": "string",
  • "amount": {
    },
  • "amount_refunded": {
    },
  • "amount_refund_pending": {
    },
  • "failure_code": "insufficient_funds",
  • "failure_message": "string",
  • "created_at": "2019-08-24T14:15:22Z"
}

Refunds

A refund returns captured funds to the payer. Refunds are only valid against a payment in the succeeded state.

A refund returned funds to the payer Webhook

Sent when a refund leaves processing for succeeded. Its amount moves from amount_refund_pending to amount_refunded on the payment.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

A refund reached a terminal failure Webhook

Sent when a refund leaves processing for failed. No funds moved, and the amount it reserved is released back to the payment's refund headroom.

Authorizations:
secretKey
Request Body schema: application/json
required
id
required
string

Unique per event. Deduplicate on this.

object
required
string
Value: "event"
type
required
string

The event name, for example invoice.paid.

created_at
required
string <date-time>
required
object

The resource snapshot at the moment the event fired.

Responses

Request samples

Content type
application/json
{
  • "id": "evt_2WnF6yBk8R",
  • "object": "event",
  • "type": "invoice.paid",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

Refund a payment

Returns funds to the payer. Only a succeeded payment can be refunded; any other state returns 422 with payment_not_refundable. Omit amount to refund everything not already refunded or pending.

A refund is accepted only if its amount fits within the payment's refund headroom, amount - amount_refunded - amount_refund_pending. Refunds still processing count against that headroom, and the check and the reservation are a single atomic step, so two concurrent requests cannot both claim the same amount. A request that does not fit is rejected with 422 and refund_amount_exceeded. If a pending refund ends failed, its amount is released and can be refunded again.

This is the behavior from release 2026-09-01. Earlier releases did not count processing refunds, so concurrent partial refunds could together exceed the payment. The release notes and the 2026-09-01 migration guide describe the change.

Refunding the full amount of the only payment on an invoice does not reopen the invoice. Its state remains paid.

Authorizations:
secretKey
path Parameters
payment_id
required
string <= 64 characters ^pay_[A-Za-z0-9]{1,58}$

The payment identifier, for example pay_3MvB9kLd7Q.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Idempotency-Key
required
string [ 16 .. 255 ] characters
Example: 8f14e45f-ea1b-4c2d-9a35-7b1d6e0c3a92

A client-generated unique key that makes this POST safe to retry. Use a UUIDv4 or another value with at least 128 bits of entropy, and generate it once per logical operation, not once per HTTP attempt.

Semantics:

  • First use. The request is processed and the response is stored against the key for 24 hours.
  • Replay with an identical body. The stored response is returned verbatim, with the original status code and an Idempotent-Replay: true header. No new resource is created.
  • Replay while the original is still in flight. 409 with the idempotency_in_progress error code. Retry after a short delay.
  • Reuse with a different body. 409 with idempotency_key_reused. Nothing is processed. This is a bug in the caller: the same key is being used for two different operations.
  • After 24 hours. The key is forgotten and a request bearing it is treated as new. An integration that retries on a schedule longer than 24 hours must not rely on this header for deduplication.

The key is scoped to the account and the endpoint. The same key may be used on createInvoice and on createPayment without collision.

Request Body schema: application/json
optional
object (Money)

A monetary value.

amount is an integer in the minor unit of currency, never a decimal. Floating-point types cannot represent every decimal fraction exactly, so accumulating them across a ledger drifts; integers do not.

The minor unit depends on the currency, and there is no safe default:

  • Two-decimal currencies (INR, USD, EUR): amount is in paise or cents. 450000 INR is Rs 4,500.00.
  • Zero-decimal currencies (JPY, KRW, VND): amount is the whole unit. 4500 JPY is Yen 4,500.
  • Three-decimal currencies (KWD, BHD, TND): amount is thousandths. 4500 KWD is KD 4.500.

Do not hardcode a divisor of 100 when formatting for display.

reason
string
Enum: "duplicate" "fraudulent" "requested_by_customer"

Responses

Request samples

Content type
application/json
{
  • "amount": {
    },
  • "reason": "requested_by_customer"
}

Response samples

Content type
application/json
{
  • "id": "ref_5QpZ1nTc6E",
  • "object": "refund",
  • "status": "processing",
  • "payment_id": "string",
  • "amount": {
    },
  • "reason": "duplicate",
  • "created_at": "2019-08-24T14:15:22Z",
  • "settled_at": "2019-08-24T14:15:22Z"
}

Retrieve a refund

Returns a single refund. status says whether it has reached a terminal state, and settled_at says when.

The refund.succeeded and refund.failed webhooks deliver the same transition without a request. Retrieve a refund when you need its current state on demand, for example to confirm that one refund has finished before issuing the next against the same payment.

Authorizations:
secretKey
path Parameters
refund_id
required
string <= 64 characters ^ref_[A-Za-z0-9]{1,58}$

The refund identifier, for example ref_5QpZ1nTc6E.

header Parameters
Ledger-Version
string <date>
Example: 2026-09-01

Pins the request to a dated API release, for example 2026-09-01. Omit to use the release pinned to the account. Pin explicitly in any integration you intend to maintain.

Responses

Response samples

Content type
application/json
{
  • "id": "ref_5QpZ1nTc6E",
  • "object": "refund",
  • "status": "processing",
  • "payment_id": "string",
  • "amount": {
    },
  • "reason": "duplicate",
  • "created_at": "2019-08-24T14:15:22Z",
  • "settled_at": "2019-08-24T14:15:22Z"
}