Release notes — 2.4
Idempotent refunds, webhook retries, EU invoice residency, a new export command, and the end of Node 18.
Version 2.4 makes two operations safe to retry: refunds and webhooks. It also stores EU customers' invoices in the EU, adds orders export --since to the CLI, and fixes a double charge that a network retry could cause. Node 18 support ends with this release. Upgrade steps are at the end.
What changed
Node 18 is no longer supported
Breaking for Node 18 users
Refunds you can retry
Create a refund
Refund all or part of a charge. Send the same Idempotency-Key on a retry and the API returns the first refund instead of creating a second.
| Name | In | Type | Description |
|---|---|---|---|
| Idempotency-Key required | header | string | Unique per refund attempt; kept for 24 hours |
| Field | Type | Description |
|---|---|---|
| charge_id required | string | The charge to refund |
| amount | integer | Minor units; omit for a full refund |
| reason | string | requested_by_customer, duplicate, or fraudulent |
| Status | Description | |
|---|---|---|
| 201 | Refund created | |
| 200 | Same key seen before; the original refund is returned | |
| 409 | Same key with a different body | |
| 402 | Charge already fully refunded | |
{ "charge_id": "ch_9K2f", "amount": 1500, "reason": "requested_by_customer" }
{ "id": "re_4Hd1", "charge_id": "ch_9K2f", "amount": 1500, "status": "succeeded" }
The key is scoped to your account and kept for 24 hours. A retry after 24 hours creates a new refund, so a client that retries across days must check the charge's amount_refunded first. The same header now protects POST /v1/charges, and the double-charge fix in #430 is that header applied by the SDK on every network retry.
Export orders from the CLI
$ orders export --since 2026-09-01 > september.ndjsonexported 18,204 orders (2026-09-01 to 2026-09-10) in 41 s$ head -1 september.ndjson{"id":"ord_7Qm2","created_at":"2026-09-01T00:02:11Z","status":"shipped","total":8490,"currency":"EUR"}$ orders export --since 2026-09-01 --until 2026-09-02 --status refunded | wc -l113
Upgrade steps
- 1Move to Node 20 or later
Check CI images and the Dockerfile as well as your laptop.
bashnode --version && npm install @example/sdk@2.4 @example/cli@2.4 - 2Make webhook handlers idempotent on event id
2.4 retries a webhook for up to 24 hours. A handler can receive the same event up to 8 times. Store the event id and skip a repeat.
Handlers that already return 200 within 10 seconds need no change beyond the dedupe.
- 3Send an Idempotency-Key on every refund
The SDK sets the header when you pass idempotencyKey. A call without it works, but each retry creates a new refund.
typescriptawait client.refunds.create({ chargeId: 'ch_9K2f', amount: 1500 }, { idempotencyKey: 'ref-9K2f-1' }); - 4Confirm invoice residency for EU accounts
Accounts with an EU billing address moved on 8 September. The invoice URL now starts with eu.files.example.com. No API change; update any allowlist that pins the old host.