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