@garuhq/node 0.12.0 → 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 +21 -0
- package/README.md +32 -0
- package/dist/index.cjs +8 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +8 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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
|
+
|
|
6
27
|
## [0.12.0] — 2026-05-19
|
|
7
28
|
|
|
8
29
|
### Added
|
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
|
@@ -943,6 +943,11 @@ var WebhookEvents = class {
|
|
|
943
943
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
944
944
|
* the response payload's `manualResendOf` field.
|
|
945
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
|
+
*
|
|
946
951
|
* Returns the *clone* event (new id), not the original. The original is
|
|
947
952
|
* unchanged on the server.
|
|
948
953
|
*
|
|
@@ -952,10 +957,12 @@ var WebhookEvents = class {
|
|
|
952
957
|
* clone.id !== event.id; // true — clone has its own id
|
|
953
958
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
954
959
|
*/
|
|
955
|
-
async resend(id) {
|
|
960
|
+
async resend(id, params = {}) {
|
|
961
|
+
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
956
962
|
return this.http.call(
|
|
957
963
|
(signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
|
|
958
964
|
body: {},
|
|
965
|
+
headers: { "X-Idempotency-Key": idempotencyKey },
|
|
959
966
|
signal
|
|
960
967
|
}).then((r) => r)
|
|
961
968
|
);
|
package/dist/index.d.cts
CHANGED
|
@@ -618,6 +618,16 @@ interface ListWebhookEventsParams {
|
|
|
618
618
|
/** Filter by the destination endpoint that should receive (or received) the event. */
|
|
619
619
|
endpointId?: number;
|
|
620
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
|
+
}
|
|
621
631
|
/**
|
|
622
632
|
* Per-product portal customization (Atletia coach-as-product modeling and
|
|
623
633
|
* any other B2B2C platform). `null` fields inherit from the seller-level
|
|
@@ -1163,6 +1173,11 @@ declare class WebhookEvents {
|
|
|
1163
1173
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1164
1174
|
* the response payload's `manualResendOf` field.
|
|
1165
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
|
+
*
|
|
1166
1181
|
* Returns the *clone* event (new id), not the original. The original is
|
|
1167
1182
|
* unchanged on the server.
|
|
1168
1183
|
*
|
|
@@ -1172,7 +1187,7 @@ declare class WebhookEvents {
|
|
|
1172
1187
|
* clone.id !== event.id; // true — clone has its own id
|
|
1173
1188
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
1174
1189
|
*/
|
|
1175
|
-
resend(id: number): Promise<WebhookEvent>;
|
|
1190
|
+
resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
|
|
1176
1191
|
}
|
|
1177
1192
|
|
|
1178
1193
|
interface GaruOptions {
|
|
@@ -1274,4 +1289,4 @@ declare class GaruServerError extends GaruAPIError {
|
|
|
1274
1289
|
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
1275
1290
|
}
|
|
1276
1291
|
|
|
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 };
|
|
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
|
@@ -618,6 +618,16 @@ interface ListWebhookEventsParams {
|
|
|
618
618
|
/** Filter by the destination endpoint that should receive (or received) the event. */
|
|
619
619
|
endpointId?: number;
|
|
620
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
|
+
}
|
|
621
631
|
/**
|
|
622
632
|
* Per-product portal customization (Atletia coach-as-product modeling and
|
|
623
633
|
* any other B2B2C platform). `null` fields inherit from the seller-level
|
|
@@ -1163,6 +1173,11 @@ declare class WebhookEvents {
|
|
|
1163
1173
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1164
1174
|
* the response payload's `manualResendOf` field.
|
|
1165
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
|
+
*
|
|
1166
1181
|
* Returns the *clone* event (new id), not the original. The original is
|
|
1167
1182
|
* unchanged on the server.
|
|
1168
1183
|
*
|
|
@@ -1172,7 +1187,7 @@ declare class WebhookEvents {
|
|
|
1172
1187
|
* clone.id !== event.id; // true — clone has its own id
|
|
1173
1188
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
1174
1189
|
*/
|
|
1175
|
-
resend(id: number): Promise<WebhookEvent>;
|
|
1190
|
+
resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
|
|
1176
1191
|
}
|
|
1177
1192
|
|
|
1178
1193
|
interface GaruOptions {
|
|
@@ -1274,4 +1289,4 @@ declare class GaruServerError extends GaruAPIError {
|
|
|
1274
1289
|
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
1275
1290
|
}
|
|
1276
1291
|
|
|
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 };
|
|
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
|
@@ -937,6 +937,11 @@ var WebhookEvents = class {
|
|
|
937
937
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
938
938
|
* the response payload's `manualResendOf` field.
|
|
939
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
|
+
*
|
|
940
945
|
* Returns the *clone* event (new id), not the original. The original is
|
|
941
946
|
* unchanged on the server.
|
|
942
947
|
*
|
|
@@ -946,10 +951,12 @@ var WebhookEvents = class {
|
|
|
946
951
|
* clone.id !== event.id; // true — clone has its own id
|
|
947
952
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
948
953
|
*/
|
|
949
|
-
async resend(id) {
|
|
954
|
+
async resend(id, params = {}) {
|
|
955
|
+
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
950
956
|
return this.http.call(
|
|
951
957
|
(signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
|
|
952
958
|
body: {},
|
|
959
|
+
headers: { "X-Idempotency-Key": idempotencyKey },
|
|
953
960
|
signal
|
|
954
961
|
}).then((r) => r)
|
|
955
962
|
);
|