@clocknext/sdk 0.14.0 → 0.16.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/README.md +274 -128
- package/dist/index.cjs +345 -281
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +689 -773
- package/dist/index.d.ts +689 -773
- package/dist/index.js +345 -281
- package/dist/index.js.map +1 -1
- package/examples/node-basic.ts +24 -32
- package/package.json +1 -1
- package/src/client.ts +15 -23
- package/src/customer-ref.ts +14 -0
- package/src/errors.ts +10 -2
- package/src/filters.ts +1 -1
- package/src/index.ts +32 -58
- package/src/resources/composites.ts +55 -20
- package/src/resources/credits.ts +40 -35
- package/src/resources/customers.ts +201 -82
- package/src/resources/invoices.ts +1 -1
- package/src/resources/outcomes.ts +40 -41
- package/src/resources/plans.ts +38 -32
- package/src/resources/signals.ts +8 -46
- package/src/resources/units.ts +37 -37
- package/src/types.ts +549 -619
- package/src/resources/webhooks.ts +0 -37
- package/src/resources/workspace.ts +0 -33
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Transport } from "../http.js";
|
|
2
|
+
import { newKey } from "../idempotency.js";
|
|
2
3
|
import type {
|
|
3
4
|
AddMemberInput,
|
|
5
|
+
AdjustResult,
|
|
4
6
|
ApiCustomer,
|
|
5
7
|
CompositeAdjustInput,
|
|
6
8
|
CreateCustomerInput,
|
|
@@ -14,19 +16,18 @@ import type {
|
|
|
14
16
|
MemberProfile,
|
|
15
17
|
OutcomeAdjustInput,
|
|
16
18
|
OutcomeBalance,
|
|
19
|
+
RecurringGrantInput,
|
|
20
|
+
RecurringGrantResult,
|
|
17
21
|
RemovedMember,
|
|
18
22
|
UnitAdjustInput,
|
|
19
23
|
UnitBalance,
|
|
20
24
|
UpdateMemberInput,
|
|
21
|
-
|
|
22
|
-
WalletEntryInput,
|
|
23
|
-
WalletList,
|
|
24
|
-
WalletTransaction,
|
|
25
|
+
WalletAdjustInput,
|
|
25
26
|
} from "../types.js";
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* The `customers` resource — full CRUD over the org's customers plus the
|
|
29
|
-
* read-only insight endpoints (
|
|
30
|
+
* read-only insight endpoints (balances / plan). Every call is scoped
|
|
30
31
|
* to the API key's organisation server-side; a customer in another org
|
|
31
32
|
* resolves as a NotFoundError.
|
|
32
33
|
*/
|
|
@@ -94,6 +95,42 @@ export class Customers {
|
|
|
94
95
|
return res.customer;
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Fetch one customer by YOUR id for them (its `externalId`), instead of
|
|
100
|
+
* ours. Same result as {@link get}. `GET /api/v1/customers/ext/:externalId`.
|
|
101
|
+
* An unknown external id is a `NotFoundError`; only active customers match.
|
|
102
|
+
*/
|
|
103
|
+
async getByExternalId(externalId: string): Promise<ApiCustomer> {
|
|
104
|
+
const res = await this.transport.request<{ customer: ApiCustomer }>({
|
|
105
|
+
method: "GET",
|
|
106
|
+
path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}`,
|
|
107
|
+
});
|
|
108
|
+
return res.customer;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Update a customer (partial), addressed by its `externalId`. Same body as
|
|
112
|
+
* {@link update}. `PATCH /api/v1/customers/ext/:externalId`. */
|
|
113
|
+
async updateByExternalId(
|
|
114
|
+
externalId: string,
|
|
115
|
+
input: CustomerProfileInput,
|
|
116
|
+
): Promise<ApiCustomer> {
|
|
117
|
+
const res = await this.transport.request<{ customer: ApiCustomer }>({
|
|
118
|
+
method: "PATCH",
|
|
119
|
+
path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}`,
|
|
120
|
+
body: input,
|
|
121
|
+
});
|
|
122
|
+
return res.customer;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** {@link delete}, addressing the customer by its `externalId`.
|
|
126
|
+
* `DELETE /api/v1/customers/ext/:externalId`. */
|
|
127
|
+
async deleteByExternalId(externalId: string): Promise<void> {
|
|
128
|
+
await this.transport.request({
|
|
129
|
+
method: "DELETE",
|
|
130
|
+
path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
97
134
|
/** Delete a customer (members, invitations, usage logs cascade). */
|
|
98
135
|
async delete(id: string): Promise<void> {
|
|
99
136
|
await this.transport.request({
|
|
@@ -102,21 +139,20 @@ export class Customers {
|
|
|
102
139
|
});
|
|
103
140
|
}
|
|
104
141
|
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
142
|
+
/** Wallet / credit / outcome / unit balances. `GET …/:id/balances`. */
|
|
143
|
+
balances(id: string): Promise<CustomerBalances> {
|
|
144
|
+
return this.transport.request<CustomerBalances>({
|
|
108
145
|
method: "GET",
|
|
109
|
-
path: `/api/v1/
|
|
110
|
-
query: { customerId: id, limit: params.limit },
|
|
146
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/balances`,
|
|
111
147
|
});
|
|
112
|
-
return { customer: res.customer, totals: res.totals, logs: res.logs };
|
|
113
148
|
}
|
|
114
149
|
|
|
115
|
-
/**
|
|
116
|
-
balances
|
|
150
|
+
/** {@link balances}, addressing the customer by its `externalId`.
|
|
151
|
+
* `GET /api/v1/customers/ext/:externalId/balances`. */
|
|
152
|
+
balancesByExternalId(externalId: string): Promise<CustomerBalances> {
|
|
117
153
|
return this.transport.request<CustomerBalances>({
|
|
118
154
|
method: "GET",
|
|
119
|
-
path: `/api/v1/customers/${encodeURIComponent(
|
|
155
|
+
path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}/balances`,
|
|
120
156
|
});
|
|
121
157
|
}
|
|
122
158
|
|
|
@@ -183,6 +219,15 @@ export class Customers {
|
|
|
183
219
|
});
|
|
184
220
|
}
|
|
185
221
|
|
|
222
|
+
/** {@link plan}, addressing the customer by its `externalId`.
|
|
223
|
+
* `GET /api/v1/customers/ext/:externalId/plan`. */
|
|
224
|
+
planByExternalId(externalId: string): Promise<CustomerPlan> {
|
|
225
|
+
return this.transport.request<CustomerPlan>({
|
|
226
|
+
method: "GET",
|
|
227
|
+
path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}/plan`,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
186
231
|
// --- Members -------------------------------------------------------------
|
|
187
232
|
|
|
188
233
|
/** List a customer's members. `GET …/:id/members`. */
|
|
@@ -227,95 +272,169 @@ export class Customers {
|
|
|
227
272
|
return res.member;
|
|
228
273
|
}
|
|
229
274
|
|
|
230
|
-
// ---
|
|
275
|
+
// --- Billing config -----------------------------------------------------
|
|
231
276
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
277
|
+
// Note: a customer's currency is set through `update(id, { currencyCode })`
|
|
278
|
+
// (PATCH …/:id) — there is no separate currency endpoint. Credit-type
|
|
279
|
+
// assignment has no public API.
|
|
280
|
+
|
|
281
|
+
// --- Balance adjustments -------------------------------------------------
|
|
282
|
+
|
|
283
|
+
/** Grant or claw back a credit balance. The credit must be a prepaid part of
|
|
284
|
+
* the customer's current plan. Pass `idempotencyKey` to make it safe to
|
|
285
|
+
* retry. `POST …/:id/credits/adjust`. */
|
|
286
|
+
adjustCredit(id: string, input: CreditAdjustInput): Promise<AdjustResult> {
|
|
287
|
+
return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`, input);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Grant or claw back an outcome balance. The outcome must be a prepaid part
|
|
291
|
+
* of the customer's current plan. `POST …/:id/outcomes/adjust`. */
|
|
292
|
+
adjustOutcome(id: string, input: OutcomeAdjustInput): Promise<AdjustResult> {
|
|
293
|
+
return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`, input);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Grant or claw back a unit balance. The unit must be a prepaid part of the
|
|
297
|
+
* customer's current plan. `POST …/:id/units/adjust`. */
|
|
298
|
+
adjustUnit(id: string, input: UnitAdjustInput): Promise<AdjustResult> {
|
|
299
|
+
return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/units/adjust`, input);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Grant or claw back slots on a customer's ADVANCE composite pool for the
|
|
303
|
+
* current billing cycle. `remaining` is the resulting slot count.
|
|
304
|
+
* `POST …/:id/composites/adjust`. */
|
|
305
|
+
adjustComposite(id: string, input: CompositeAdjustInput): Promise<AdjustResult> {
|
|
306
|
+
return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/composites/adjust`, input);
|
|
239
307
|
}
|
|
240
308
|
|
|
241
309
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
310
|
+
* Top up (positive `delta`) or draw down (negative `delta`) a customer's
|
|
311
|
+
* wallet, in USD — allowed whatever the customer's plan. `remaining` is the
|
|
312
|
+
* resulting USD balance. No `agentKey` — a customer has exactly one wallet.
|
|
313
|
+
* Pass `pool: "reserved"` to move the Reserved Wallet instead (then
|
|
314
|
+
* `remaining` is its balance). `POST …/:id/wallet/adjust`.
|
|
244
315
|
*/
|
|
245
|
-
|
|
246
|
-
id
|
|
247
|
-
input: WalletEntryInput,
|
|
248
|
-
): Promise<{ transaction: WalletTransaction; balanceUsd: number }> {
|
|
249
|
-
return this.transport.request<{
|
|
250
|
-
transaction: WalletTransaction;
|
|
251
|
-
balanceUsd: number;
|
|
252
|
-
}>({
|
|
253
|
-
method: "POST",
|
|
254
|
-
path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
|
|
255
|
-
body: input,
|
|
256
|
-
});
|
|
316
|
+
adjustWallet(id: string, input: WalletAdjustInput): Promise<AdjustResult> {
|
|
317
|
+
return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/wallet/adjust`, input);
|
|
257
318
|
}
|
|
258
319
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
320
|
+
/** {@link adjustCredit}, addressing the customer by its `externalId` (`id`
|
|
321
|
+
* here). `POST /api/v1/customers/ext/:externalId/credits/adjust`. */
|
|
322
|
+
adjustCreditByExternalId(id: string, input: CreditAdjustInput): Promise<AdjustResult> {
|
|
323
|
+
return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/credits/adjust`, input);
|
|
324
|
+
}
|
|
262
325
|
|
|
263
|
-
|
|
326
|
+
/** {@link adjustOutcome}, addressing the customer by its `externalId` (`id`
|
|
327
|
+
* here). `POST /api/v1/customers/ext/:externalId/outcomes/adjust`. */
|
|
328
|
+
adjustOutcomeByExternalId(id: string, input: OutcomeAdjustInput): Promise<AdjustResult> {
|
|
329
|
+
return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/outcomes/adjust`, input);
|
|
330
|
+
}
|
|
264
331
|
|
|
265
|
-
/**
|
|
266
|
-
* `POST
|
|
267
|
-
|
|
268
|
-
id
|
|
269
|
-
input: CreditAdjustInput,
|
|
270
|
-
): Promise<{ remaining: number }> {
|
|
271
|
-
const res = await this.transport.request<{ remaining: number }>({
|
|
272
|
-
method: "POST",
|
|
273
|
-
path: `/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`,
|
|
274
|
-
body: input,
|
|
275
|
-
});
|
|
276
|
-
return { remaining: res.remaining };
|
|
332
|
+
/** {@link adjustUnit}, addressing the customer by its `externalId` (`id`
|
|
333
|
+
* here). `POST /api/v1/customers/ext/:externalId/units/adjust`. */
|
|
334
|
+
adjustUnitByExternalId(id: string, input: UnitAdjustInput): Promise<AdjustResult> {
|
|
335
|
+
return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/units/adjust`, input);
|
|
277
336
|
}
|
|
278
337
|
|
|
279
|
-
/**
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
input
|
|
283
|
-
): Promise<{ remaining: number }> {
|
|
284
|
-
const res = await this.transport.request<{ remaining: number }>({
|
|
285
|
-
method: "POST",
|
|
286
|
-
path: `/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`,
|
|
287
|
-
body: input,
|
|
288
|
-
});
|
|
289
|
-
return { remaining: res.remaining };
|
|
338
|
+
/** {@link adjustComposite}, addressing the customer by its `externalId`
|
|
339
|
+
* (`id` here). `POST /api/v1/customers/ext/:externalId/composites/adjust`. */
|
|
340
|
+
adjustCompositeByExternalId(id: string, input: CompositeAdjustInput): Promise<AdjustResult> {
|
|
341
|
+
return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/composites/adjust`, input);
|
|
290
342
|
}
|
|
291
343
|
|
|
292
|
-
/**
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
input
|
|
296
|
-
|
|
297
|
-
|
|
344
|
+
/** {@link adjustWallet}, addressing the customer by its `externalId` (`id`
|
|
345
|
+
* here). `POST /api/v1/customers/ext/:externalId/wallet/adjust`. */
|
|
346
|
+
adjustWalletByExternalId(id: string, input: WalletAdjustInput): Promise<AdjustResult> {
|
|
347
|
+
return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/wallet/adjust`, input);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The one body behind all ten adjust calls.
|
|
352
|
+
*
|
|
353
|
+
* Every adjustment carries an idempotency key — the caller's, or one
|
|
354
|
+
* generated here ONCE per call, exactly like signals. The server
|
|
355
|
+
* deduplicates on it, so a transient failure is safe to retry: the same key
|
|
356
|
+
* rides every attempt, and a retry of one that already landed comes back as
|
|
357
|
+
* `replayed: true` instead of adjusting again. The key is also copied onto
|
|
358
|
+
* any thrown error (`error.idempotencyKey`) so the caller can replay safely.
|
|
359
|
+
*/
|
|
360
|
+
private async adjust(
|
|
361
|
+
path: string,
|
|
362
|
+
input:
|
|
363
|
+
| CreditAdjustInput
|
|
364
|
+
| OutcomeAdjustInput
|
|
365
|
+
| UnitAdjustInput
|
|
366
|
+
| CompositeAdjustInput
|
|
367
|
+
| WalletAdjustInput,
|
|
368
|
+
): Promise<AdjustResult> {
|
|
369
|
+
const idempotencyKey = input.idempotencyKey ?? newKey();
|
|
370
|
+
const res = await this.transport.request<{
|
|
371
|
+
remaining: number;
|
|
372
|
+
replayed?: boolean;
|
|
373
|
+
adjustedAt?: string;
|
|
374
|
+
customerArchived?: boolean;
|
|
375
|
+
}>({
|
|
298
376
|
method: "POST",
|
|
299
|
-
path
|
|
300
|
-
|
|
377
|
+
path,
|
|
378
|
+
idempotencyKey,
|
|
379
|
+
body: { ...input, idempotencyKey },
|
|
380
|
+
retry: true,
|
|
301
381
|
});
|
|
302
|
-
|
|
382
|
+
|
|
383
|
+
const result: AdjustResult = {
|
|
384
|
+
remaining: res.remaining,
|
|
385
|
+
replayed: res.replayed === true,
|
|
386
|
+
customerArchived: res.customerArchived === true,
|
|
387
|
+
};
|
|
388
|
+
if (result.replayed && typeof res.adjustedAt === "string") {
|
|
389
|
+
result.adjustedAt = res.adjustedAt;
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
303
392
|
}
|
|
304
393
|
|
|
394
|
+
// --- Recurring grants ----------------------------------------------------
|
|
395
|
+
|
|
305
396
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* `
|
|
397
|
+
* Change how much a customer is granted every billing cycle (wallet top-up,
|
|
398
|
+
* credits, outcomes, units or a composite pool). Any increase or decrease
|
|
399
|
+
* applies to their current balance immediately. `PUT …/:id/recurring-grant`.
|
|
309
400
|
*/
|
|
310
|
-
async
|
|
401
|
+
async updateRecurringGrant(
|
|
311
402
|
id: string,
|
|
312
|
-
input:
|
|
313
|
-
): Promise<
|
|
314
|
-
const res = await this.transport.request<
|
|
315
|
-
|
|
316
|
-
|
|
403
|
+
input: RecurringGrantInput,
|
|
404
|
+
): Promise<RecurringGrantResult> {
|
|
405
|
+
const res = await this.transport.request<
|
|
406
|
+
RecurringGrantResult & { updated: true }
|
|
407
|
+
>({
|
|
408
|
+
method: "PUT",
|
|
409
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/recurring-grant`,
|
|
410
|
+
body: input,
|
|
411
|
+
});
|
|
412
|
+
return toRecurringGrantResult(res);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** {@link updateRecurringGrant}, addressing the customer by its
|
|
416
|
+
* `externalId`. `PUT /api/v1/customers/ext/:externalId/recurring-grant`. */
|
|
417
|
+
async updateRecurringGrantByExternalId(
|
|
418
|
+
externalId: string,
|
|
419
|
+
input: RecurringGrantInput,
|
|
420
|
+
): Promise<RecurringGrantResult> {
|
|
421
|
+
const res = await this.transport.request<
|
|
422
|
+
RecurringGrantResult & { updated: true }
|
|
423
|
+
>({
|
|
424
|
+
method: "PUT",
|
|
425
|
+
path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}/recurring-grant`,
|
|
317
426
|
body: input,
|
|
318
427
|
});
|
|
319
|
-
return
|
|
428
|
+
return toRecurringGrantResult(res);
|
|
320
429
|
}
|
|
321
430
|
}
|
|
431
|
+
|
|
432
|
+
/** Drop the response's `updated: true` marker, keeping `balanceUsd` only when
|
|
433
|
+
* the server sent it (wallet grants). */
|
|
434
|
+
function toRecurringGrantResult(res: RecurringGrantResult): RecurringGrantResult {
|
|
435
|
+
return {
|
|
436
|
+
kind: res.kind,
|
|
437
|
+
quantity: res.quantity,
|
|
438
|
+
...(res.balanceUsd !== undefined ? { balanceUsd: res.balanceUsd } : {}),
|
|
439
|
+
};
|
|
440
|
+
}
|
|
@@ -39,7 +39,7 @@ export class Invoices {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Mint a hosted pay link for an OPEN invoice. PAID/VOID invoices are rejected
|
|
42
|
+
* Mint a hosted pay link for an OPEN invoice. PAID/VOID/AUTO_CLOSED invoices are rejected
|
|
43
43
|
* with a `ConflictError`. `GET /api/v1/invoices/:id/pay-link`.
|
|
44
44
|
*/
|
|
45
45
|
payLink(id: string): Promise<PayLink> {
|
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
import { activeQuery, filterByActive } from "../filters.js";
|
|
2
2
|
import type { Transport } from "../http.js";
|
|
3
|
-
import type {
|
|
4
|
-
CreateOutcomeInput,
|
|
5
|
-
Outcome,
|
|
6
|
-
OutcomeDetail,
|
|
7
|
-
UpdateOutcomeInput,
|
|
8
|
-
} from "../types.js";
|
|
3
|
+
import type { CreateOutcomeInput, Outcome, OutcomeWithSteps, UpdateOutcomeInput } from "../types.js";
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
|
-
* The `outcomes` resource —
|
|
12
|
-
* outcome
|
|
6
|
+
* The `outcomes` resource — multi-step deliverables billed per completed
|
|
7
|
+
* outcome. `/api/v1/outcomes`.
|
|
13
8
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
9
|
+
* `list()` and every write return the outcome without its steps; `get()`
|
|
10
|
+
* returns it with every step in full (ids included). Each new step is priced
|
|
11
|
+
* from its own `modelBundle`, on the server.
|
|
12
|
+
*
|
|
13
|
+
* There is no `delete()` — the API never deletes catalogue items. Retire an
|
|
14
|
+
* outcome with `archive()`; `unarchive()` brings it back unchanged.
|
|
20
15
|
*/
|
|
21
16
|
export class Outcomes {
|
|
22
17
|
constructor(private readonly transport: Transport) {}
|
|
23
18
|
|
|
24
|
-
/** List
|
|
25
|
-
*
|
|
26
|
-
* both. See `activeQuery` — the API itself only supports `true`. */
|
|
19
|
+
/** List them. `{ active: true }` returns only active ones, `{ active: false }`
|
|
20
|
+
* only archived ones; omit it for both. */
|
|
27
21
|
async list(params: { active?: boolean } = {}): Promise<Outcome[]> {
|
|
28
22
|
const res = await this.transport.request<{ outcomes: Outcome[] }>({
|
|
29
23
|
method: "GET",
|
|
@@ -33,28 +27,31 @@ export class Outcomes {
|
|
|
33
27
|
return filterByActive(res.outcomes, params.active);
|
|
34
28
|
}
|
|
35
29
|
|
|
36
|
-
/**
|
|
37
|
-
async
|
|
38
|
-
const res = await this.transport.request<{ outcome:
|
|
39
|
-
method: "
|
|
40
|
-
path:
|
|
41
|
-
body: input,
|
|
30
|
+
/** One outcome's configuration, with every step in full. */
|
|
31
|
+
async get(id: string): Promise<OutcomeWithSteps> {
|
|
32
|
+
const res = await this.transport.request<{ outcome: OutcomeWithSteps }>({
|
|
33
|
+
method: "GET",
|
|
34
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
42
35
|
});
|
|
43
36
|
return res.outcome;
|
|
44
37
|
}
|
|
45
38
|
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
/** Create one. It starts active. Not retried on failure — a retry could
|
|
40
|
+
* create a second row. */
|
|
41
|
+
async create(input: CreateOutcomeInput): Promise<Outcome> {
|
|
42
|
+
const res = await this.transport.request<{ created: true; outcome: Outcome }>({
|
|
43
|
+
method: "POST",
|
|
44
|
+
path: "/api/v1/outcomes",
|
|
45
|
+
body: input,
|
|
51
46
|
});
|
|
52
47
|
return res.outcome;
|
|
53
48
|
}
|
|
54
49
|
|
|
55
|
-
/**
|
|
50
|
+
/** PARTIAL update: only the fields you send change. Leave `steps` out to
|
|
51
|
+
* leave the steps alone; send it and it is the complete list — pass each
|
|
52
|
+
* existing step's `id` (or its same `agentKey`) to keep it. */
|
|
56
53
|
async update(id: string, input: UpdateOutcomeInput): Promise<Outcome> {
|
|
57
|
-
const res = await this.transport.request<{ outcome: Outcome }>({
|
|
54
|
+
const res = await this.transport.request<{ updated: true; outcome: Outcome }>({
|
|
58
55
|
method: "PATCH",
|
|
59
56
|
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
60
57
|
body: input,
|
|
@@ -62,21 +59,23 @@ export class Outcomes {
|
|
|
62
59
|
return res.outcome;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
/**
|
|
66
|
-
async
|
|
67
|
-
const res = await this.transport.request<{ outcome: Outcome }>({
|
|
68
|
-
method: "
|
|
69
|
-
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
70
|
-
|
|
62
|
+
/** Retire it (`isActive` → false). Nothing is deleted. Safe to retry. */
|
|
63
|
+
async archive(id: string): Promise<Outcome> {
|
|
64
|
+
const res = await this.transport.request<{ archived: true; outcome: Outcome }>({
|
|
65
|
+
method: "POST",
|
|
66
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}/archive`,
|
|
67
|
+
retry: true,
|
|
71
68
|
});
|
|
72
69
|
return res.outcome;
|
|
73
70
|
}
|
|
74
71
|
|
|
75
|
-
/**
|
|
76
|
-
async
|
|
77
|
-
await this.transport.request({
|
|
78
|
-
method: "
|
|
79
|
-
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
72
|
+
/** Bring an archived one back (`isActive` → true). Safe to retry. */
|
|
73
|
+
async unarchive(id: string): Promise<Outcome> {
|
|
74
|
+
const res = await this.transport.request<{ unarchived: true; outcome: Outcome }>({
|
|
75
|
+
method: "POST",
|
|
76
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}/unarchive`,
|
|
77
|
+
retry: true,
|
|
80
78
|
});
|
|
79
|
+
return res.outcome;
|
|
81
80
|
}
|
|
82
81
|
}
|
package/src/resources/plans.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { activeQuery, filterByActive } from "../filters.js";
|
|
2
2
|
import type { Transport } from "../http.js";
|
|
3
|
-
import type {
|
|
4
|
-
CreatePlanInput,
|
|
5
|
-
Plan,
|
|
6
|
-
PlanDetail,
|
|
7
|
-
UpdatePlanInput,
|
|
8
|
-
} from "../types.js";
|
|
3
|
+
import type { CreatePlanInput, Plan, UpdatePlanInput } from "../types.js";
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
|
-
* The `plans` resource —
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* The `plans` resource — the billing plans customers subscribe to. Each plan
|
|
7
|
+
* is a set of `entitlements` (wallet / credit / outcome / unit / composite /
|
|
8
|
+
* flat lines) billed on a cycle. `/api/v1/plans`.
|
|
9
|
+
*
|
|
10
|
+
* A plan's currency is the workspace's primary currency; it can't be chosen.
|
|
11
|
+
* There is no `delete()` — the API never deletes plans, so customers already
|
|
12
|
+
* on one keep what they bought. Retire a plan with `archive()`; `unarchive()`
|
|
13
|
+
* makes it purchasable again.
|
|
14
14
|
*/
|
|
15
15
|
export class Plans {
|
|
16
16
|
constructor(private readonly transport: Transport) {}
|
|
@@ -27,28 +27,31 @@ export class Plans {
|
|
|
27
27
|
return filterByActive(res.plans, params.active);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
async
|
|
30
|
+
/** One plan's configuration. */
|
|
31
|
+
async get(id: string): Promise<Plan> {
|
|
32
32
|
const res = await this.transport.request<{ plan: Plan }>({
|
|
33
|
-
method: "
|
|
34
|
-
path:
|
|
35
|
-
body: input,
|
|
33
|
+
method: "GET",
|
|
34
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
36
35
|
});
|
|
37
36
|
return res.plan;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
/** Create a plan. It starts active. Not retried on failure — a retry could
|
|
40
|
+
* create a second plan. */
|
|
41
|
+
async create(input: CreatePlanInput): Promise<Plan> {
|
|
42
|
+
const res = await this.transport.request<{ created: true; plan: Plan }>({
|
|
43
|
+
method: "POST",
|
|
44
|
+
path: "/api/v1/plans",
|
|
45
|
+
body: input,
|
|
45
46
|
});
|
|
46
47
|
return res.plan;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
/**
|
|
50
|
+
/** PARTIAL update: only the fields you send change. `entitlements`, when
|
|
51
|
+
* sent, is the complete new list — the list `get()` returned can be sent
|
|
52
|
+
* back as it is. Changes apply to new purchases only. */
|
|
50
53
|
async update(id: string, input: UpdatePlanInput): Promise<Plan> {
|
|
51
|
-
const res = await this.transport.request<{ plan: Plan }>({
|
|
54
|
+
const res = await this.transport.request<{ updated: true; plan: Plan }>({
|
|
52
55
|
method: "PATCH",
|
|
53
56
|
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
54
57
|
body: input,
|
|
@@ -56,21 +59,24 @@ export class Plans {
|
|
|
56
59
|
return res.plan;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
/** Retire a plan (`isActive` → false): it can't be bought any more; customers
|
|
63
|
+
* already on it are unaffected. Safe to retry. */
|
|
64
|
+
async archive(id: string): Promise<Plan> {
|
|
65
|
+
const res = await this.transport.request<{ archived: true; plan: Plan }>({
|
|
66
|
+
method: "POST",
|
|
67
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/archive`,
|
|
68
|
+
retry: true,
|
|
65
69
|
});
|
|
66
70
|
return res.plan;
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
/**
|
|
70
|
-
async
|
|
71
|
-
await this.transport.request({
|
|
72
|
-
method: "
|
|
73
|
-
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
73
|
+
/** Make an archived plan purchasable again (`isActive` → true). Safe to retry. */
|
|
74
|
+
async unarchive(id: string): Promise<Plan> {
|
|
75
|
+
const res = await this.transport.request<{ unarchived: true; plan: Plan }>({
|
|
76
|
+
method: "POST",
|
|
77
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/unarchive`,
|
|
78
|
+
retry: true,
|
|
74
79
|
});
|
|
80
|
+
return res.plan;
|
|
75
81
|
}
|
|
76
82
|
}
|