Release history

Ledger API

The package page: prerequisites, versioning, what is not changing, and the full release history.

The Ledger API lets you issue invoices, capture payments against them, and refund captured funds over HTTPS. This page explains how releases are identified and pinned, and lists every release with its changes.

Prerequisites

  • A secret key for the environment you call. sk_test_ keys are accepted only by the sandbox host, and sk_live_ keys only by the production host.
  • A release to pin. Send it in the Ledger-Version header on every request.

Versioning

The API uses dated releases, not SemVer. A release is identified by the date it shipped, and an integration opts into one with the Ledger-Version header:

Ledger-Version: 2026-09-01

Omitting the header pins the request to the release that was current when the account made its first API call, so an unmaintained integration never breaks on our schedule.

The client SDKs use SemVer, because an SDK is a dependency in your build and needs a version range your package manager can reason about. The API is a remote service, so it needs a pin that survives a deploy you did not perform. @ledger/node@4.2.0 can target API release 2026-09-01; the two numbers are not related and do not move together.

Only breaking changes wait for you to opt in. Additive changes reach every pinned release on the day they ship: new optional request fields, new response fields, new enum members in responses, new operations, and new webhooks. Clients must ignore unrecognized response fields and tolerate unrecognized enum members. An integration that fails closed on an unknown enum member breaks on an additive change.

What is not changing

These commitments come up at upgrade planning often enough to be stated here.

  • A declined payment continues to return 201, not 402. The HTTP status describes the fate of the request, not the commercial outcome of the attempt. Decision 1 of the design decisions records the reasoning and the cost it imposes on integrators arriving from other payment APIs.
  • Money stays an integer in the currency minor unit. There is no plan to add a decimal-string representation alongside it. Two representations of one amount is a reconciliation defect waiting for a mismatched parser.
  • invoice_number stays gapless, and voided numbers are never reused. Auditors rely on the sequence, and a gap is meaningful information that renumbering would destroy.

Release history

Releases of this API are listed newest first. For day-to-day integration, stay on this page. Archive topics remain available for bookmarks and support lookups.

2026-09-01 (current)

Overview

Ledger API 2026-09-01 makes refunds that are still processing count against a payment's refund headroom, adds the fields and events needed to track those refunds, and corrects the status returned for a missing If-Match header. This release changes refund behavior. Review the compatibility information before you pin to it.

New Features

  • Pending refund total: Payment.amount_refund_pending carries the total of refunds that are still processing. The remaining refund headroom on a payment is amount - amount_refunded - amount_refund_pending.
  • Refund retrieval and events: GET /v1/refunds/{refund_id} returns a single refund, and the refund.succeeded and refund.failed webhooks report when a refund reaches a terminal state. An integration that waits for one refund to finish before it issues the next can now observe that directly.
  • Refund settlement time: Refund.settled_at records when a refund reached a terminal state, separately from created_at, so reconciliation can align each refund to the period in which it completed.

Enhancements

  • Refund headroom reservation: A refund that is still processing reserves its amount against the payment. A new refund is accepted only if it fits in the remaining headroom, and the check and the reservation are a single atomic step. A request that does not fit returns 422 refund_amount_exceeded. Previously, only succeeded refunds counted, so two concurrent ₹600 refunds against a ₹1,000 payment were both accepted and the payer received ₹1,200.
  • Refunded amount: Payment.amount_refunded counts only succeeded refunds, which is the money actually returned to the payer. It previously also counted refunds that were still processing, so the field showed a pending refund as spent while the refund check ignored it.

Fixes

  • Missing If-Match header: When a PATCH /v1/invoices/{invoice_id} request has no If-Match header, the API returns 428 precondition_required. It previously returned 412 precondition_failed, the status for a stale value, so a client could not tell the two conditions apart. This fix applies to every release.

Compatibility

The refund headroom and amount_refunded changes apply only to requests pinned to 2026-09-01 or later. Accounts created before this date keep their existing pin until they send Ledger-Version: 2026-09-01. amount_refund_pending is returned only from this release, because on earlier releases amount_refunded still includes pending refunds and returning both would count them twice. GET /v1/refunds/{refund_id}, the two refund webhooks, and settled_at are available on every release.

Review every read of amount_refunded, and every code path that can issue more than one refund against a payment, before you pin to this release. Refer to Migrating to 2026-09-01 for the full procedure, including a sandbox test.

Known Issues

  • Several earlier SDK versions apply the Ledger-Version value set when the client is created and ignore a per-call override. Confirm the header on the outgoing request when you test this release.

Archive topic: Ledger API 2026-09-01

2026-06-15

New Features

  • Payment success event: The payment.succeeded webhook reports when a payment reaches succeeded. A payment that did not complete its invoice previously produced no event when it succeeded, so the only way to learn the outcome was to retrieve the payment.
  • Void reason: Invoice.void_reason returns the reason supplied to voidInvoice. The reason was previously stored but never returned, so an audit could not read it back through the API.

Compatibility

The changes in this release reach every pinned release. No change to Ledger-Version is needed.

Invoice.amount_due is deprecated. It is superseded by Invoice.amount_remaining, which has carried the same value since the initial release; two names for one value left integrators asking which to trust. amount_due continues to be populated in every release up to and including the first dated release after 2027-06-15, and is then removed. Replace each read of amount_due with amount_remaining before that release.

Archive topic: Ledger API 2026-06-15

2026-03-01

Initial Release

  • Introduces invoices: create, retrieve, update, delete, finalize, void, and list.
  • Introduces payments: create and retrieve, with the processing, succeeded, and failed outcomes carried in Payment.status rather than in the HTTP status code.
  • Introduces full and partial refunds against a succeeded payment.
  • Introduces the invoice.finalized, invoice.paid, invoice.voided, and payment.failed webhooks.
  • Introduces cursor pagination on GET /v1/invoices.
  • Introduces Idempotency-Key, required on every POST that creates a resource.
  • Introduces If-Match and ETag optimistic concurrency on PATCH /v1/invoices/{invoice_id}.

Archive topic: Ledger API 2026-03-01

← Back to Portfolio