Packages

Upgrading api-client from 2.x to 4.x

Two major versions shipped a day apart in August 2026: 3.0.0 (one order status instead of two) and 4.0.0 (payments that are still in flight). This page lists every breaking change between 2.18.0 and 4.3.0, taken from a diff of the two published packages.

The API moved, whichever client you run
Throttle serves one API version. Staying on 2.x does not keep the old behaviour — it keeps types that describe it. Three things are already true for a 2.x client today: postApiV1OrdersComplete returns 404, an order is never completed (it is fulfilled), and order responses carry no fulfillmentStatus. Everything else in 2.x keeps working.

Breaking changes that stop compiling

  • OrdersService.postApiV1OrdersComplete is gone. The endpoint (POST /orders/{id}/complete) was removed with the status it set. Use postApiV1OrdersStatus, which moves an order to any lifecycle status.
  • Order status: completed became fulfilled. The full set is now draft, pending, processing, partially_fulfilled, fulfilled, cancelled, closed. Stored orders were migrated; you will not receive completed from any client version.
  • fulfillmentStatus was removed from order responses and filters. Delivery progress is in status itself, with per-item counts in fulfillmentSummary. See Order states.
  • Status unions grew. Order paymentStatus gained processing, disputed and expired; payment status gained processing and expired. An exhaustive switch over either stops type-checking until it handles them.
  • The delivery-log eventType filter no longer accepts order.completed. See the event rename below.
Completing an order
// 2.x
await OrdersService.postApiV1OrdersComplete(orderId);

// 4.x
await OrdersService.postApiV1OrdersStatus(orderId, { status: 'fulfilled' });
The new payment statuses
switch (order.paymentStatus) {
  case 'captured':
  case 'partially_paid':
    // ...
    break;
  case 'processing': // new — neither approved nor declined yet. Do NOT treat as failed.
    break;
  case 'disputed':   // new — captured, then charged back
    break;
  case 'expired':    // new — the authorization lapsed before capture
    break;
  // ...
}
processing is not failed
Before 4.0.0 a payment the processor had not yet decided — a 3DS challenge in progress, a redirect the buyer had not returned from, a bank debit still settling — was reported as failed. If your code retries or re-charges on failed, make sure it does nothing on processing.

Breaking changes that still compile

The generated client takes positional arguments. Two list methods gained filters in the middle of their argument lists, so an existing positional call shifts by one or two places. TypeScript catches the shift where the types differ and misses it where both sides are string.

  • OrdersService.getApiV1Orders — delivery was inserted before source, and subscriptionId before createdAfter. A call that filtered by customerId now passes that id as the free-text search q, and its createdAfter as customerId. Neither is a type error.
  • WebhooksOutboundService.getApiV1WebhookDeliveries — status and eventType were inserted before limit and offset. TypeScript rejects a number where status is expected; plain JavaScript does not.
Listing orders
// 2.x argument order
//   (environmentId, cursor, limit, status, paymentStatus, type,
//    source, q, customerId, createdAfter, createdBefore)
//
// 4.x argument order — delivery and subscriptionId are new, and sit in the middle
//   (environmentId, cursor, limit, status, paymentStatus, type,
//    delivery, source, q, customerId, subscriptionId, createdAfter, createdBefore)

// This 2.x call still compiles on 4.x and asks a different question:
await OrdersService.getApiV1Orders(
  undefined, undefined, 50, undefined, undefined, undefined,
  undefined,        // 2.x: source        4.x: delivery
  undefined,        // 2.x: q             4.x: source
  customerId,       // 2.x: customerId    4.x: q            <- free-text search
  '2026-01-01',     // 2.x: createdAfter  4.x: customerId
);

// 4.x, same intent
await OrdersService.getApiV1Orders(
  undefined, undefined, 50, undefined, undefined, undefined,
  undefined, undefined, undefined,
  customerId,
  undefined,
  '2026-01-01',
);
Listing webhook deliveries
// 2.x: (environmentId, endpointId, orderId, limit, offset)
// 4.x: (environmentId, endpointId, orderId, status, eventType, limit, offset)

// 2.x
await WebhooksOutboundService.getApiV1WebhookDeliveries(undefined, endpointId, undefined, 25, 0);

// 4.x
await WebhooksOutboundService.getApiV1WebhookDeliveries(
  undefined, endpointId, undefined, undefined, undefined, 25, 0,
);

Every other method kept its argument order. Re-check these two by hand; a green build does not prove them.

order.completed became order.fulfilled

  • order.completed was renamed to order.fulfilled on 2026-08-22 and is no longer delivered. Stored webhook and extension subscriptions were rewritten to the new name.
  • order.completed is still accepted in enabledEvents when you create or update an endpoint, in 4.x as in 2.x, and is dropped from the saved list. That is deliberate: it keeps a read-modify-write of an old subscription list from failing. It is not a sign the event still fires.
  • order.fulfilled is not in the 2.x enabledEvents type, but the API accepts it from any client. On 2.x, cast the value; on 4.x it is in the union.
  • order.fulfilled and fulfillment.completed are different events — the first is about the order, the second about one fulfillment. Subscribe to whichever your handler is really about.

What you gain

  • Orders: postApiV1OrdersStatus, postApiV1OrdersHold / postApiV1OrdersHoldRelease, postApiV1OrdersComp / postApiV1OrdersCompReverse, getApiV1OrdersTimeline, getApiV1OrdersStatusHistory; a delivery filter (in_transit, delivered, returned) and a subscriptionId filter; the same filters on the CSV export.
  • Payments: postApiV1OrdersPaymentsRecord (record a payment taken outside Throttle, including cheque) and postApiV1PaymentsMarkFailed; payment responses declare errorCode and errorMessage.
  • Fulfillments: postApiV1FulfillmentsDelivered. Subscriptions: postApiV1SubscriptionsRetryCharge and postApiV1SubscriptionsWaivePeriod.
  • Webhooks: order.fulfilled, order.held, order.hold_released, order.comped, order.comp_reversed, payment.processing, payment.expired, payment.recorded; the delivery log filters by status and eventType.
  • Customers carry company; embed config carries checkoutLinkBaseUrl.

Upgrade checklist

  1. Search for postApiV1OrdersComplete, 'completed' and fulfillmentStatus. These are already wrong on 2.x, so fix them first — before or without the upgrade.
  2. Install 4.x and fix what the compiler reports: the removed method, the removed field, and any exhaustive switch over a payment status.
  3. Find every call to getApiV1Orders and getApiV1WebhookDeliveries and re-count the arguments against the lists above. The compiler will not find all of these for you.
  4. Replace order.completed with order.fulfilled in the event list you send, and in the handler that switches on the delivered type.
  5. Make sure nothing treats a processing payment as a failure.