@garuhq/node 2.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,97 @@
3
3
  All notable changes to `@garuhq/node` are documented in this file. Format:
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
5
5
 
6
+ ## [4.0.0] — 2026-08-22
7
+
8
+ **Breaking:** `scheduledCharges` now targets the versioned public API
9
+ `/api/v1/scheduled-charges`. `id` was already a stable, non-enumerable
10
+ string (`sch_...`) — no identifier change — but the list envelope shape
11
+ changed. If you use `garu.scheduledCharges.*`, read the migration below.
12
+
13
+ ### Breaking
14
+
15
+ - **`scheduledCharges` moved to `/api/v1/scheduled-charges`.**
16
+ - **`scheduledCharges.list()` and `.listAttempts()` now return
17
+ `{ data, count, totalCount, totalPages }`** (was `{ data, meta }`).
18
+ - No method signatures changed — every method already took/returned the
19
+ same shapes, since `id` was never a numeric internal id here.
20
+
21
+ ### Added
22
+
23
+ - `ScheduledChargeRecord` now explicitly types `recurrence`,
24
+ `cancelAtPeriodEnd`, `trialEndsAt`, and `subscriptionId` (previously only
25
+ reachable via the type's index signature, untyped).
26
+ - Test coverage for `cancelRecurrence`, `setCancelAtPeriodEnd`,
27
+ `changePaymentMethod`, `clearPaymentMethod`, and `listAttempts` — none of
28
+ these five methods had a single test before this release.
29
+
30
+ ### Fixed
31
+
32
+ - `ScheduledChargeLinkedTransaction.value`'s docstring claimed centavos;
33
+ `/api/v1/charges`' own mapper treats the same `transaction.value` column
34
+ as decimal reais with no conversion. Corrected to match reality — this is
35
+ a documentation fix, not a behavior or wire-format change.
36
+ - `scheduledCharges.create()`'s docstring claimed the SDK's
37
+ `X-Idempotency-Key` prevents duplicate creates on retry. The gateway does
38
+ not deduplicate `/scheduled-charges` creates against it (same pre-existing
39
+ gap as `webhookEvents.resend()` had). Corrected the docstring; the header
40
+ is still sent (harmless) in case the gateway adds this later.
41
+
42
+ ### Migration
43
+
44
+ ```ts
45
+ // before (≤ 3.x)
46
+ const { data, meta } = await garu.scheduledCharges.list({ status: 'overdue' });
47
+ meta.total; // number
48
+
49
+ // after (4.0.0)
50
+ const { data, totalCount } = await garu.scheduledCharges.list({ status: 'overdue' });
51
+ totalCount; // number
52
+ ```
53
+
54
+ ## [3.0.0] — 2026-08-22
55
+
56
+ **Breaking:** `webhookEvents` now targets the versioned public API
57
+ `/api/v1/webhook-events`, keyed on `uuid`. If you use `garu.webhookEvents.*`,
58
+ read the migration below.
59
+
60
+ ### Breaking
61
+
62
+ - **`webhookEvents` moved to `/api/v1/webhook-events`** and an event is
63
+ keyed by **`uuid`**, not a numeric `id`.
64
+ - `webhookEvents.get(id: number)` → **`webhookEvents.get(uuid: string)`**.
65
+ - `webhookEvents.retry(id)` / `webhookEvents.resend(id, params?)` — same
66
+ signature shape, but the id argument is now the `uuid`.
67
+ - `WebhookEvent.id` is **removed**; there is no numeric id in the public
68
+ shape. Use `WebhookEvent.uuid` everywhere. `WebhookEvent.endpointId` is
69
+ also removed — read `webhookEndpoint.id` instead (endpoint configuration
70
+ stays numeric; it did not move to `/api/v1`).
71
+ - `WebhookEvent.manualResendOf` is now a **`uuid` string** (was a numeric
72
+ id), pointing at the source event's `uuid`.
73
+ - **`webhookEvents.list()` returns `{ data, count, totalCount, totalPages }`**
74
+ (was `{ data, meta }`).
75
+ - The gateway's outbound `Idempotency-Key` for `/resend` clones is now
76
+ `resend_<uuid>` (was `resend_<numeric id>`), to match the public
77
+ identifier the SDK/CLI/MCP now expose.
78
+
79
+ ### Migration
80
+
81
+ ```ts
82
+ // before (0.x – 2.x)
83
+ const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
84
+ const event = failed.data[0];
85
+ event.id; // number
86
+ const clone = await garu.webhookEvents.resend(event.id);
87
+ clone.manualResendOf === event.id; // true
88
+
89
+ // after (3.0.0)
90
+ const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
91
+ const event = failed.data[0];
92
+ event.uuid; // string
93
+ const clone = await garu.webhookEvents.resend(event.uuid);
94
+ clone.manualResendOf === event.uuid; // true
95
+ ```
96
+
6
97
  ## [2.0.0] — 2026-08-22
7
98
 
8
99
  **Breaking:** `customers` now targets the versioned public API `/api/v1/customers`,
@@ -35,14 +126,14 @@ keyed on `uuid`. If you use `garu.customers.*`, read the migration below.
35
126
  ```ts
36
127
  // before (1.x)
37
128
  const c = await garu.customers.create({ name, email, document, phone, personType });
38
- c.id; // number
129
+ c.id; // number
39
130
  const one = await garu.customers.get(c.id);
40
131
  await garu.customers.update(c.id, { name: 'Maria Santos' });
41
132
  await garu.customers.delete(c.id);
42
133
 
43
134
  // after (2.0.0)
44
135
  const c = await garu.customers.create({ name, email, document, phone, personType });
45
- c.uuid; // string
136
+ c.uuid; // string
46
137
  const one = await garu.customers.get(c.uuid);
47
138
  await garu.customers.update(c.uuid, { name: 'Maria Santos' });
48
139
  const { removed } = await garu.customers.delete(c.uuid);
@@ -50,7 +141,6 @@ const { removed } = await garu.customers.delete(c.uuid);
50
141
 
51
142
  ## [1.1.0] — 2026-08-15
52
143
 
53
-
54
144
  ### Added
55
145
 
56
146
  - **`garu.installmentPlans` — boleto parcelado (carnê).** One product sold as N
@@ -127,22 +217,26 @@ webhook-events) changed.
127
217
  ```ts
128
218
  // before (0.16.x)
129
219
  const c = await garu.charges.create({
130
- productId, paymentMethod: 'credit_card', customer,
220
+ productId,
221
+ paymentMethod: 'credit_card',
222
+ customer,
131
223
  cardInfo: { cardNumber: '4111…', cvv, expirationDate, holderName, installments: 2 }
132
224
  });
133
- c.id; // number
134
- c.paymentMethodId; // 'creditcard'
225
+ c.id; // number
226
+ c.paymentMethodId; // 'creditcard'
135
227
  const one = await garu.charges.get(c.id);
136
228
  await garu.charges.refund(c.id, { amount: 1000 }); // "R$10,00" (bug: reais)
137
229
 
138
230
  // after (1.0.0)
139
231
  const c = await garu.charges.create({
140
- productId, paymentMethod: 'creditCard', customer,
232
+ productId,
233
+ paymentMethod: 'creditCard',
234
+ customer,
141
235
  card: { number: '4111…', cvv, expirationDate, holderName, installments: 2 }
142
236
  });
143
- c.uuid; // string
144
- c.paymentMethod; // 'creditCard'
145
- c.chargedTotal; // what was actually charged
237
+ c.uuid; // string
238
+ c.paymentMethod; // 'creditCard'
239
+ c.chargedTotal; // what was actually charged
146
240
  const one = await garu.charges.retrieve(c.uuid);
147
241
  await garu.charges.refund(c.uuid, { amount: 10.0 }); // R$10,00
148
242
  ```
package/README.md CHANGED
@@ -147,10 +147,13 @@ await garu.charges.refund('6f1c9b2e-…', { amount: 10.0 }); // partial refund (
147
147
  ## Customers
148
148
 
149
149
  Backed by `/api/v1/customers`, keyed on `uuid` — there is no numeric id in
150
- this shape. `installmentPlans.create` and `scheduledCharges.create` still
151
- link customers by the internal numeric id (unmigrated resources); fetch that
152
- id from the dashboard or the internal `/api/customers` endpoint until they
153
- move to `/api/v1` too.
150
+ this shape. `installmentPlans.create` still links customers by the internal
151
+ numeric id (unmigrated resource); fetch that id from the dashboard or the
152
+ internal `/api/customers` endpoint until it moves to `/api/v1` too.
153
+ `scheduledCharges.create` also takes the internal numeric `customerId`
154
+ customers and scheduled charges are on separate `/api/v1` resources, and
155
+ neither exposes a cross-reference between a customer's `uuid` and their
156
+ numeric id yet.
154
157
 
155
158
  | Method | Description |
156
159
  | --------------------------------------- | ----------------------------------------------- |
@@ -206,20 +209,25 @@ await garu.products.portalConfig.patch('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
206
209
 
207
210
  Bill an existing customer on a future date — one-time or recurring with card tokenization. The Garu drives email reminders, dunning, retries, and the lifecycle state machine.
208
211
 
209
- | Method | Description |
210
- | ------------------------------------ | --------------------------------------------------------------------------- |
211
- | `create(params)` | Create one-time or recurring schedule. Auto-attaches `X-Idempotency-Key`. |
212
- | `list(params?)` | Paginated list with status / type / dueFrom / dueTo / customerId filters. |
213
- | `get(id)` | Detail bundle: charge + event timeline + linked transactions. |
214
- | `chargeNow(id)` | Force-bill the current cycle now instead of waiting for the due date. |
215
- | `markPaid(id, params)` | Mark cycle paid (off-Garu reconciliation). |
216
- | `postpone(id, params)` | Move the next cycle's due date forward. |
217
- | `pause(id, params?)` / `resume(id)` | Suspend / re-enable a series. |
218
- | `cancelRecurrence(id, params?)` | Hard-stop future cycles (recurring only). |
219
- | `cancelAtPeriodEnd(id, { enabled })` | Stripe-style soft-cancel; reversible. |
220
- | `changePaymentMethod(id, params)` | Swap the saved card. |
221
- | `clearPaymentMethod(id)` | Remove the saved card; future cycles email-with-link. |
222
- | `listAttempts(id, params?)` | Per-attempt billing log every silent-charge / retry / mark-paid (v0.8.2). |
212
+ Backed by `/api/v1/scheduled-charges`. Unlike products/customers/webhook-events,
213
+ `id` here was already a stable, non-enumerable string (`sch_...`) before this
214
+ move there is no separate uuid and no id change, only the path and the list
215
+ envelope (`totalCount`/`totalPages`, not `meta.total`/`meta.totalPages`).
216
+
217
+ | Method | Description |
218
+ | --------------------------------------- | ------------------------------------------------------------------------- |
219
+ | `create(params)` | Create one-time or recurring schedule. |
220
+ | `list(params?)` | Paginated list with status / type / dueFrom / dueTo / customerId filters. |
221
+ | `get(id)` | Detail bundle: charge + event timeline + linked transactions. |
222
+ | `chargeNow(id)` | Force-bill the current cycle now instead of waiting for the due date. |
223
+ | `markPaid(id, params)` | Mark cycle paid (off-Garu reconciliation). |
224
+ | `postpone(id, params)` | Move the next cycle's due date forward. |
225
+ | `pause(id, params?)` / `resume(id)` | Suspend / re-enable a series. |
226
+ | `cancelRecurrence(id, params?)` | Hard-stop future cycles (recurring only). |
227
+ | `setCancelAtPeriodEnd(id, { enabled })` | Stripe-style soft-cancel; reversible. |
228
+ | `changePaymentMethod(id, params)` | Swap the saved card. |
229
+ | `clearPaymentMethod(id)` | Remove the saved card; future cycles email-with-link. |
230
+ | `listAttempts(id, params?)` | Per-attempt billing log — every silent-charge / retry / mark-paid. |
223
231
 
224
232
  ```ts
225
233
  // Recurring with 7-day trial. `maxRecoveryDays` caps how long past the due
@@ -240,11 +248,10 @@ const series = await garu.scheduledCharges.create({
240
248
  // Idempotent: a cycle already dispatched today reports `already_sent`.
241
249
  const result = await garu.scheduledCharges.chargeNow(series.id);
242
250
  if (result.outcome === 'failed') {
243
- // result.reason is e.g. 'card_expired' or a gateway decline code
244
- console.error(`${result.message} (${result.reason})`);
251
+ result.reason; // e.g. 'card_expired' or a gateway decline code
245
252
  }
246
253
 
247
- // Audit why cycle 3 failed (v0.8.2)
254
+ // Audit why cycle 3 failed
248
255
  const { data } = await garu.scheduledCharges.listAttempts(series.id, {
249
256
  cycleNumber: 3
250
257
  });
@@ -308,7 +315,7 @@ const series = await garu.scheduledCharges.create({
308
315
  });
309
316
  ```
310
317
 
311
- Cancel and the rest of the lifecycle use the **same methods** as card-backed series — `cancelRecurrence(id)`, `cancelAtPeriodEnd(id, { enabled })`, `pause(id)` / `resume(id)`. The customer can also revoke the authorization directly in their bank app; Garu surfaces that as a `subscription.cancelled` event.
318
+ Cancel and the rest of the lifecycle use the **same methods** as card-backed series — `cancelRecurrence(id)`, `setCancelAtPeriodEnd(id, { enabled })`, `pause(id)` / `resume(id)`. The customer can also revoke the authorization directly in their bank app; Garu surfaces that as a `subscription.cancelled` event.
312
319
 
313
320
  ### 3. Handle the webhooks
314
321
 
@@ -397,18 +404,19 @@ The seller-facing delivery log for outbound webhooks. Use it to audit deliveries
397
404
  const failed = await garu.webhookEvents.list({ status: 'failed', limit: 50 });
398
405
 
399
406
  // Inspect one event end-to-end
400
- const event = await garu.webhookEvents.get(42);
401
- console.log(event.responseStatus, event.responseBody);
407
+ const event = await garu.webhookEvents.get('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
408
+ event.responseStatus;
409
+ event.responseBody;
402
410
 
403
411
  // Audit-trail-preserving replay (recommended)
404
- const clone = await garu.webhookEvents.resend(42);
405
- clone.id !== event.id; // true — fresh row with its own id
406
- clone.manualResendOf === event.id; // true — points back at the source
412
+ const clone = await garu.webhookEvents.resend(event.uuid);
413
+ clone.uuid !== event.uuid; // true — fresh row with its own uuid
414
+ clone.manualResendOf === event.uuid; // true — points back at the source
407
415
  ```
408
416
 
409
- `resend(id)` is the audit-preserving counterpart to `retry(id)` — the backend inserts a fresh event whose `manualResendOf` points back at the source, then dispatches that clone. The original row stays exactly as it was, so the historical record of the prior failure (status, response status/body, attempts) survives. Works on any source status (`success` / `failed` / `pending`).
417
+ `resend(uuid)` is the audit-preserving counterpart to `retry(uuid)` — the backend inserts a fresh event whose `manualResendOf` points back at the source, then dispatches that clone. The original row stays exactly as it was, so the historical record of the prior failure (status, response status/body, attempts) survives. Works on any source status (`success` / `failed` / `pending`).
410
418
 
411
- Outbound deliveries of a resent event carry `Idempotency-Key: resend_<originalId>`, so recipient handlers can distinguish a resend from a fresh delivery both by the header prefix and by reading the response payload's `manualResendOf` field.
419
+ Outbound deliveries of a resent event carry `Idempotency-Key: resend_<cloneUuid>`, so recipient handlers can distinguish a resend from a fresh delivery both by the header prefix and by reading the response payload's `manualResendOf` field.
412
420
 
413
421
  > [!NOTE]
414
422
  > The SDK auto-attaches `X-Idempotency-Key` (UUIDv4) on `resend()` so transient transport retries can't create duplicate clones. Pass `{ idempotencyKey }` to dedupe across your own retry layer.
package/dist/index.cjs CHANGED
@@ -942,9 +942,13 @@ var ScheduledCharges = class {
942
942
  }
943
943
  http;
944
944
  /**
945
- * Create a new scheduled charge. Auto-attaches `X-Idempotency-Key`
946
- * (UUIDv4 if you don't pass `idempotencyKey`) so retries on transient
947
- * network failures don't silently double-create.
945
+ * Create a new scheduled charge. The SDK attaches an `X-Idempotency-Key`
946
+ * header (UUIDv4 unless you pass `idempotencyKey`), but the gateway does
947
+ * not currently deduplicate `/scheduled-charges` creates against it — a
948
+ * retry after a network failure can create more than one series. Pair
949
+ * this with your own retry-suppression (e.g. check `list` for an existing
950
+ * series with the same `externalReference`) if that matters for your
951
+ * integration.
948
952
  *
949
953
  * @example
950
954
  * const charge = await garu.scheduledCharges.create({
@@ -974,7 +978,7 @@ var ScheduledCharges = class {
974
978
  const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
975
979
  const { idempotencyKey: _omit, ...body } = params;
976
980
  return this.http.call(
977
- (signal) => this.http.client.POST("/api/scheduled-charges", {
981
+ (signal) => this.http.client.POST("/api/v1/scheduled-charges", {
978
982
  body,
979
983
  headers: { "X-Idempotency-Key": idempotencyKey },
980
984
  signal
@@ -1009,7 +1013,7 @@ var ScheduledCharges = class {
1009
1013
  for (const s of statuses) qs.append("status", s);
1010
1014
  }
1011
1015
  const query = qs.toString();
1012
- const url = `/api/scheduled-charges${query ? `?${query}` : ""}`;
1016
+ const url = `/api/v1/scheduled-charges${query ? `?${query}` : ""}`;
1013
1017
  return this.http.call(
1014
1018
  (signal) => this.http.client.GET(url, { signal }).then(
1015
1019
  (r) => r
@@ -1026,7 +1030,7 @@ var ScheduledCharges = class {
1026
1030
  */
1027
1031
  async get(id) {
1028
1032
  return this.http.call(
1029
- (signal) => this.http.client.GET(`/api/scheduled-charges/${encodeURIComponent(id)}`, {
1033
+ (signal) => this.http.client.GET(`/api/v1/scheduled-charges/${encodeURIComponent(id)}`, {
1030
1034
  signal
1031
1035
  }).then((r) => r)
1032
1036
  );
@@ -1045,7 +1049,7 @@ var ScheduledCharges = class {
1045
1049
  async postpone(id, params) {
1046
1050
  return this.http.call(
1047
1051
  (signal) => this.http.client.POST(
1048
- `/api/scheduled-charges/${encodeURIComponent(id)}/postpone`,
1052
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/postpone`,
1049
1053
  {
1050
1054
  body: params,
1051
1055
  signal
@@ -1064,7 +1068,7 @@ var ScheduledCharges = class {
1064
1068
  async pause(id, params = {}) {
1065
1069
  return this.http.call(
1066
1070
  (signal) => this.http.client.POST(
1067
- `/api/scheduled-charges/${encodeURIComponent(id)}/pause`,
1071
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/pause`,
1068
1072
  {
1069
1073
  body: params,
1070
1074
  signal
@@ -1081,7 +1085,7 @@ var ScheduledCharges = class {
1081
1085
  async resume(id) {
1082
1086
  return this.http.call(
1083
1087
  (signal) => this.http.client.POST(
1084
- `/api/scheduled-charges/${encodeURIComponent(id)}/resume`,
1088
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/resume`,
1085
1089
  {
1086
1090
  body: {},
1087
1091
  signal
@@ -1115,7 +1119,7 @@ var ScheduledCharges = class {
1115
1119
  async markPaid(id, params) {
1116
1120
  return this.http.call(
1117
1121
  (signal) => this.http.client.POST(
1118
- `/api/scheduled-charges/${encodeURIComponent(id)}/mark-paid`,
1122
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/mark-paid`,
1119
1123
  {
1120
1124
  body: params,
1121
1125
  signal
@@ -1137,24 +1141,22 @@ var ScheduledCharges = class {
1137
1141
  * const result = await garu.scheduledCharges.chargeNow('sch_abc123');
1138
1142
  * switch (result.outcome) {
1139
1143
  * case 'dispatched':
1140
- * console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
1144
+ * result.cycleNumber; // billed this cycle
1141
1145
  * break;
1142
1146
  * case 'already_sent':
1143
- * console.log('Já havia sido enviada — nada a fazer.');
1144
- * break;
1147
+ * break; // nothing to do
1145
1148
  * case 'failed':
1146
- * // result.reason is e.g. 'card_expired' or a gateway decline code
1147
- * console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
1149
+ * result.reason; // e.g. 'card_expired' or a gateway decline code
1148
1150
  * break;
1149
1151
  * case 'not_sent':
1150
- * console.warn(`Não enviada (${result.reason}): ${result.message}`);
1152
+ * result.reason;
1151
1153
  * break;
1152
1154
  * }
1153
1155
  */
1154
1156
  async chargeNow(id) {
1155
1157
  return this.http.call(
1156
1158
  (signal) => this.http.client.POST(
1157
- `/api/scheduled-charges/${encodeURIComponent(id)}/charge-now`,
1159
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/charge-now`,
1158
1160
  {
1159
1161
  body: {},
1160
1162
  signal
@@ -1176,7 +1178,7 @@ var ScheduledCharges = class {
1176
1178
  async cancelRecurrence(id, params = {}) {
1177
1179
  return this.http.call(
1178
1180
  (signal) => this.http.client.POST(
1179
- `/api/scheduled-charges/${encodeURIComponent(id)}/cancel-recurrence`,
1181
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/cancel-recurrence`,
1180
1182
  {
1181
1183
  body: params,
1182
1184
  signal
@@ -1196,7 +1198,7 @@ var ScheduledCharges = class {
1196
1198
  async setCancelAtPeriodEnd(id, params) {
1197
1199
  return this.http.call(
1198
1200
  (signal) => this.http.client.POST(
1199
- `/api/scheduled-charges/${encodeURIComponent(id)}/cancel-at-period-end`,
1201
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/cancel-at-period-end`,
1200
1202
  {
1201
1203
  body: params,
1202
1204
  signal
@@ -1215,7 +1217,7 @@ var ScheduledCharges = class {
1215
1217
  async changePaymentMethod(id, params) {
1216
1218
  return this.http.call(
1217
1219
  (signal) => this.http.client.POST(
1218
- `/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
1220
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
1219
1221
  {
1220
1222
  body: params,
1221
1223
  signal
@@ -1234,7 +1236,7 @@ var ScheduledCharges = class {
1234
1236
  async clearPaymentMethod(id) {
1235
1237
  return this.http.call(
1236
1238
  (signal) => this.http.client.DELETE(
1237
- `/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
1239
+ `/api/v1/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
1238
1240
  {
1239
1241
  body: {},
1240
1242
  signal
@@ -1261,7 +1263,7 @@ var ScheduledCharges = class {
1261
1263
  if (params.limit !== void 0) qs.set("limit", String(params.limit));
1262
1264
  if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
1263
1265
  const query = qs.toString();
1264
- const url = `/api/scheduled-charges/${encodeURIComponent(id)}/attempts${query ? `?${query}` : ""}`;
1266
+ const url = `/api/v1/scheduled-charges/${encodeURIComponent(id)}/attempts${query ? `?${query}` : ""}`;
1265
1267
  return this.http.call(
1266
1268
  (signal) => this.http.client.GET(url, { signal }).then(
1267
1269
  (r) => r
@@ -1293,42 +1295,31 @@ var WebhookEvents = class {
1293
1295
  * });
1294
1296
  */
1295
1297
  async list(params = {}) {
1296
- const qs = new URLSearchParams();
1297
- if (params.page !== void 0) qs.set("page", String(params.page));
1298
- if (params.limit !== void 0) qs.set("limit", String(params.limit));
1299
- if (params.status) qs.set("status", params.status);
1300
- if (params.eventType) qs.set("event_type", params.eventType);
1301
- if (params.endpointId !== void 0) qs.set("endpoint_id", String(params.endpointId));
1302
- const query = qs.toString();
1303
- const url = `/api/webhook-events${query ? `?${query}` : ""}`;
1304
- const raw = await this.http.call(
1298
+ const query = {};
1299
+ if (params.page !== void 0) query.page = String(params.page);
1300
+ if (params.limit !== void 0) query.limit = String(params.limit);
1301
+ if (params.status) query.status = params.status;
1302
+ if (params.eventType) query.eventType = params.eventType;
1303
+ if (params.endpointId !== void 0) query.endpointId = String(params.endpointId);
1304
+ const qs = new URLSearchParams(query).toString();
1305
+ const url = `/api/v1/webhook-events${qs ? `?${qs}` : ""}`;
1306
+ return this.http.call(
1305
1307
  (signal) => this.http.client.GET(url, { signal }).then(
1306
1308
  (r) => r
1307
1309
  )
1308
1310
  );
1309
- return {
1310
- data: raw.events,
1311
- meta: {
1312
- page: raw.page,
1313
- limit: raw.limit,
1314
- total: raw.total,
1315
- totalPages: raw.pages
1316
- }
1317
- };
1318
1311
  }
1319
1312
  /**
1320
- * Fetch one webhook event by numeric ID — includes the full payload, the
1313
+ * Fetch one webhook event by uuid — includes the full payload, the
1321
1314
  * embedded endpoint snapshot, and the most recent response status/body.
1322
1315
  *
1323
1316
  * @example
1324
- * const event = await garu.webhookEvents.get(42);
1325
- * if (event.status === 'failed') {
1326
- * console.log(event.responseStatus, event.responseBody);
1327
- * }
1317
+ * const event = await garu.webhookEvents.get('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
1318
+ * event.status === 'failed' && event.responseStatus;
1328
1319
  */
1329
- async get(id) {
1320
+ async get(uuid) {
1330
1321
  return this.http.call(
1331
- (signal) => this.http.client.GET(`/api/webhook-events/${id}`, { signal }).then(
1322
+ (signal) => this.http.client.GET(`/api/v1/webhook-events/${uuid}`, { signal }).then(
1332
1323
  (r) => r
1333
1324
  )
1334
1325
  );
@@ -1341,28 +1332,28 @@ var WebhookEvents = class {
1341
1332
  * explicitly want the legacy in-place semantics (and for backwards
1342
1333
  * compatibility with older CLI / MCP releases).
1343
1334
  *
1344
- * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
1335
+ * Re-deliver a webhook event by uuid. Resets it to `pending`, clears the
1345
1336
  * retry schedule, and triggers an immediate delivery attempt. Works on
1346
1337
  * any status (`success`, `failed`, `pending`).
1347
1338
  *
1348
1339
  * @example
1349
1340
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
1350
1341
  * for (const event of failed.data) {
1351
- * await garu.webhookEvents.retry(event.id);
1342
+ * await garu.webhookEvents.retry(event.uuid);
1352
1343
  * }
1353
1344
  */
1354
- async retry(id) {
1345
+ async retry(uuid) {
1355
1346
  return this.http.call(
1356
- (signal) => this.http.client.POST(`/api/webhook-events/${id}/retry`, {
1347
+ (signal) => this.http.client.POST(`/api/v1/webhook-events/${uuid}/retry`, {
1357
1348
  body: {},
1358
1349
  signal
1359
1350
  }).then((r) => r)
1360
1351
  );
1361
1352
  }
1362
1353
  /**
1363
- * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
1354
+ * Re-deliver a webhook event by uuid, audit-trail preserving. Unlike
1364
1355
  * {@link retry}, this does *not* mutate the original row — it inserts a
1365
- * fresh event (new numeric id) that points back at the source via
1356
+ * fresh event (new uuid) that points back at the source via
1366
1357
  * `manualResendOf`, then dispatches that clone. The original row is
1367
1358
  * untouched, so the historical record of the prior failure (and its
1368
1359
  * response status / body) is preserved.
@@ -1373,30 +1364,30 @@ var WebhookEvents = class {
1373
1364
  * delivery's outcome to remain on the record.
1374
1365
  *
1375
1366
  * **Outbound delivery semantics**: the gateway POSTs the clone with
1376
- * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
1377
- * of the source event, not the clone). Recipient handlers that key off
1367
+ * `Idempotency-Key: resend_<cloneUuid>`. Recipient handlers that key off
1378
1368
  * `Idempotency-Key` will see this as a distinct delivery from the
1379
1369
  * original — distinguishable both by the `resend_` prefix and by reading
1380
1370
  * the response payload's `manualResendOf` field.
1381
1371
  *
1382
- * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1383
- * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1384
- * retries (5xx SDK backoff) cannot create duplicate clones the
1385
- * backend returns the original clone on the second call within 24h.
1372
+ * The SDK also attaches an `X-Idempotency-Key` header (UUIDv4 unless you
1373
+ * pass `idempotencyKey`); the gateway does not currently deduplicate
1374
+ * `/resend` calls against it, so retrying this call from your own code
1375
+ * after a network failure can create more than one clone — pair it with
1376
+ * your own retry-suppression if that matters for your integration.
1386
1377
  *
1387
- * Returns the *clone* event (new id), not the original. The original is
1378
+ * Returns the *clone* event (new uuid), not the original. The original is
1388
1379
  * unchanged on the server.
1389
1380
  *
1390
1381
  * @example
1391
- * const event = await garu.webhookEvents.get(42);
1392
- * const clone = await garu.webhookEvents.resend(42);
1393
- * clone.id !== event.id; // true — clone has its own id
1394
- * clone.manualResendOf === event.id; // true — points back at the source
1382
+ * const event = await garu.webhookEvents.get('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
1383
+ * const clone = await garu.webhookEvents.resend(event.uuid);
1384
+ * clone.uuid !== event.uuid; // true — clone has its own uuid
1385
+ * clone.manualResendOf === event.uuid; // true — points back at the source
1395
1386
  */
1396
- async resend(id, params = {}) {
1387
+ async resend(uuid, params = {}) {
1397
1388
  const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
1398
1389
  return this.http.call(
1399
- (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
1390
+ (signal) => this.http.client.POST(`/api/v1/webhook-events/${uuid}/resend`, {
1400
1391
  body: {},
1401
1392
  headers: { "X-Idempotency-Key": idempotencyKey },
1402
1393
  signal