@garuhq/node 0.11.1 → 0.12.1

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,55 @@
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.12.1] — 2026-05-19
7
+
8
+ ### Fixed
9
+
10
+ - `webhookEvents.resend(id)` now auto-attaches `X-Idempotency-Key`
11
+ (UUIDv4) so transient transport retries (5xx → SDK backoff) cannot
12
+ create duplicate clones. Previously, a 503 mid-flight after the
13
+ backend had already committed the clone could trigger an SDK retry
14
+ and produce a second clone with a different id. With the
15
+ idempotency key in place, the backend returns the original clone on
16
+ the second call within 24h. Pass `{ idempotencyKey }` to dedupe
17
+ across your own retry layer.
18
+
19
+ ### Added
20
+
21
+ - `ResendWebhookEventParams` type exported from the package root —
22
+ the optional `{ idempotencyKey?: string }` for `resend()`.
23
+ - README quickstart entry for `webhookEvents`
24
+ (`list` / `get` / `resend` / `retry`), including the audit-trail
25
+ contract and the SDK→gateway idempotency note.
26
+
27
+ ## [0.12.0] — 2026-05-19
28
+
29
+ ### Added
30
+
31
+ - `webhookEvents.resend(id)` — `POST /api/webhook-events/{id}/resend`,
32
+ the audit-trail-preserving counterpart to `retry()`. The backend
33
+ inserts a *clone* event (new numeric id) that points back at the
34
+ source via `manualResendOf`, then dispatches that clone. The
35
+ original row is untouched, so the historical record of the prior
36
+ failure (status, response status/body, attempts) survives. Works on
37
+ any source status (`success` / `failed` / `pending`).
38
+ - Outbound delivery uses `Idempotency-Key: resend_<originalId>`, so
39
+ recipient handlers can distinguish a resend from a fresh delivery
40
+ both by the header prefix and by reading the response payload's
41
+ `manualResendOf` field.
42
+ - `WebhookEvent.manualResendOf: number | null` — populated with the
43
+ source event's numeric id on rows produced by `resend()`, `null`
44
+ everywhere else (originally-fired events and legacy `retry()`
45
+ outputs).
46
+
47
+ ### Deprecated
48
+
49
+ - `webhookEvents.retry(id)` is soft-deprecated. It still works and is
50
+ not scheduled for removal — older CLI / MCP releases depend on it —
51
+ but new integrations should prefer `resend()`, which preserves the
52
+ original event's audit trail by cloning instead of mutating the row
53
+ in place.
54
+
6
55
  ## [0.11.1] — 2026-05-19
7
56
 
8
57
  ### Fixed
package/README.md CHANGED
@@ -292,6 +292,38 @@ app.post('/webhooks/garu', express.raw({ type: 'application/json' }), (req, res)
292
292
  > [!IMPORTANT]
293
293
  > Always pass the raw request body to `verify()`. Parsing and re-serializing JSON will break the signature check.
294
294
 
295
+ ## Webhook events
296
+
297
+ 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.
298
+
299
+ ```ts
300
+ // Surface anything that didn't make it through
301
+ const failed = await garu.webhookEvents.list({ status: 'failed', limit: 50 });
302
+
303
+ // Inspect one event end-to-end
304
+ const event = await garu.webhookEvents.get(42);
305
+ console.log(event.responseStatus, event.responseBody);
306
+
307
+ // Audit-trail-preserving replay (recommended)
308
+ const clone = await garu.webhookEvents.resend(42);
309
+ clone.id !== event.id; // true — fresh row with its own id
310
+ clone.manualResendOf === event.id; // true — points back at the source
311
+ ```
312
+
313
+ `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`).
314
+
315
+ 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.
316
+
317
+ > [!NOTE]
318
+ > 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.
319
+
320
+ | Method | Purpose |
321
+ | ------------------------------- | ---------------------------------------------------------------------------------- |
322
+ | `list(params?)` | Paginated event log. Filter by `status`, `eventType`, `endpointId`. Newest first. |
323
+ | `get(id)` | One event — full payload, endpoint snapshot, most recent response. |
324
+ | `resend(id, params?)` | Clone-on-resend. Returns the new event; original is untouched. **Preferred.** |
325
+ | `retry(id)` | Legacy in-place reset (mutates the original row). Soft-deprecated. |
326
+
295
327
  ## Error handling
296
328
 
297
329
  Every error extends `GaruError`. API errors include `status`, `requestId`, and `body`.
package/dist/index.cjs CHANGED
@@ -898,10 +898,16 @@ var WebhookEvents = class {
898
898
  );
899
899
  }
900
900
  /**
901
+ * @deprecated For most cases prefer {@link resend}, which preserves the
902
+ * original event's audit trail by cloning rather than mutating. `retry()`
903
+ * resets the original row in place — once it succeeds, the historical
904
+ * record of the prior failure is gone. Kept here for callers that
905
+ * explicitly want the legacy in-place semantics (and for backwards
906
+ * compatibility with older CLI / MCP releases).
907
+ *
901
908
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
902
909
  * retry schedule, and triggers an immediate delivery attempt. Works on
903
- * any status (`success`, `failed`, `pending`) — use this when a
904
- * customer reports a missed or unprocessed event.
910
+ * any status (`success`, `failed`, `pending`).
905
911
  *
906
912
  * @example
907
913
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -917,6 +923,50 @@ var WebhookEvents = class {
917
923
  }).then((r) => r)
918
924
  );
919
925
  }
926
+ /**
927
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
928
+ * {@link retry}, this does *not* mutate the original row — it inserts a
929
+ * fresh event (new numeric id) that points back at the source via
930
+ * `manualResendOf`, then dispatches that clone. The original row is
931
+ * untouched, so the historical record of the prior failure (and its
932
+ * response status / body) is preserved.
933
+ *
934
+ * Works on any source status (`success`, `failed`, `pending`). Use this
935
+ * when a customer reports a missed or unprocessed event, or to replay an
936
+ * event during a backfill — both reasons where you want the original
937
+ * delivery's outcome to remain on the record.
938
+ *
939
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
940
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
941
+ * of the source event, not the clone). Recipient handlers that key off
942
+ * `Idempotency-Key` will see this as a distinct delivery from the
943
+ * original — distinguishable both by the `resend_` prefix and by reading
944
+ * the response payload's `manualResendOf` field.
945
+ *
946
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
947
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
948
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
949
+ * backend returns the original clone on the second call within 24h.
950
+ *
951
+ * Returns the *clone* event (new id), not the original. The original is
952
+ * unchanged on the server.
953
+ *
954
+ * @example
955
+ * const event = await garu.webhookEvents.get(42);
956
+ * const clone = await garu.webhookEvents.resend(42);
957
+ * clone.id !== event.id; // true — clone has its own id
958
+ * clone.manualResendOf === event.id; // true — points back at the source
959
+ */
960
+ async resend(id, params = {}) {
961
+ const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
962
+ return this.http.call(
963
+ (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
964
+ body: {},
965
+ headers: { "X-Idempotency-Key": idempotencyKey },
966
+ signal
967
+ }).then((r) => r)
968
+ );
969
+ }
920
970
  };
921
971
  var webhooks = {
922
972
  verify(params) {
package/dist/index.d.cts CHANGED
@@ -596,6 +596,14 @@ interface WebhookEvent {
596
596
  responseStatus: number | null;
597
597
  /** Response body from the most recent attempt, truncated by the gateway. */
598
598
  responseBody: string | null;
599
+ /**
600
+ * When this row is a clone produced by `webhookEvents.resend(id)`, this is
601
+ * the numeric id of the original event the clone was forked from. `null`
602
+ * on every originally-fired event (and on events resurrected via the
603
+ * legacy `webhookEvents.retry(id)` mutation, which mutates in place
604
+ * instead of cloning).
605
+ */
606
+ manualResendOf: number | null;
599
607
  createdAt: string;
600
608
  [key: string]: unknown;
601
609
  }
@@ -610,6 +618,16 @@ interface ListWebhookEventsParams {
610
618
  /** Filter by the destination endpoint that should receive (or received) the event. */
611
619
  endpointId?: number;
612
620
  }
621
+ interface ResendWebhookEventParams {
622
+ /**
623
+ * SDK→gateway idempotency key. If omitted, the SDK generates a UUIDv4
624
+ * and forwards it as `X-Idempotency-Key`. Within 24h the backend
625
+ * returns the original clone instead of creating a new one — pass a
626
+ * stable key from your own retry layer to dedupe across SDK
627
+ * invocations.
628
+ */
629
+ idempotencyKey?: string;
630
+ }
613
631
  /**
614
632
  * Per-product portal customization (Atletia coach-as-product modeling and
615
633
  * any other B2B2C platform). `null` fields inherit from the seller-level
@@ -1117,10 +1135,16 @@ declare class WebhookEvents {
1117
1135
  */
1118
1136
  get(id: number): Promise<WebhookEvent>;
1119
1137
  /**
1138
+ * @deprecated For most cases prefer {@link resend}, which preserves the
1139
+ * original event's audit trail by cloning rather than mutating. `retry()`
1140
+ * resets the original row in place — once it succeeds, the historical
1141
+ * record of the prior failure is gone. Kept here for callers that
1142
+ * explicitly want the legacy in-place semantics (and for backwards
1143
+ * compatibility with older CLI / MCP releases).
1144
+ *
1120
1145
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
1121
1146
  * retry schedule, and triggers an immediate delivery attempt. Works on
1122
- * any status (`success`, `failed`, `pending`) — use this when a
1123
- * customer reports a missed or unprocessed event.
1147
+ * any status (`success`, `failed`, `pending`).
1124
1148
  *
1125
1149
  * @example
1126
1150
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -1129,6 +1153,41 @@ declare class WebhookEvents {
1129
1153
  * }
1130
1154
  */
1131
1155
  retry(id: number): Promise<WebhookEvent>;
1156
+ /**
1157
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
1158
+ * {@link retry}, this does *not* mutate the original row — it inserts a
1159
+ * fresh event (new numeric id) that points back at the source via
1160
+ * `manualResendOf`, then dispatches that clone. The original row is
1161
+ * untouched, so the historical record of the prior failure (and its
1162
+ * response status / body) is preserved.
1163
+ *
1164
+ * Works on any source status (`success`, `failed`, `pending`). Use this
1165
+ * when a customer reports a missed or unprocessed event, or to replay an
1166
+ * event during a backfill — both reasons where you want the original
1167
+ * delivery's outcome to remain on the record.
1168
+ *
1169
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
1170
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
1171
+ * of the source event, not the clone). Recipient handlers that key off
1172
+ * `Idempotency-Key` will see this as a distinct delivery from the
1173
+ * original — distinguishable both by the `resend_` prefix and by reading
1174
+ * the response payload's `manualResendOf` field.
1175
+ *
1176
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1177
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1178
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
1179
+ * backend returns the original clone on the second call within 24h.
1180
+ *
1181
+ * Returns the *clone* event (new id), not the original. The original is
1182
+ * unchanged on the server.
1183
+ *
1184
+ * @example
1185
+ * const event = await garu.webhookEvents.get(42);
1186
+ * const clone = await garu.webhookEvents.resend(42);
1187
+ * clone.id !== event.id; // true — clone has its own id
1188
+ * clone.manualResendOf === event.id; // true — points back at the source
1189
+ */
1190
+ resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
1132
1191
  }
1133
1192
 
1134
1193
  interface GaruOptions {
@@ -1230,4 +1289,4 @@ declare class GaruServerError extends GaruAPIError {
1230
1289
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1231
1290
  }
1232
1291
 
1233
- 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 };
1292
+ 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 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
@@ -596,6 +596,14 @@ interface WebhookEvent {
596
596
  responseStatus: number | null;
597
597
  /** Response body from the most recent attempt, truncated by the gateway. */
598
598
  responseBody: string | null;
599
+ /**
600
+ * When this row is a clone produced by `webhookEvents.resend(id)`, this is
601
+ * the numeric id of the original event the clone was forked from. `null`
602
+ * on every originally-fired event (and on events resurrected via the
603
+ * legacy `webhookEvents.retry(id)` mutation, which mutates in place
604
+ * instead of cloning).
605
+ */
606
+ manualResendOf: number | null;
599
607
  createdAt: string;
600
608
  [key: string]: unknown;
601
609
  }
@@ -610,6 +618,16 @@ interface ListWebhookEventsParams {
610
618
  /** Filter by the destination endpoint that should receive (or received) the event. */
611
619
  endpointId?: number;
612
620
  }
621
+ interface ResendWebhookEventParams {
622
+ /**
623
+ * SDK→gateway idempotency key. If omitted, the SDK generates a UUIDv4
624
+ * and forwards it as `X-Idempotency-Key`. Within 24h the backend
625
+ * returns the original clone instead of creating a new one — pass a
626
+ * stable key from your own retry layer to dedupe across SDK
627
+ * invocations.
628
+ */
629
+ idempotencyKey?: string;
630
+ }
613
631
  /**
614
632
  * Per-product portal customization (Atletia coach-as-product modeling and
615
633
  * any other B2B2C platform). `null` fields inherit from the seller-level
@@ -1117,10 +1135,16 @@ declare class WebhookEvents {
1117
1135
  */
1118
1136
  get(id: number): Promise<WebhookEvent>;
1119
1137
  /**
1138
+ * @deprecated For most cases prefer {@link resend}, which preserves the
1139
+ * original event's audit trail by cloning rather than mutating. `retry()`
1140
+ * resets the original row in place — once it succeeds, the historical
1141
+ * record of the prior failure is gone. Kept here for callers that
1142
+ * explicitly want the legacy in-place semantics (and for backwards
1143
+ * compatibility with older CLI / MCP releases).
1144
+ *
1120
1145
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
1121
1146
  * retry schedule, and triggers an immediate delivery attempt. Works on
1122
- * any status (`success`, `failed`, `pending`) — use this when a
1123
- * customer reports a missed or unprocessed event.
1147
+ * any status (`success`, `failed`, `pending`).
1124
1148
  *
1125
1149
  * @example
1126
1150
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -1129,6 +1153,41 @@ declare class WebhookEvents {
1129
1153
  * }
1130
1154
  */
1131
1155
  retry(id: number): Promise<WebhookEvent>;
1156
+ /**
1157
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
1158
+ * {@link retry}, this does *not* mutate the original row — it inserts a
1159
+ * fresh event (new numeric id) that points back at the source via
1160
+ * `manualResendOf`, then dispatches that clone. The original row is
1161
+ * untouched, so the historical record of the prior failure (and its
1162
+ * response status / body) is preserved.
1163
+ *
1164
+ * Works on any source status (`success`, `failed`, `pending`). Use this
1165
+ * when a customer reports a missed or unprocessed event, or to replay an
1166
+ * event during a backfill — both reasons where you want the original
1167
+ * delivery's outcome to remain on the record.
1168
+ *
1169
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
1170
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
1171
+ * of the source event, not the clone). Recipient handlers that key off
1172
+ * `Idempotency-Key` will see this as a distinct delivery from the
1173
+ * original — distinguishable both by the `resend_` prefix and by reading
1174
+ * the response payload's `manualResendOf` field.
1175
+ *
1176
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
1177
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
1178
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
1179
+ * backend returns the original clone on the second call within 24h.
1180
+ *
1181
+ * Returns the *clone* event (new id), not the original. The original is
1182
+ * unchanged on the server.
1183
+ *
1184
+ * @example
1185
+ * const event = await garu.webhookEvents.get(42);
1186
+ * const clone = await garu.webhookEvents.resend(42);
1187
+ * clone.id !== event.id; // true — clone has its own id
1188
+ * clone.manualResendOf === event.id; // true — points back at the source
1189
+ */
1190
+ resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
1132
1191
  }
1133
1192
 
1134
1193
  interface GaruOptions {
@@ -1230,4 +1289,4 @@ declare class GaruServerError extends GaruAPIError {
1230
1289
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1231
1290
  }
1232
1291
 
1233
- 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 };
1292
+ 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 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
@@ -892,10 +892,16 @@ var WebhookEvents = class {
892
892
  );
893
893
  }
894
894
  /**
895
+ * @deprecated For most cases prefer {@link resend}, which preserves the
896
+ * original event's audit trail by cloning rather than mutating. `retry()`
897
+ * resets the original row in place — once it succeeds, the historical
898
+ * record of the prior failure is gone. Kept here for callers that
899
+ * explicitly want the legacy in-place semantics (and for backwards
900
+ * compatibility with older CLI / MCP releases).
901
+ *
895
902
  * Re-deliver a webhook event by ID. Resets it to `pending`, clears the
896
903
  * retry schedule, and triggers an immediate delivery attempt. Works on
897
- * any status (`success`, `failed`, `pending`) — use this when a
898
- * customer reports a missed or unprocessed event.
904
+ * any status (`success`, `failed`, `pending`).
899
905
  *
900
906
  * @example
901
907
  * const failed = await garu.webhookEvents.list({ status: 'failed', limit: 5 });
@@ -911,6 +917,50 @@ var WebhookEvents = class {
911
917
  }).then((r) => r)
912
918
  );
913
919
  }
920
+ /**
921
+ * Re-deliver a webhook event by ID, audit-trail preserving. Unlike
922
+ * {@link retry}, this does *not* mutate the original row — it inserts a
923
+ * fresh event (new numeric id) that points back at the source via
924
+ * `manualResendOf`, then dispatches that clone. The original row is
925
+ * untouched, so the historical record of the prior failure (and its
926
+ * response status / body) is preserved.
927
+ *
928
+ * Works on any source status (`success`, `failed`, `pending`). Use this
929
+ * when a customer reports a missed or unprocessed event, or to replay an
930
+ * event during a backfill — both reasons where you want the original
931
+ * delivery's outcome to remain on the record.
932
+ *
933
+ * **Outbound delivery semantics**: the gateway POSTs the clone with
934
+ * `Idempotency-Key: resend_<originalId>` (where `<originalId>` is the id
935
+ * of the source event, not the clone). Recipient handlers that key off
936
+ * `Idempotency-Key` will see this as a distinct delivery from the
937
+ * original — distinguishable both by the `resend_` prefix and by reading
938
+ * the response payload's `manualResendOf` field.
939
+ *
940
+ * **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
941
+ * (UUIDv4 unless you pass `idempotencyKey`) so transient transport
942
+ * retries (5xx → SDK backoff) cannot create duplicate clones — the
943
+ * backend returns the original clone on the second call within 24h.
944
+ *
945
+ * Returns the *clone* event (new id), not the original. The original is
946
+ * unchanged on the server.
947
+ *
948
+ * @example
949
+ * const event = await garu.webhookEvents.get(42);
950
+ * const clone = await garu.webhookEvents.resend(42);
951
+ * clone.id !== event.id; // true — clone has its own id
952
+ * clone.manualResendOf === event.id; // true — points back at the source
953
+ */
954
+ async resend(id, params = {}) {
955
+ const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
956
+ return this.http.call(
957
+ (signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
958
+ body: {},
959
+ headers: { "X-Idempotency-Key": idempotencyKey },
960
+ signal
961
+ }).then((r) => r)
962
+ );
963
+ }
914
964
  };
915
965
  var webhooks = {
916
966
  verify(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "0.11.1",
3
+ "version": "0.12.1",
4
4
  "description": "Official Node.js / TypeScript SDK for the Garu payment gateway.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://garu.com.br",