@garuhq/node 0.12.0 → 0.13.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,53 @@
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
+ ## [0.13.0] — 2026-05-25
7
+
8
+ ### Added
9
+
10
+ - `scheduledCharges.chargeNow(id)` — `POST /api/scheduled-charges/{id}/charge-now`.
11
+ Force-bills the current cycle right now instead of waiting for its due
12
+ date, running the same dispatch the daily billing cron would (customer
13
+ email/notification + outbound webhook + timeline event). Allowed only
14
+ from a billable status (`scheduled` / `due_today`); a recurring series
15
+ must have an open cycle. **Idempotent** — a cycle whose d-day was
16
+ already dispatched reports `already_sent` and does not re-charge.
17
+ Returns `{ outcome, cycleNumber, reason?, message }`:
18
+ - `outcome` is `'dispatched' | 'already_sent' | 'not_sent' | 'failed'`.
19
+ - `reason` (on `not_sent` / `failed`) is one of the documented literals
20
+ (`no_email`, `lock_lost`, `no_saved_payment_method`, `card_expired`,
21
+ `payment_method_missing`, `customer_missing`) or a raw gateway decline
22
+ code.
23
+ - `message` is a ready-to-show pt-BR string.
24
+ - `ChargeNowOutcome`, `ChargeNowReason`, and `ChargeNowResult` types
25
+ exported from the package root.
26
+ - `maxRecoveryDays?: number` (integer 1–365) on
27
+ `CreateScheduledChargeParams` — caps how many days past `dueDate` the
28
+ daily recovery sweep will still auto-bill a missed charge. Omit for the
29
+ system default (14). Also surfaced on the scheduled charge object as
30
+ `ScheduledChargeRecord.maxRecoveryDays: number | null`.
31
+
32
+ ## [0.12.1] — 2026-05-19
33
+
34
+ ### Fixed
35
+
36
+ - `webhookEvents.resend(id)` now auto-attaches `X-Idempotency-Key`
37
+ (UUIDv4) so transient transport retries (5xx → SDK backoff) cannot
38
+ create duplicate clones. Previously, a 503 mid-flight after the
39
+ backend had already committed the clone could trigger an SDK retry
40
+ and produce a second clone with a different id. With the
41
+ idempotency key in place, the backend returns the original clone on
42
+ the second call within 24h. Pass `{ idempotencyKey }` to dedupe
43
+ across your own retry layer.
44
+
45
+ ### Added
46
+
47
+ - `ResendWebhookEventParams` type exported from the package root —
48
+ the optional `{ idempotencyKey?: string }` for `resend()`.
49
+ - README quickstart entry for `webhookEvents`
50
+ (`list` / `get` / `resend` / `retry`), including the audit-trail
51
+ contract and the SDK→gateway idempotency note.
52
+
6
53
  ## [0.12.0] — 2026-05-19
7
54
 
8
55
  ### Added
package/README.md CHANGED
@@ -203,6 +203,7 @@ Bill an existing customer on a future date — one-time or recurring with card t
203
203
  | `create(params)` | Create one-time or recurring schedule. Auto-attaches `X-Idempotency-Key`. |
204
204
  | `list(params?)` | Paginated list with status / type / dueFrom / dueTo / customerId filters. |
205
205
  | `get(id)` | Detail bundle: charge + event timeline + linked transactions. |
206
+ | `chargeNow(id)` | Force-bill the current cycle now instead of waiting for the due date. |
206
207
  | `markPaid(id, params)` | Mark cycle paid (off-Garu reconciliation). |
207
208
  | `postpone(id, params)` | Move the next cycle's due date forward. |
208
209
  | `pause(id, params?)` / `resume(id)` | Suspend / re-enable a series. |
@@ -213,7 +214,8 @@ Bill an existing customer on a future date — one-time or recurring with card t
213
214
  | `listAttempts(id, params?)` | Per-attempt billing log — every silent-charge / retry / mark-paid (v0.8.2).|
214
215
 
215
216
  ```ts
216
- // Recurring with 7-day trial
217
+ // Recurring with 7-day trial. `maxRecoveryDays` caps how long past the due
218
+ // date the daily recovery sweep keeps auto-billing a missed charge (default 14).
217
219
  const series = await garu.scheduledCharges.create({
218
220
  customerId: 42,
219
221
  productId: 17,
@@ -223,8 +225,17 @@ const series = await garu.scheduledCharges.create({
223
225
  methods: ['card', 'pix'],
224
226
  recurrence: { interval: 'monthly' },
225
227
  trialDays: 7,
228
+ maxRecoveryDays: 30,
226
229
  });
227
230
 
231
+ // Force-bill the current cycle now instead of waiting for the due date.
232
+ // Idempotent: a cycle already dispatched today reports `already_sent`.
233
+ const result = await garu.scheduledCharges.chargeNow(series.id);
234
+ if (result.outcome === 'failed') {
235
+ // result.reason is e.g. 'card_expired' or a gateway decline code
236
+ console.error(`${result.message} (${result.reason})`);
237
+ }
238
+
228
239
  // Audit why cycle 3 failed (v0.8.2)
229
240
  const { data } = await garu.scheduledCharges.listAttempts(series.id, {
230
241
  cycleNumber: 3,
@@ -292,6 +303,38 @@ app.post('/webhooks/garu', express.raw({ type: 'application/json' }), (req, res)
292
303
  > [!IMPORTANT]
293
304
  > Always pass the raw request body to `verify()`. Parsing and re-serializing JSON will break the signature check.
294
305
 
306
+ ## Webhook events
307
+
308
+ The seller-facing delivery log for outbound webhooks. Use it to audit deliveries, surface failures, and replay events when a customer's endpoint missed one. Webhook endpoint *configuration* (URL, subscribed events, secret) is still dashboard-only — this resource only covers the event log + manual retries.
309
+
310
+ ```ts
311
+ // Surface anything that didn't make it through
312
+ const failed = await garu.webhookEvents.list({ status: 'failed', limit: 50 });
313
+
314
+ // Inspect one event end-to-end
315
+ const event = await garu.webhookEvents.get(42);
316
+ console.log(event.responseStatus, event.responseBody);
317
+
318
+ // Audit-trail-preserving replay (recommended)
319
+ const clone = await garu.webhookEvents.resend(42);
320
+ clone.id !== event.id; // true — fresh row with its own id
321
+ clone.manualResendOf === event.id; // true — points back at the source
322
+ ```
323
+
324
+ `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`).
325
+
326
+ 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.
327
+
328
+ > [!NOTE]
329
+ > 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.
330
+
331
+ | Method | Purpose |
332
+ | ------------------------------- | ---------------------------------------------------------------------------------- |
333
+ | `list(params?)` | Paginated event log. Filter by `status`, `eventType`, `endpointId`. Newest first. |
334
+ | `get(id)` | One event — full payload, endpoint snapshot, most recent response. |
335
+ | `resend(id, params?)` | Clone-on-resend. Returns the new event; original is untouched. **Preferred.** |
336
+ | `retry(id)` | Legacy in-place reset (mutates the original row). Soft-deprecated. |
337
+
295
338
  ## Error handling
296
339
 
297
340
  Every error extends `GaruError`. API errors include `status`, `requestId`, and `body`.
package/dist/index.cjs CHANGED
@@ -653,9 +653,9 @@ var ScheduledCharges = class {
653
653
  */
654
654
  async get(id) {
655
655
  return this.http.call(
656
- (signal) => this.http.client.GET(`/api/scheduled-charges/${id}`, { signal }).then(
657
- (r) => r
658
- )
656
+ (signal) => this.http.client.GET(`/api/scheduled-charges/${encodeURIComponent(id)}`, {
657
+ signal
658
+ }).then((r) => r)
659
659
  );
660
660
  }
661
661
  /**
@@ -671,10 +671,13 @@ var ScheduledCharges = class {
671
671
  */
672
672
  async postpone(id, params) {
673
673
  return this.http.call(
674
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/postpone`, {
675
- body: params,
676
- signal
677
- }).then((r) => r)
674
+ (signal) => this.http.client.POST(
675
+ `/api/scheduled-charges/${encodeURIComponent(id)}/postpone`,
676
+ {
677
+ body: params,
678
+ signal
679
+ }
680
+ ).then((r) => r)
678
681
  );
679
682
  }
680
683
  /**
@@ -687,10 +690,13 @@ var ScheduledCharges = class {
687
690
  */
688
691
  async pause(id, params = {}) {
689
692
  return this.http.call(
690
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/pause`, {
691
- body: params,
692
- signal
693
- }).then((r) => r)
693
+ (signal) => this.http.client.POST(
694
+ `/api/scheduled-charges/${encodeURIComponent(id)}/pause`,
695
+ {
696
+ body: params,
697
+ signal
698
+ }
699
+ ).then((r) => r)
694
700
  );
695
701
  }
696
702
  /**
@@ -701,10 +707,13 @@ var ScheduledCharges = class {
701
707
  */
702
708
  async resume(id) {
703
709
  return this.http.call(
704
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/resume`, {
705
- body: {},
706
- signal
707
- }).then((r) => r)
710
+ (signal) => this.http.client.POST(
711
+ `/api/scheduled-charges/${encodeURIComponent(id)}/resume`,
712
+ {
713
+ body: {},
714
+ signal
715
+ }
716
+ ).then((r) => r)
708
717
  );
709
718
  }
710
719
  /**
@@ -732,10 +741,52 @@ var ScheduledCharges = class {
732
741
  */
733
742
  async markPaid(id, params) {
734
743
  return this.http.call(
735
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/mark-paid`, {
736
- body: params,
737
- signal
738
- }).then((r) => r)
744
+ (signal) => this.http.client.POST(
745
+ `/api/scheduled-charges/${encodeURIComponent(id)}/mark-paid`,
746
+ {
747
+ body: params,
748
+ signal
749
+ }
750
+ ).then((r) => r)
751
+ );
752
+ }
753
+ /**
754
+ * Force-bill the current cycle right now instead of waiting for its due
755
+ * date. Runs the same dispatch the daily billing cron would (customer
756
+ * email/notification + outbound webhook + timeline event). Allowed only
757
+ * from a billable status (`scheduled` / `due_today`); a recurring series
758
+ * must have an open cycle (else the backend returns 400).
759
+ *
760
+ * Idempotent: if this cycle's d-day was already dispatched it reports
761
+ * `already_sent` and does not re-charge. Inspect `outcome` to branch.
762
+ *
763
+ * @example
764
+ * const result = await garu.scheduledCharges.chargeNow('sch_abc123');
765
+ * switch (result.outcome) {
766
+ * case 'dispatched':
767
+ * console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
768
+ * break;
769
+ * case 'already_sent':
770
+ * console.log('Já havia sido enviada — nada a fazer.');
771
+ * break;
772
+ * case 'failed':
773
+ * // result.reason is e.g. 'card_expired' or a gateway decline code
774
+ * console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
775
+ * break;
776
+ * case 'not_sent':
777
+ * console.warn(`Não enviada (${result.reason}): ${result.message}`);
778
+ * break;
779
+ * }
780
+ */
781
+ async chargeNow(id) {
782
+ return this.http.call(
783
+ (signal) => this.http.client.POST(
784
+ `/api/scheduled-charges/${encodeURIComponent(id)}/charge-now`,
785
+ {
786
+ body: {},
787
+ signal
788
+ }
789
+ ).then((r) => r)
739
790
  );
740
791
  }
741
792
  /**
@@ -751,10 +802,13 @@ var ScheduledCharges = class {
751
802
  */
752
803
  async cancelRecurrence(id, params = {}) {
753
804
  return this.http.call(
754
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/cancel-recurrence`, {
755
- body: params,
756
- signal
757
- }).then((r) => r)
805
+ (signal) => this.http.client.POST(
806
+ `/api/scheduled-charges/${encodeURIComponent(id)}/cancel-recurrence`,
807
+ {
808
+ body: params,
809
+ signal
810
+ }
811
+ ).then((r) => r)
758
812
  );
759
813
  }
760
814
  /**
@@ -768,10 +822,13 @@ var ScheduledCharges = class {
768
822
  */
769
823
  async setCancelAtPeriodEnd(id, params) {
770
824
  return this.http.call(
771
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/cancel-at-period-end`, {
772
- body: params,
773
- signal
774
- }).then((r) => r)
825
+ (signal) => this.http.client.POST(
826
+ `/api/scheduled-charges/${encodeURIComponent(id)}/cancel-at-period-end`,
827
+ {
828
+ body: params,
829
+ signal
830
+ }
831
+ ).then((r) => r)
775
832
  );
776
833
  }
777
834
  /**
@@ -784,10 +841,13 @@ var ScheduledCharges = class {
784
841
  */
785
842
  async changePaymentMethod(id, params) {
786
843
  return this.http.call(
787
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/payment-method`, {
788
- body: params,
789
- signal
790
- }).then((r) => r)
844
+ (signal) => this.http.client.POST(
845
+ `/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
846
+ {
847
+ body: params,
848
+ signal
849
+ }
850
+ ).then((r) => r)
791
851
  );
792
852
  }
793
853
  /**
@@ -800,10 +860,13 @@ var ScheduledCharges = class {
800
860
  */
801
861
  async clearPaymentMethod(id) {
802
862
  return this.http.call(
803
- (signal) => this.http.client.DELETE(`/api/scheduled-charges/${id}/payment-method`, {
804
- body: {},
805
- signal
806
- }).then((r) => r)
863
+ (signal) => this.http.client.DELETE(
864
+ `/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
865
+ {
866
+ body: {},
867
+ signal
868
+ }
869
+ ).then((r) => r)
807
870
  );
808
871
  }
809
872
  /**
@@ -825,7 +888,7 @@ var ScheduledCharges = class {
825
888
  if (params.limit !== void 0) qs.set("limit", String(params.limit));
826
889
  if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
827
890
  const query = qs.toString();
828
- const url = `/api/scheduled-charges/${id}/attempts${query ? `?${query}` : ""}`;
891
+ const url = `/api/scheduled-charges/${encodeURIComponent(id)}/attempts${query ? `?${query}` : ""}`;
829
892
  return this.http.call(
830
893
  (signal) => this.http.client.GET(url, { signal }).then(
831
894
  (r) => r
@@ -943,6 +1006,11 @@ var WebhookEvents = class {
943
1006
  * original — distinguishable both by the `resend_` prefix and by reading
944
1007
  * the response payload's `manualResendOf` field.
945
1008
  *
1009
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1010
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1011
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
1012
+ * backend returns the original clone on the second call within 24h.
1013
+ *
946
1014
  * Returns the *clone* event (new id), not the original. The original is
947
1015
  * unchanged on the server.
948
1016
  *
@@ -952,10 +1020,12 @@ var WebhookEvents = class {
952
1020
  * clone.id !== event.id; // true — clone has its own id
953
1021
  * clone.manualResendOf === event.id; // true — points back at the source
954
1022
  */
955
- async resend(id) {
1023
+ async resend(id, params = {}) {
1024
+ const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
956
1025
  return this.http.call(
957
1026
  (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
958
1027
  body: {},
1028
+ headers: { "X-Idempotency-Key": idempotencyKey },
959
1029
  signal
960
1030
  }).then((r) => r)
961
1031
  );
package/dist/index.d.cts CHANGED
@@ -312,6 +312,11 @@ interface ScheduledChargeRecord {
312
312
  methods: ScheduledPaymentMethod[];
313
313
  status: ScheduledChargeStatus;
314
314
  externalReference: string | null;
315
+ /**
316
+ * Max days past `dueDate` the daily recovery sweep will still auto-bill a
317
+ * missed charge. `null` means the system default (14) applies.
318
+ */
319
+ maxRecoveryDays: number | null;
315
320
  metadata: Record<string, unknown> | null;
316
321
  createdAt: string;
317
322
  updatedAt: string;
@@ -407,6 +412,11 @@ interface CreateScheduledChargeParams {
407
412
  trialDays?: number;
408
413
  externalReference?: string;
409
414
  metadata?: Record<string, unknown>;
415
+ /**
416
+ * Max days past `dueDate` the daily recovery sweep will still auto-bill a
417
+ * missed charge (integer 1..365). Omit for the system default (14).
418
+ */
419
+ maxRecoveryDays?: number;
410
420
  /**
411
421
  * Optional idempotency key for safe retries. The SDK auto-generates a
412
422
  * UUIDv4 when omitted and forwards it as `X-Idempotency-Key`.
@@ -456,6 +466,30 @@ interface ChangePaymentMethodScheduledChargeParams {
456
466
  /** PaymentMethod id to bind. Must belong to the same customerId. */
457
467
  paymentMethodId: number;
458
468
  }
469
+ /**
470
+ * Result of `scheduledCharges.chargeNow(id)` — what the immediate dispatch did:
471
+ *
472
+ * - `dispatched` — sent now (customer email/notification + outbound webhook + timeline event).
473
+ * - `already_sent` — this cycle's d-day was already dispatched; no-op (the action is idempotent).
474
+ * - `not_sent` — couldn't send; see `reason` (e.g. `no_email`, `lock_lost`, `no_saved_payment_method`).
475
+ * - `failed` — card charge failed; see `reason` (e.g. `card_expired`, or a gateway decline code).
476
+ */
477
+ type ChargeNowOutcome = 'dispatched' | 'already_sent' | 'not_sent' | 'failed';
478
+ /**
479
+ * Why a `not_sent` / `failed` charge-now didn't go through. The documented
480
+ * literals are stable; `failed` may also surface a raw gateway decline code,
481
+ * so the type stays open (`string & {}`) without losing autocomplete.
482
+ */
483
+ type ChargeNowReason = 'no_email' | 'lock_lost' | 'no_saved_payment_method' | 'card_expired' | 'payment_method_missing' | 'customer_missing' | (string & {});
484
+ interface ChargeNowResult {
485
+ outcome: ChargeNowOutcome;
486
+ /** Cycle that was dispatched/attempted, or `null` for one-time charges. */
487
+ cycleNumber: number | null;
488
+ /** Present on `not_sent` / `failed`. See {@link ChargeNowReason}. */
489
+ reason?: ChargeNowReason;
490
+ /** Ready-to-show pt-BR message describing the outcome. */
491
+ message: string;
492
+ }
459
493
  interface Product {
460
494
  id: number;
461
495
  uuid: string;
@@ -618,6 +652,16 @@ interface ListWebhookEventsParams {
618
652
  /** Filter by the destination endpoint that should receive (or received) the event. */
619
653
  endpointId?: number;
620
654
  }
655
+ interface ResendWebhookEventParams {
656
+ /**
657
+ * SDK→gateway idempotency key. If omitted, the SDK generates a UUIDv4
658
+ * and forwards it as `X-Idempotency-Key`. Within 24h the backend
659
+ * returns the original clone instead of creating a new one — pass a
660
+ * stable key from your own retry layer to dedupe across SDK
661
+ * invocations.
662
+ */
663
+ idempotencyKey?: string;
664
+ }
621
665
  /**
622
666
  * Per-product portal customization (Atletia coach-as-product modeling and
623
667
  * any other B2B2C platform). `null` fields inherit from the seller-level
@@ -1025,6 +1069,35 @@ declare class ScheduledCharges {
1025
1069
  * });
1026
1070
  */
1027
1071
  markPaid(id: string, params: MarkPaidScheduledChargeParams): Promise<ScheduledChargeRecord>;
1072
+ /**
1073
+ * Force-bill the current cycle right now instead of waiting for its due
1074
+ * date. Runs the same dispatch the daily billing cron would (customer
1075
+ * email/notification + outbound webhook + timeline event). Allowed only
1076
+ * from a billable status (`scheduled` / `due_today`); a recurring series
1077
+ * must have an open cycle (else the backend returns 400).
1078
+ *
1079
+ * Idempotent: if this cycle's d-day was already dispatched it reports
1080
+ * `already_sent` and does not re-charge. Inspect `outcome` to branch.
1081
+ *
1082
+ * @example
1083
+ * const result = await garu.scheduledCharges.chargeNow('sch_abc123');
1084
+ * switch (result.outcome) {
1085
+ * case 'dispatched':
1086
+ * console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
1087
+ * break;
1088
+ * case 'already_sent':
1089
+ * console.log('Já havia sido enviada — nada a fazer.');
1090
+ * break;
1091
+ * case 'failed':
1092
+ * // result.reason is e.g. 'card_expired' or a gateway decline code
1093
+ * console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
1094
+ * break;
1095
+ * case 'not_sent':
1096
+ * console.warn(`Não enviada (${result.reason}): ${result.message}`);
1097
+ * break;
1098
+ * }
1099
+ */
1100
+ chargeNow(id: string): Promise<ChargeNowResult>;
1028
1101
  /**
1029
1102
  * Stop future cycles for a recurring series. The currently in-flight
1030
1103
  * cycle (if any) remains active until paid, postponed, or marked-paid;
@@ -1163,6 +1236,11 @@ declare class WebhookEvents {
1163
1236
  * original — distinguishable both by the `resend_` prefix and by reading
1164
1237
  * the response payload's `manualResendOf` field.
1165
1238
  *
1239
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1240
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1241
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
1242
+ * backend returns the original clone on the second call within 24h.
1243
+ *
1166
1244
  * Returns the *clone* event (new id), not the original. The original is
1167
1245
  * unchanged on the server.
1168
1246
  *
@@ -1172,7 +1250,7 @@ declare class WebhookEvents {
1172
1250
  * clone.id !== event.id; // true — clone has its own id
1173
1251
  * clone.manualResendOf === event.id; // true — points back at the source
1174
1252
  */
1175
- resend(id: number): Promise<WebhookEvent>;
1253
+ resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
1176
1254
  }
1177
1255
 
1178
1256
  interface GaruOptions {
@@ -1274,4 +1352,4 @@ declare class GaruServerError extends GaruAPIError {
1274
1352
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1275
1353
  }
1276
1354
 
1277
- export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
1355
+ export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
package/dist/index.d.ts CHANGED
@@ -312,6 +312,11 @@ interface ScheduledChargeRecord {
312
312
  methods: ScheduledPaymentMethod[];
313
313
  status: ScheduledChargeStatus;
314
314
  externalReference: string | null;
315
+ /**
316
+ * Max days past `dueDate` the daily recovery sweep will still auto-bill a
317
+ * missed charge. `null` means the system default (14) applies.
318
+ */
319
+ maxRecoveryDays: number | null;
315
320
  metadata: Record<string, unknown> | null;
316
321
  createdAt: string;
317
322
  updatedAt: string;
@@ -407,6 +412,11 @@ interface CreateScheduledChargeParams {
407
412
  trialDays?: number;
408
413
  externalReference?: string;
409
414
  metadata?: Record<string, unknown>;
415
+ /**
416
+ * Max days past `dueDate` the daily recovery sweep will still auto-bill a
417
+ * missed charge (integer 1..365). Omit for the system default (14).
418
+ */
419
+ maxRecoveryDays?: number;
410
420
  /**
411
421
  * Optional idempotency key for safe retries. The SDK auto-generates a
412
422
  * UUIDv4 when omitted and forwards it as `X-Idempotency-Key`.
@@ -456,6 +466,30 @@ interface ChangePaymentMethodScheduledChargeParams {
456
466
  /** PaymentMethod id to bind. Must belong to the same customerId. */
457
467
  paymentMethodId: number;
458
468
  }
469
+ /**
470
+ * Result of `scheduledCharges.chargeNow(id)` — what the immediate dispatch did:
471
+ *
472
+ * - `dispatched` — sent now (customer email/notification + outbound webhook + timeline event).
473
+ * - `already_sent` — this cycle's d-day was already dispatched; no-op (the action is idempotent).
474
+ * - `not_sent` — couldn't send; see `reason` (e.g. `no_email`, `lock_lost`, `no_saved_payment_method`).
475
+ * - `failed` — card charge failed; see `reason` (e.g. `card_expired`, or a gateway decline code).
476
+ */
477
+ type ChargeNowOutcome = 'dispatched' | 'already_sent' | 'not_sent' | 'failed';
478
+ /**
479
+ * Why a `not_sent` / `failed` charge-now didn't go through. The documented
480
+ * literals are stable; `failed` may also surface a raw gateway decline code,
481
+ * so the type stays open (`string & {}`) without losing autocomplete.
482
+ */
483
+ type ChargeNowReason = 'no_email' | 'lock_lost' | 'no_saved_payment_method' | 'card_expired' | 'payment_method_missing' | 'customer_missing' | (string & {});
484
+ interface ChargeNowResult {
485
+ outcome: ChargeNowOutcome;
486
+ /** Cycle that was dispatched/attempted, or `null` for one-time charges. */
487
+ cycleNumber: number | null;
488
+ /** Present on `not_sent` / `failed`. See {@link ChargeNowReason}. */
489
+ reason?: ChargeNowReason;
490
+ /** Ready-to-show pt-BR message describing the outcome. */
491
+ message: string;
492
+ }
459
493
  interface Product {
460
494
  id: number;
461
495
  uuid: string;
@@ -618,6 +652,16 @@ interface ListWebhookEventsParams {
618
652
  /** Filter by the destination endpoint that should receive (or received) the event. */
619
653
  endpointId?: number;
620
654
  }
655
+ interface ResendWebhookEventParams {
656
+ /**
657
+ * SDK→gateway idempotency key. If omitted, the SDK generates a UUIDv4
658
+ * and forwards it as `X-Idempotency-Key`. Within 24h the backend
659
+ * returns the original clone instead of creating a new one — pass a
660
+ * stable key from your own retry layer to dedupe across SDK
661
+ * invocations.
662
+ */
663
+ idempotencyKey?: string;
664
+ }
621
665
  /**
622
666
  * Per-product portal customization (Atletia coach-as-product modeling and
623
667
  * any other B2B2C platform). `null` fields inherit from the seller-level
@@ -1025,6 +1069,35 @@ declare class ScheduledCharges {
1025
1069
  * });
1026
1070
  */
1027
1071
  markPaid(id: string, params: MarkPaidScheduledChargeParams): Promise<ScheduledChargeRecord>;
1072
+ /**
1073
+ * Force-bill the current cycle right now instead of waiting for its due
1074
+ * date. Runs the same dispatch the daily billing cron would (customer
1075
+ * email/notification + outbound webhook + timeline event). Allowed only
1076
+ * from a billable status (`scheduled` / `due_today`); a recurring series
1077
+ * must have an open cycle (else the backend returns 400).
1078
+ *
1079
+ * Idempotent: if this cycle's d-day was already dispatched it reports
1080
+ * `already_sent` and does not re-charge. Inspect `outcome` to branch.
1081
+ *
1082
+ * @example
1083
+ * const result = await garu.scheduledCharges.chargeNow('sch_abc123');
1084
+ * switch (result.outcome) {
1085
+ * case 'dispatched':
1086
+ * console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
1087
+ * break;
1088
+ * case 'already_sent':
1089
+ * console.log('Já havia sido enviada — nada a fazer.');
1090
+ * break;
1091
+ * case 'failed':
1092
+ * // result.reason is e.g. 'card_expired' or a gateway decline code
1093
+ * console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
1094
+ * break;
1095
+ * case 'not_sent':
1096
+ * console.warn(`Não enviada (${result.reason}): ${result.message}`);
1097
+ * break;
1098
+ * }
1099
+ */
1100
+ chargeNow(id: string): Promise<ChargeNowResult>;
1028
1101
  /**
1029
1102
  * Stop future cycles for a recurring series. The currently in-flight
1030
1103
  * cycle (if any) remains active until paid, postponed, or marked-paid;
@@ -1163,6 +1236,11 @@ declare class WebhookEvents {
1163
1236
  * original — distinguishable both by the `resend_` prefix and by reading
1164
1237
  * the response payload's `manualResendOf` field.
1165
1238
  *
1239
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1240
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1241
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
1242
+ * backend returns the original clone on the second call within 24h.
1243
+ *
1166
1244
  * Returns the *clone* event (new id), not the original. The original is
1167
1245
  * unchanged on the server.
1168
1246
  *
@@ -1172,7 +1250,7 @@ declare class WebhookEvents {
1172
1250
  * clone.id !== event.id; // true — clone has its own id
1173
1251
  * clone.manualResendOf === event.id; // true — points back at the source
1174
1252
  */
1175
- resend(id: number): Promise<WebhookEvent>;
1253
+ resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
1176
1254
  }
1177
1255
 
1178
1256
  interface GaruOptions {
@@ -1274,4 +1352,4 @@ declare class GaruServerError extends GaruAPIError {
1274
1352
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1275
1353
  }
1276
1354
 
1277
- export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
1355
+ export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
package/dist/index.js CHANGED
@@ -647,9 +647,9 @@ var ScheduledCharges = class {
647
647
  */
648
648
  async get(id) {
649
649
  return this.http.call(
650
- (signal) => this.http.client.GET(`/api/scheduled-charges/${id}`, { signal }).then(
651
- (r) => r
652
- )
650
+ (signal) => this.http.client.GET(`/api/scheduled-charges/${encodeURIComponent(id)}`, {
651
+ signal
652
+ }).then((r) => r)
653
653
  );
654
654
  }
655
655
  /**
@@ -665,10 +665,13 @@ var ScheduledCharges = class {
665
665
  */
666
666
  async postpone(id, params) {
667
667
  return this.http.call(
668
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/postpone`, {
669
- body: params,
670
- signal
671
- }).then((r) => r)
668
+ (signal) => this.http.client.POST(
669
+ `/api/scheduled-charges/${encodeURIComponent(id)}/postpone`,
670
+ {
671
+ body: params,
672
+ signal
673
+ }
674
+ ).then((r) => r)
672
675
  );
673
676
  }
674
677
  /**
@@ -681,10 +684,13 @@ var ScheduledCharges = class {
681
684
  */
682
685
  async pause(id, params = {}) {
683
686
  return this.http.call(
684
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/pause`, {
685
- body: params,
686
- signal
687
- }).then((r) => r)
687
+ (signal) => this.http.client.POST(
688
+ `/api/scheduled-charges/${encodeURIComponent(id)}/pause`,
689
+ {
690
+ body: params,
691
+ signal
692
+ }
693
+ ).then((r) => r)
688
694
  );
689
695
  }
690
696
  /**
@@ -695,10 +701,13 @@ var ScheduledCharges = class {
695
701
  */
696
702
  async resume(id) {
697
703
  return this.http.call(
698
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/resume`, {
699
- body: {},
700
- signal
701
- }).then((r) => r)
704
+ (signal) => this.http.client.POST(
705
+ `/api/scheduled-charges/${encodeURIComponent(id)}/resume`,
706
+ {
707
+ body: {},
708
+ signal
709
+ }
710
+ ).then((r) => r)
702
711
  );
703
712
  }
704
713
  /**
@@ -726,10 +735,52 @@ var ScheduledCharges = class {
726
735
  */
727
736
  async markPaid(id, params) {
728
737
  return this.http.call(
729
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/mark-paid`, {
730
- body: params,
731
- signal
732
- }).then((r) => r)
738
+ (signal) => this.http.client.POST(
739
+ `/api/scheduled-charges/${encodeURIComponent(id)}/mark-paid`,
740
+ {
741
+ body: params,
742
+ signal
743
+ }
744
+ ).then((r) => r)
745
+ );
746
+ }
747
+ /**
748
+ * Force-bill the current cycle right now instead of waiting for its due
749
+ * date. Runs the same dispatch the daily billing cron would (customer
750
+ * email/notification + outbound webhook + timeline event). Allowed only
751
+ * from a billable status (`scheduled` / `due_today`); a recurring series
752
+ * must have an open cycle (else the backend returns 400).
753
+ *
754
+ * Idempotent: if this cycle's d-day was already dispatched it reports
755
+ * `already_sent` and does not re-charge. Inspect `outcome` to branch.
756
+ *
757
+ * @example
758
+ * const result = await garu.scheduledCharges.chargeNow('sch_abc123');
759
+ * switch (result.outcome) {
760
+ * case 'dispatched':
761
+ * console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
762
+ * break;
763
+ * case 'already_sent':
764
+ * console.log('Já havia sido enviada — nada a fazer.');
765
+ * break;
766
+ * case 'failed':
767
+ * // result.reason is e.g. 'card_expired' or a gateway decline code
768
+ * console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
769
+ * break;
770
+ * case 'not_sent':
771
+ * console.warn(`Não enviada (${result.reason}): ${result.message}`);
772
+ * break;
773
+ * }
774
+ */
775
+ async chargeNow(id) {
776
+ return this.http.call(
777
+ (signal) => this.http.client.POST(
778
+ `/api/scheduled-charges/${encodeURIComponent(id)}/charge-now`,
779
+ {
780
+ body: {},
781
+ signal
782
+ }
783
+ ).then((r) => r)
733
784
  );
734
785
  }
735
786
  /**
@@ -745,10 +796,13 @@ var ScheduledCharges = class {
745
796
  */
746
797
  async cancelRecurrence(id, params = {}) {
747
798
  return this.http.call(
748
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/cancel-recurrence`, {
749
- body: params,
750
- signal
751
- }).then((r) => r)
799
+ (signal) => this.http.client.POST(
800
+ `/api/scheduled-charges/${encodeURIComponent(id)}/cancel-recurrence`,
801
+ {
802
+ body: params,
803
+ signal
804
+ }
805
+ ).then((r) => r)
752
806
  );
753
807
  }
754
808
  /**
@@ -762,10 +816,13 @@ var ScheduledCharges = class {
762
816
  */
763
817
  async setCancelAtPeriodEnd(id, params) {
764
818
  return this.http.call(
765
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/cancel-at-period-end`, {
766
- body: params,
767
- signal
768
- }).then((r) => r)
819
+ (signal) => this.http.client.POST(
820
+ `/api/scheduled-charges/${encodeURIComponent(id)}/cancel-at-period-end`,
821
+ {
822
+ body: params,
823
+ signal
824
+ }
825
+ ).then((r) => r)
769
826
  );
770
827
  }
771
828
  /**
@@ -778,10 +835,13 @@ var ScheduledCharges = class {
778
835
  */
779
836
  async changePaymentMethod(id, params) {
780
837
  return this.http.call(
781
- (signal) => this.http.client.POST(`/api/scheduled-charges/${id}/payment-method`, {
782
- body: params,
783
- signal
784
- }).then((r) => r)
838
+ (signal) => this.http.client.POST(
839
+ `/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
840
+ {
841
+ body: params,
842
+ signal
843
+ }
844
+ ).then((r) => r)
785
845
  );
786
846
  }
787
847
  /**
@@ -794,10 +854,13 @@ var ScheduledCharges = class {
794
854
  */
795
855
  async clearPaymentMethod(id) {
796
856
  return this.http.call(
797
- (signal) => this.http.client.DELETE(`/api/scheduled-charges/${id}/payment-method`, {
798
- body: {},
799
- signal
800
- }).then((r) => r)
857
+ (signal) => this.http.client.DELETE(
858
+ `/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
859
+ {
860
+ body: {},
861
+ signal
862
+ }
863
+ ).then((r) => r)
801
864
  );
802
865
  }
803
866
  /**
@@ -819,7 +882,7 @@ var ScheduledCharges = class {
819
882
  if (params.limit !== void 0) qs.set("limit", String(params.limit));
820
883
  if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
821
884
  const query = qs.toString();
822
- const url = `/api/scheduled-charges/${id}/attempts${query ? `?${query}` : ""}`;
885
+ const url = `/api/scheduled-charges/${encodeURIComponent(id)}/attempts${query ? `?${query}` : ""}`;
823
886
  return this.http.call(
824
887
  (signal) => this.http.client.GET(url, { signal }).then(
825
888
  (r) => r
@@ -937,6 +1000,11 @@ var WebhookEvents = class {
937
1000
  * original — distinguishable both by the `resend_` prefix and by reading
938
1001
  * the response payload's `manualResendOf` field.
939
1002
  *
1003
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1004
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1005
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
1006
+ * backend returns the original clone on the second call within 24h.
1007
+ *
940
1008
  * Returns the *clone* event (new id), not the original. The original is
941
1009
  * unchanged on the server.
942
1010
  *
@@ -946,10 +1014,12 @@ var WebhookEvents = class {
946
1014
  * clone.id !== event.id; // true — clone has its own id
947
1015
  * clone.manualResendOf === event.id; // true — points back at the source
948
1016
  */
949
- async resend(id) {
1017
+ async resend(id, params = {}) {
1018
+ const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
950
1019
  return this.http.call(
951
1020
  (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
952
1021
  body: {},
1022
+ headers: { "X-Idempotency-Key": idempotencyKey },
953
1023
  signal
954
1024
  }).then((r) => r)
955
1025
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Official Node.js / TypeScript SDK for the Garu payment gateway.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://garu.com.br",
@@ -46,7 +46,7 @@
46
46
  "typecheck": "tsc --noEmit",
47
47
  "test": "vitest run",
48
48
  "test:watch": "vitest",
49
- "generate:fetch": "curl -sf ${GARU_SPEC_URL:-https://garu.com.br/api/swagger-json} -o src/generated/openapi.json",
49
+ "generate:fetch": "curl -sf ${GARU_SPEC_URL:-https://garu.com.br/api/openapi.json} -o src/generated/openapi.json",
50
50
  "generate:filter": "node scripts/filter-spec.mjs",
51
51
  "generate": "npm run generate:fetch && npm run generate:filter && openapi-typescript src/generated/openapi-sdk.json -o src/generated/schema.d.ts",
52
52
  "prepublishOnly": "npm run typecheck && npm test && npm run build"