@planescrm/sdk 0.55.0 → 0.72.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 +145 -3
- package/dist/client.d.mts +6016 -0
- package/dist/client.d.ts +6016 -0
- package/dist/client.js +3255 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +3232 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.d.mts +1787 -35
- package/dist/index.d.ts +1787 -35
- package/dist/index.js +1146 -92
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1136 -92
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.mjs
CHANGED
|
@@ -166,6 +166,9 @@ var HttpClient = class {
|
|
|
166
166
|
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
167
167
|
this.retryOnRateLimit = config.retryOnRateLimit !== false;
|
|
168
168
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
169
|
+
this.getAccessToken = config.getAccessToken;
|
|
170
|
+
this.onUnauthorized = config.onUnauthorized;
|
|
171
|
+
this.fetchImpl = config.fetch;
|
|
169
172
|
if (config.clientId && config.clientSecret) {
|
|
170
173
|
this.oauth = {
|
|
171
174
|
clientId: config.clientId,
|
|
@@ -182,53 +185,68 @@ var HttpClient = class {
|
|
|
182
185
|
let lastError;
|
|
183
186
|
let didRefreshOn401 = false;
|
|
184
187
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
188
|
+
const authHeaders = await this.resolveAuthHeaders();
|
|
185
189
|
const headers = {
|
|
186
190
|
"Content-Type": "application/json",
|
|
187
|
-
...
|
|
191
|
+
...authHeaders,
|
|
188
192
|
...options.headers
|
|
189
193
|
};
|
|
194
|
+
if (options.idempotencyKey) headers["Idempotency-Key"] = options.idempotencyKey;
|
|
195
|
+
const timer = timeoutSignal(this.timeout);
|
|
190
196
|
const fetchOptions = {
|
|
191
197
|
method: options.method,
|
|
192
198
|
headers,
|
|
193
|
-
signal:
|
|
199
|
+
signal: timer.signal
|
|
194
200
|
};
|
|
195
201
|
if (bodyStr !== void 0) {
|
|
196
202
|
fetchOptions.body = bodyStr;
|
|
197
203
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (response.
|
|
201
|
-
|
|
204
|
+
try {
|
|
205
|
+
const response = await (this.fetchImpl ?? fetch)(url, fetchOptions);
|
|
206
|
+
if (response.ok) {
|
|
207
|
+
if (response.status === 204) {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
if (options.responseType === "arrayBuffer") {
|
|
211
|
+
return await response.arrayBuffer();
|
|
212
|
+
}
|
|
213
|
+
return await response.json();
|
|
202
214
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
215
|
+
if (response.status === 429 && this.retryOnRateLimit && attempt < this.maxRetries) {
|
|
216
|
+
const retryAfter = this.parseRetryAfter(response);
|
|
217
|
+
await this.sleep(retryAfter * 1e3);
|
|
218
|
+
continue;
|
|
205
219
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
if (response.status === 401 && this.oauth && !didRefreshOn401) {
|
|
221
|
+
didRefreshOn401 = true;
|
|
222
|
+
await this.refreshTokens();
|
|
223
|
+
attempt--;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (response.status === 401 && this.onUnauthorized && !didRefreshOn401) {
|
|
227
|
+
didRefreshOn401 = true;
|
|
228
|
+
if (await this.recoverSession(authHeaders["Authorization"])) {
|
|
229
|
+
attempt--;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
lastError = await this.mapError(response);
|
|
234
|
+
if (response.status !== 429 || !this.retryOnRateLimit) {
|
|
235
|
+
throw lastError;
|
|
236
|
+
}
|
|
237
|
+
} finally {
|
|
238
|
+
timer.clear();
|
|
222
239
|
}
|
|
223
240
|
}
|
|
224
241
|
throw lastError ?? new RateLimitError();
|
|
225
242
|
}
|
|
226
243
|
getAuthHeaders() {
|
|
244
|
+
if (this.getAccessToken) return {};
|
|
227
245
|
if (this.accessToken) {
|
|
228
246
|
return { Authorization: `Bearer ${this.accessToken}` };
|
|
229
247
|
}
|
|
230
248
|
if (this.apiKey) {
|
|
231
|
-
return {
|
|
249
|
+
return { Authorization: `Bearer ${this.apiKey}` };
|
|
232
250
|
}
|
|
233
251
|
return {};
|
|
234
252
|
}
|
|
@@ -239,6 +257,10 @@ var HttpClient = class {
|
|
|
239
257
|
* the token is within the skew window of expiry.
|
|
240
258
|
*/
|
|
241
259
|
async resolveAuthHeaders() {
|
|
260
|
+
if (this.getAccessToken) {
|
|
261
|
+
const token = await withTimeout(this.getAccessToken(), this.timeout, "getAccessToken");
|
|
262
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
263
|
+
}
|
|
242
264
|
if (!this.oauth) {
|
|
243
265
|
return this.getAuthHeaders();
|
|
244
266
|
}
|
|
@@ -302,6 +324,41 @@ var HttpClient = class {
|
|
|
302
324
|
this.refreshInFlight = void 0;
|
|
303
325
|
}
|
|
304
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Ask the app to refresh its session after a 401. Resolves `true` when the
|
|
329
|
+
* request should be retried.
|
|
330
|
+
* - Requests that got a 401 while a refresh is running share that one call.
|
|
331
|
+
* - A request whose 401 arrives after a refresh already finished (it was
|
|
332
|
+
* sent with the old token) doesn't start a second refresh: if the current
|
|
333
|
+
* token differs from the one it sent, it just retries with the new one.
|
|
334
|
+
* - A refresh that throws, or doesn't settle within the request timeout,
|
|
335
|
+
* counts as "still signed out": the original 401 surfaces.
|
|
336
|
+
*/
|
|
337
|
+
async recoverSession(sentAuthorization) {
|
|
338
|
+
if (!this.onUnauthorized || !this.getAccessToken) return false;
|
|
339
|
+
if (!this.unauthorizedInFlight) {
|
|
340
|
+
try {
|
|
341
|
+
const current = await withTimeout(this.getAccessToken(), this.timeout, "getAccessToken");
|
|
342
|
+
if (current && `Bearer ${current}` !== sentAuthorization) return true;
|
|
343
|
+
} catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (!this.unauthorizedInFlight) {
|
|
348
|
+
const call = this.onUnauthorized;
|
|
349
|
+
const inFlight = (async () => {
|
|
350
|
+
try {
|
|
351
|
+
return await withTimeout(call(), this.timeout, "onUnauthorized");
|
|
352
|
+
} catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
})().finally(() => {
|
|
356
|
+
if (this.unauthorizedInFlight === inFlight) this.unauthorizedInFlight = void 0;
|
|
357
|
+
});
|
|
358
|
+
this.unauthorizedInFlight = inFlight;
|
|
359
|
+
}
|
|
360
|
+
return this.unauthorizedInFlight;
|
|
361
|
+
}
|
|
305
362
|
buildUrl(path, params) {
|
|
306
363
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
307
364
|
if (params) {
|
|
@@ -329,7 +386,8 @@ var HttpClient = class {
|
|
|
329
386
|
body = await response.json();
|
|
330
387
|
} catch {
|
|
331
388
|
}
|
|
332
|
-
const
|
|
389
|
+
const envelope = body["error"];
|
|
390
|
+
const message = (typeof body["message"] === "string" ? body["message"] : void 0) ?? (typeof envelope?.message === "string" ? envelope.message : void 0) ?? response.statusText ?? "Request failed";
|
|
333
391
|
switch (response.status) {
|
|
334
392
|
case 400: {
|
|
335
393
|
const fieldErrors = body["errors"] ?? [];
|
|
@@ -356,6 +414,29 @@ var HttpClient = class {
|
|
|
356
414
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
357
415
|
}
|
|
358
416
|
};
|
|
417
|
+
function timeoutSignal(ms) {
|
|
418
|
+
if (typeof AbortSignal.timeout === "function") {
|
|
419
|
+
return { signal: AbortSignal.timeout(ms), clear: () => void 0 };
|
|
420
|
+
}
|
|
421
|
+
const controller = new AbortController();
|
|
422
|
+
const timer = setTimeout(() => controller.abort(), ms);
|
|
423
|
+
return { signal: controller.signal, clear: () => clearTimeout(timer) };
|
|
424
|
+
}
|
|
425
|
+
function withTimeout(promise, ms, what) {
|
|
426
|
+
return new Promise((resolve, reject) => {
|
|
427
|
+
const timer = setTimeout(() => reject(new Error(`${what} did not settle within ${ms}ms`)), ms);
|
|
428
|
+
Promise.resolve(promise).then(
|
|
429
|
+
(value) => {
|
|
430
|
+
clearTimeout(timer);
|
|
431
|
+
resolve(value);
|
|
432
|
+
},
|
|
433
|
+
(err) => {
|
|
434
|
+
clearTimeout(timer);
|
|
435
|
+
reject(err);
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
359
440
|
|
|
360
441
|
// src/resources/base-resource.ts
|
|
361
442
|
var ApiResource = class {
|
|
@@ -385,13 +466,16 @@ var BaseResource = class extends ApiResource {
|
|
|
385
466
|
});
|
|
386
467
|
}
|
|
387
468
|
/**
|
|
388
|
-
* Create a new entity.
|
|
469
|
+
* Create a new entity. Pass `idempotencyKey` (a UUID you keep for retries) to
|
|
470
|
+
* make a retry safe on routes that honor it (customers, opportunities, contacts,
|
|
471
|
+
* tasks): a repeat returns the first response and creates nothing.
|
|
389
472
|
*/
|
|
390
|
-
async create(data) {
|
|
473
|
+
async create(data, options) {
|
|
391
474
|
return this.http.request({
|
|
392
475
|
method: "POST",
|
|
393
476
|
path: this.basePath,
|
|
394
|
-
body: data
|
|
477
|
+
body: data,
|
|
478
|
+
...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
|
|
395
479
|
});
|
|
396
480
|
}
|
|
397
481
|
/**
|
|
@@ -421,6 +505,12 @@ function toQueryParams(params) {
|
|
|
421
505
|
if (!params) return out;
|
|
422
506
|
for (const [key, value] of Object.entries(params)) {
|
|
423
507
|
if (value === void 0 || value === null) continue;
|
|
508
|
+
if (key === "customFields" && typeof value === "object" && !Array.isArray(value)) {
|
|
509
|
+
for (const [field, v] of Object.entries(value)) {
|
|
510
|
+
if (v !== void 0 && v !== null) out[`cf.${field}`] = String(v);
|
|
511
|
+
}
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
424
514
|
if (Array.isArray(value)) {
|
|
425
515
|
if (value.length === 0) continue;
|
|
426
516
|
out[key] = value.map(String).join(",");
|
|
@@ -447,6 +537,16 @@ var OpportunityResource = class extends BaseResource {
|
|
|
447
537
|
params: toQueryParams(params)
|
|
448
538
|
});
|
|
449
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* GET /opportunities/trade-options — the active trades a client can request
|
|
542
|
+
* (`requestedTradeIds` on create/update), sorted by name.
|
|
543
|
+
*/
|
|
544
|
+
async tradeOptions() {
|
|
545
|
+
return this.http.request({
|
|
546
|
+
method: "GET",
|
|
547
|
+
path: `${this.basePath}/trade-options`
|
|
548
|
+
});
|
|
549
|
+
}
|
|
450
550
|
/** PUT /opportunities/{id} — partial update; stage moves go through workflow transitions. */
|
|
451
551
|
async update(id, data) {
|
|
452
552
|
return this.http.request({
|
|
@@ -455,6 +555,21 @@ var OpportunityResource = class extends BaseResource {
|
|
|
455
555
|
body: data
|
|
456
556
|
});
|
|
457
557
|
}
|
|
558
|
+
/** GET /opportunities/:id/transitions — the pipeline moves available from the current stage, for the caller's role. */
|
|
559
|
+
async listTransitions(id) {
|
|
560
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}/transitions` });
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* POST /opportunities/:id/transition — take one pipeline move, as the stage button does: required
|
|
564
|
+
* fields are checked, the move is logged, and the stage's automations run (they can send email).
|
|
565
|
+
*/
|
|
566
|
+
async transition(id, data) {
|
|
567
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/transition`, body: data });
|
|
568
|
+
}
|
|
569
|
+
/** GET /opportunities/:id/workflow — the pipeline instance with its transition history (404 if none). */
|
|
570
|
+
async getWorkflow(id) {
|
|
571
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}/workflow` });
|
|
572
|
+
}
|
|
458
573
|
};
|
|
459
574
|
|
|
460
575
|
// src/resources/customers.ts
|
|
@@ -501,6 +616,19 @@ var CustomerResource = class extends BaseResource {
|
|
|
501
616
|
path: `/customers/${winnerId}/merge/${loserId}`
|
|
502
617
|
});
|
|
503
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* POST /customers/{id}/convert-to-company — turn a person customer into a
|
|
621
|
+
* company. The person becomes the company's primary contact (their email and
|
|
622
|
+
* phone, with do-not-contact and marketing opt-outs carried over). A customer
|
|
623
|
+
* that's already a company is a 409 `ALREADY_COMPANY`.
|
|
624
|
+
*/
|
|
625
|
+
async convertToCompany(id, data = {}) {
|
|
626
|
+
return this.http.request({
|
|
627
|
+
method: "POST",
|
|
628
|
+
path: `${this.basePath}/${id}/convert-to-company`,
|
|
629
|
+
body: data
|
|
630
|
+
});
|
|
631
|
+
}
|
|
504
632
|
};
|
|
505
633
|
|
|
506
634
|
// src/resources/properties.ts
|
|
@@ -539,6 +667,17 @@ var PropertyResource = class extends ApiResource {
|
|
|
539
667
|
body: data
|
|
540
668
|
});
|
|
541
669
|
}
|
|
670
|
+
/** GET /customers/:customerId/properties/:id — one property, checked against its customer (404 if it isn't theirs). */
|
|
671
|
+
async getForCustomer(customerId, id) {
|
|
672
|
+
return this.http.request({
|
|
673
|
+
method: "GET",
|
|
674
|
+
path: `/customers/${customerId}/properties/${id}`
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
/** POST /properties/:id/geocode — queue a fresh geocode of the property's address. */
|
|
678
|
+
async geocode(id) {
|
|
679
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/geocode` });
|
|
680
|
+
}
|
|
542
681
|
/** DELETE /customers/{customerId}/properties/{id} — soft delete (204). */
|
|
543
682
|
async delete(customerId, id) {
|
|
544
683
|
await this.http.request({
|
|
@@ -554,7 +693,11 @@ var CalendarEventResource = class extends BaseResource {
|
|
|
554
693
|
super(http2, "/events");
|
|
555
694
|
}
|
|
556
695
|
async list(params) {
|
|
557
|
-
return
|
|
696
|
+
return this.http.request({
|
|
697
|
+
method: "GET",
|
|
698
|
+
path: this.basePath,
|
|
699
|
+
params: params ?? {}
|
|
700
|
+
});
|
|
558
701
|
}
|
|
559
702
|
/**
|
|
560
703
|
* PATCH /events/:id. For an occurrence of a recurring event, `editMode`
|
|
@@ -573,7 +716,8 @@ var CalendarEventResource = class extends BaseResource {
|
|
|
573
716
|
* POST /events/:id/complete — marks the event completed and runs its "on
|
|
574
717
|
* complete" transition, best-effort. The event is completed even when the
|
|
575
718
|
* transition can't run; `workflow_trigger` says whether it fired, was
|
|
576
|
-
* skipped (none set
|
|
719
|
+
* skipped (none set, or `handled_by_pipeline_rule`: the pipeline's rule
|
|
720
|
+
* moves the record instead) or failed (with a reason).
|
|
577
721
|
*/
|
|
578
722
|
async complete(id, body = {}) {
|
|
579
723
|
return this.http.request({
|
|
@@ -582,6 +726,34 @@ var CalendarEventResource = class extends BaseResource {
|
|
|
582
726
|
body
|
|
583
727
|
});
|
|
584
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* POST /events/:id/cancel. For an occurrence of a recurring event, `editMode`
|
|
731
|
+
* picks which occurrences are cancelled (`this` when omitted). 409 if already
|
|
732
|
+
* cancelled; 422 for a completed event.
|
|
733
|
+
*/
|
|
734
|
+
async cancel(id, options = {}) {
|
|
735
|
+
return this.http.request({
|
|
736
|
+
method: "POST",
|
|
737
|
+
path: `${this.basePath}/${id}/cancel`,
|
|
738
|
+
params: { recurrence_edit_mode: options.editMode }
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/** POST /events/:id/participants — add a user, contact, crew or agent to the event. No invite is sent. */
|
|
742
|
+
async addParticipant(id, data) {
|
|
743
|
+
return this.http.request({
|
|
744
|
+
method: "POST",
|
|
745
|
+
path: `${this.basePath}/${id}/participants`,
|
|
746
|
+
body: data
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
/** PATCH /events/:id/participants/:participantId — set a participant's response. */
|
|
750
|
+
async updateParticipantStatus(id, participantId, status) {
|
|
751
|
+
return this.http.request({
|
|
752
|
+
method: "PATCH",
|
|
753
|
+
path: `${this.basePath}/${id}/participants/${participantId}`,
|
|
754
|
+
body: { status }
|
|
755
|
+
});
|
|
756
|
+
}
|
|
585
757
|
};
|
|
586
758
|
|
|
587
759
|
// src/resources/contracts.ts
|
|
@@ -589,6 +761,73 @@ var ContractResource = class extends BaseResource {
|
|
|
589
761
|
constructor(http2) {
|
|
590
762
|
super(http2, "/contracts");
|
|
591
763
|
}
|
|
764
|
+
/** POST /contracts/for-subject — a bare agreement for a customer or a service schedule (no opportunity, no line groups). */
|
|
765
|
+
async createForSubject(data) {
|
|
766
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/for-subject`, body: data });
|
|
767
|
+
}
|
|
768
|
+
/** POST /contracts/flat — a contract on an opportunity with a manually entered total and a contract template, no estimates. */
|
|
769
|
+
async createFlat(data) {
|
|
770
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/flat`, body: data });
|
|
771
|
+
}
|
|
772
|
+
/** POST /contracts/{id}/change-orders — a change order on a signed or fulfilled contract (409 otherwise). */
|
|
773
|
+
async createChangeOrder(id, data) {
|
|
774
|
+
return this.http.request({
|
|
775
|
+
method: "POST",
|
|
776
|
+
path: `${this.basePath}/${id}/change-orders`,
|
|
777
|
+
body: data
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* GET /contracts/{id}/change-orders — the change orders written against a
|
|
782
|
+
* contract, oldest first, each with its line groups. A change order has none
|
|
783
|
+
* of its own, so asking for one returns an empty list.
|
|
784
|
+
*/
|
|
785
|
+
async listChangeOrders(id) {
|
|
786
|
+
return this.http.request({
|
|
787
|
+
method: "GET",
|
|
788
|
+
path: `${this.basePath}/${id}/change-orders`
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
/** POST /contracts/{id}/submit — submit for approval; auto-approved when under the tenant's threshold. */
|
|
792
|
+
async submit(id) {
|
|
793
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/submit` });
|
|
794
|
+
}
|
|
795
|
+
/** POST /contracts/{id}/approve — approve a contract pending approval (needs the approve grant). */
|
|
796
|
+
async approve(id) {
|
|
797
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/approve` });
|
|
798
|
+
}
|
|
799
|
+
/** POST /contracts/{id}/cancel — cancel a contract in any state before it's signed. */
|
|
800
|
+
async cancel(id) {
|
|
801
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/cancel` });
|
|
802
|
+
}
|
|
803
|
+
/** POST /contracts/{id}/reopen — return a kicked-back contract to draft for review and re-send. */
|
|
804
|
+
async reopen(id) {
|
|
805
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/reopen` });
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* POST /contracts/{id}/send — **emails the signer a "Review & Sign" link.**
|
|
809
|
+
* Snapshots the clauses and creates the signing token. An empty body
|
|
810
|
+
* addresses the customer; `signerContactId` sends it to that contact instead.
|
|
811
|
+
* The first send of a contract uses one unit of the tenant's e-signature
|
|
812
|
+
* allowance; a re-send doesn't. Not safe to retry blindly after a timeout: a
|
|
813
|
+
* repeat emails again.
|
|
814
|
+
*/
|
|
815
|
+
async send(id, data = {}) {
|
|
816
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/send`, body: data });
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* GET /contracts/{id}/pdf — the contract PDF bytes. `type: 'signed'` returns
|
|
820
|
+
* the stored signed copy (404 until it's signed); otherwise a preview renders.
|
|
821
|
+
*/
|
|
822
|
+
async downloadPdf(id, options = {}) {
|
|
823
|
+
return this.http.request({
|
|
824
|
+
method: "GET",
|
|
825
|
+
path: `${this.basePath}/${id}/pdf`,
|
|
826
|
+
params: options.type ? { type: options.type } : {},
|
|
827
|
+
headers: { Accept: "application/pdf" },
|
|
828
|
+
responseType: "arrayBuffer"
|
|
829
|
+
});
|
|
830
|
+
}
|
|
592
831
|
};
|
|
593
832
|
|
|
594
833
|
// src/resources/jobs.ts
|
|
@@ -658,6 +897,28 @@ var JobResource = class extends ApiResource {
|
|
|
658
897
|
path: "/jobs/trade-coverage"
|
|
659
898
|
});
|
|
660
899
|
}
|
|
900
|
+
/** GET /jobs/{id}/workflow — the job's pipeline instance with its transition history (404 if none). */
|
|
901
|
+
async getWorkflow(id) {
|
|
902
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}/workflow` });
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* POST /jobs/{id}/transition — take one pipeline move, as the stage button
|
|
906
|
+
* does: required fields are checked, the move is logged, and **the
|
|
907
|
+
* transition's actions run**. A tenant can attach actions that create or send
|
|
908
|
+
* an invoice, charge a saved card, send a contract, or send email, SMS or a
|
|
909
|
+
* webhook. Not safe to retry blindly after a timeout: check `getWorkflow()`
|
|
910
|
+
* first, because a repeat runs the actions again.
|
|
911
|
+
*/
|
|
912
|
+
async transition(id, data) {
|
|
913
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/transition`, body: data });
|
|
914
|
+
}
|
|
915
|
+
/** GET /jobs/{id}/transitions — the pipeline moves available from the job's current stage, for the caller's role. */
|
|
916
|
+
async listTransitions(id) {
|
|
917
|
+
return this.http.request({
|
|
918
|
+
method: "GET",
|
|
919
|
+
path: `${this.basePath}/${id}/transitions`
|
|
920
|
+
});
|
|
921
|
+
}
|
|
661
922
|
};
|
|
662
923
|
|
|
663
924
|
// src/resources/estimates.ts
|
|
@@ -715,6 +976,74 @@ var EstimateResource = class extends ApiResource {
|
|
|
715
976
|
path: `/jobs/${jobId}/estimates/${id}`
|
|
716
977
|
});
|
|
717
978
|
}
|
|
979
|
+
/** GET /estimates/{id}/workflow — the estimate's pipeline instance with its transition history (404 if none). */
|
|
980
|
+
async getWorkflow(id) {
|
|
981
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}/workflow` });
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* POST /estimates/{id}/transition — take one pipeline move. **The
|
|
985
|
+
* transition's actions run**, as for `jobs.transition()`: a tenant can attach
|
|
986
|
+
* actions that bill, charge, send a contract, or send email, SMS or a webhook.
|
|
987
|
+
* Not safe to retry blindly after a timeout: check `getWorkflow()` first.
|
|
988
|
+
*/
|
|
989
|
+
async transition(id, data) {
|
|
990
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/transition`, body: data });
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* PUT /jobs/{jobId}/estimates/{id}/status — move the estimate's status (see
|
|
994
|
+
* `UpdateEstimateStatusDto` for the allowed moves). The route itself sends
|
|
995
|
+
* nothing and creates no contract or invoice; a tenant's webhooks or
|
|
996
|
+
* automations on the status change can.
|
|
997
|
+
*/
|
|
998
|
+
async updateStatus(jobId, id, data) {
|
|
999
|
+
return this.http.request({
|
|
1000
|
+
method: "PUT",
|
|
1001
|
+
path: `/jobs/${jobId}/estimates/${id}/status`,
|
|
1002
|
+
body: data
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
/** GET /estimates/{id}/transitions — the pipeline moves available from the estimate's current stage, for the caller's role. */
|
|
1006
|
+
async listTransitions(id) {
|
|
1007
|
+
return this.http.request({
|
|
1008
|
+
method: "GET",
|
|
1009
|
+
path: `${this.basePath}/${id}/transitions`
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* POST /jobs/{jobId}/estimates/{estimateId}/line-items — add a line item. With
|
|
1014
|
+
* `materialCatalogId` or `tradeLaborItemId`, the catalog fills in description,
|
|
1015
|
+
* unit and price. The estimate's subtotal and total are recalculated.
|
|
1016
|
+
*/
|
|
1017
|
+
async createLineItem(jobId, estimateId, data) {
|
|
1018
|
+
return this.http.request({
|
|
1019
|
+
method: "POST",
|
|
1020
|
+
path: `/jobs/${jobId}/estimates/${estimateId}/line-items`,
|
|
1021
|
+
body: data
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
/** PUT /jobs/{jobId}/estimates/{estimateId}/line-items/{id} — partial update; totals are recalculated. */
|
|
1025
|
+
async updateLineItem(jobId, estimateId, id, data) {
|
|
1026
|
+
return this.http.request({
|
|
1027
|
+
method: "PUT",
|
|
1028
|
+
path: `/jobs/${jobId}/estimates/${estimateId}/line-items/${id}`,
|
|
1029
|
+
body: data
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
/** DELETE /jobs/{jobId}/estimates/{estimateId}/line-items/{id} — totals are recalculated. */
|
|
1033
|
+
async deleteLineItem(jobId, estimateId, id) {
|
|
1034
|
+
await this.http.request({
|
|
1035
|
+
method: "DELETE",
|
|
1036
|
+
path: `/jobs/${jobId}/estimates/${estimateId}/line-items/${id}`
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
/** PUT /jobs/{jobId}/estimates/{estimateId}/line-items/reorder — set each item's `sortOrder` (and optionally `section`) in one call. */
|
|
1040
|
+
async reorderLineItems(jobId, estimateId, data) {
|
|
1041
|
+
return this.http.request({
|
|
1042
|
+
method: "PUT",
|
|
1043
|
+
path: `/jobs/${jobId}/estimates/${estimateId}/line-items/reorder`,
|
|
1044
|
+
body: data
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
718
1047
|
};
|
|
719
1048
|
|
|
720
1049
|
// src/resources/invoices.ts
|
|
@@ -777,6 +1106,24 @@ var InvoiceResource = class extends BaseResource {
|
|
|
777
1106
|
params
|
|
778
1107
|
});
|
|
779
1108
|
}
|
|
1109
|
+
/** GET /admin/invoices/{invoiceId}/payments — every payment recorded against the invoice (Read Payment). */
|
|
1110
|
+
async listPayments(invoiceId) {
|
|
1111
|
+
return this.http.request({
|
|
1112
|
+
method: "GET",
|
|
1113
|
+
path: `${this.basePath}/${invoiceId}/payments`
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* GET /admin/payments/recording-settings — the tenant's resolved
|
|
1118
|
+
* payment-recording settings (whether manual recording is on, and which
|
|
1119
|
+
* methods). Read Invoice.
|
|
1120
|
+
*/
|
|
1121
|
+
async getPaymentRecordingSettings() {
|
|
1122
|
+
return this.http.request({
|
|
1123
|
+
method: "GET",
|
|
1124
|
+
path: "/admin/payments/recording-settings"
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
780
1127
|
/** GET /admin/invoices/overdue/summary — `{ count, total }` of every overdue invoice. Read Invoice. */
|
|
781
1128
|
async overdueSummary() {
|
|
782
1129
|
return this.http.request({
|
|
@@ -806,33 +1153,72 @@ var InventoryResource = class extends BaseResource {
|
|
|
806
1153
|
body: data
|
|
807
1154
|
});
|
|
808
1155
|
}
|
|
1156
|
+
// ---- Material used on a job (E64-S9). The routes live under /jobs/{jobId}. ----
|
|
1157
|
+
/**
|
|
1158
|
+
* POST /jobs/{jobId}/consumption — record stock drawn from a location for a
|
|
1159
|
+
* job. Each line lowers that location's on-hand quantity.
|
|
1160
|
+
*/
|
|
1161
|
+
async recordJobConsumption(jobId, data) {
|
|
1162
|
+
return this.http.request({
|
|
1163
|
+
method: "POST",
|
|
1164
|
+
path: `/jobs/${jobId}/consumption`,
|
|
1165
|
+
body: data
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* GET /jobs/{jobId}/consumption — one page of the job's consume and return
|
|
1170
|
+
* movements (newest first by default), the job's totals across every page,
|
|
1171
|
+
* and page metadata. The rows are under `items`, not `data`.
|
|
1172
|
+
*/
|
|
1173
|
+
async listJobConsumption(jobId, params) {
|
|
1174
|
+
return this.http.request({
|
|
1175
|
+
method: "GET",
|
|
1176
|
+
path: `/jobs/${jobId}/consumption`,
|
|
1177
|
+
params: toQueryParams(params)
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
/** POST /jobs/{jobId}/return — put unused stock from a job back into a location. */
|
|
1181
|
+
async returnFromJob(jobId, data) {
|
|
1182
|
+
return this.http.request({
|
|
1183
|
+
method: "POST",
|
|
1184
|
+
path: `/jobs/${jobId}/return`,
|
|
1185
|
+
body: data
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
/** GET /jobs/{jobId}/material-variance — estimated vs actual material for the job. */
|
|
1189
|
+
async getJobMaterialVariance(jobId) {
|
|
1190
|
+
return this.http.request({
|
|
1191
|
+
method: "GET",
|
|
1192
|
+
path: `/jobs/${jobId}/material-variance`
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
809
1195
|
};
|
|
810
1196
|
|
|
811
1197
|
// src/resources/contacts.ts
|
|
812
1198
|
var ContactChannelResource = class {
|
|
813
1199
|
constructor(http2, contactId) {
|
|
814
1200
|
this.http = http2;
|
|
815
|
-
this.
|
|
1201
|
+
this.contactId = contactId;
|
|
816
1202
|
}
|
|
817
1203
|
/** Returns `{ channel, duplicateOf }` — `duplicateOf` is the dedup signal. */
|
|
818
1204
|
async create(data) {
|
|
819
1205
|
return this.http.request({
|
|
820
1206
|
method: "POST",
|
|
821
|
-
path: this.
|
|
1207
|
+
path: `/contacts/${this.contactId}/channels`,
|
|
822
1208
|
body: data
|
|
823
1209
|
});
|
|
824
1210
|
}
|
|
825
1211
|
async update(channelId, data) {
|
|
826
1212
|
return this.http.request({
|
|
827
1213
|
method: "PATCH",
|
|
828
|
-
path:
|
|
1214
|
+
path: `/contacts/${this.contactId}/channels/${channelId}`,
|
|
829
1215
|
body: data
|
|
830
1216
|
});
|
|
831
1217
|
}
|
|
832
1218
|
async delete(channelId) {
|
|
833
1219
|
await this.http.request({
|
|
834
1220
|
method: "DELETE",
|
|
835
|
-
path:
|
|
1221
|
+
path: `/contacts/${this.contactId}/channels/${channelId}`
|
|
836
1222
|
});
|
|
837
1223
|
}
|
|
838
1224
|
};
|
|
@@ -860,6 +1246,46 @@ var ContactResource = class extends BaseResource {
|
|
|
860
1246
|
path: `/contacts/${winnerId}/merge/${loserId}`
|
|
861
1247
|
});
|
|
862
1248
|
}
|
|
1249
|
+
/** GET /contacts/roles — the distinct roles in use on active links, for a role picker. */
|
|
1250
|
+
async listRoles() {
|
|
1251
|
+
return this.http.request({ method: "GET", path: "/contacts/roles" });
|
|
1252
|
+
}
|
|
1253
|
+
/** GET /contacts/:id/links — every link, active and ended (history). `get()` returns active links only. */
|
|
1254
|
+
async listLinks(contactId) {
|
|
1255
|
+
return this.http.request({ method: "GET", path: `/contacts/${contactId}/links` });
|
|
1256
|
+
}
|
|
1257
|
+
/** POST /contacts/:id/links — link the contact to a customer or property. */
|
|
1258
|
+
async addLink(contactId, data) {
|
|
1259
|
+
return this.http.request({ method: "POST", path: `/contacts/${contactId}/links`, body: data });
|
|
1260
|
+
}
|
|
1261
|
+
/** PATCH /contacts/:id/links/:linkId — change role/primary, or end the link (`endedAt`). */
|
|
1262
|
+
async updateLink(contactId, linkId, data) {
|
|
1263
|
+
return this.http.request({
|
|
1264
|
+
method: "PATCH",
|
|
1265
|
+
path: `/contacts/${contactId}/links/${linkId}`,
|
|
1266
|
+
body: data
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
/** POST /contacts/:id/workflow/start — opt the contact into the relationship pipeline (409 if started). */
|
|
1270
|
+
async startWorkflow(contactId) {
|
|
1271
|
+
return this.http.request({ method: "POST", path: `/contacts/${contactId}/workflow/start` });
|
|
1272
|
+
}
|
|
1273
|
+
/** GET /contacts/:id/workflow — current stage and the transitions available from it. */
|
|
1274
|
+
async getWorkflow(contactId) {
|
|
1275
|
+
return this.http.request({ method: "GET", path: `/contacts/${contactId}/workflow` });
|
|
1276
|
+
}
|
|
1277
|
+
/** POST /contacts/:id/workflow/transitions/:transitionId — 422 for an invalid move or unmet required fields. */
|
|
1278
|
+
async transition(contactId, transitionId) {
|
|
1279
|
+
return this.http.request({
|
|
1280
|
+
method: "POST",
|
|
1281
|
+
path: `/contacts/${contactId}/workflow/transitions/${transitionId}`,
|
|
1282
|
+
body: {}
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
/** GET /contacts/:id/workflow/history */
|
|
1286
|
+
async getWorkflowHistory(contactId) {
|
|
1287
|
+
return this.http.request({ method: "GET", path: `/contacts/${contactId}/workflow/history` });
|
|
1288
|
+
}
|
|
863
1289
|
channels(contactId) {
|
|
864
1290
|
return new ContactChannelResource(this.http, contactId);
|
|
865
1291
|
}
|
|
@@ -1106,6 +1532,14 @@ var TagResource = class extends ApiResource {
|
|
|
1106
1532
|
async delete(id) {
|
|
1107
1533
|
await this.http.request({ method: "DELETE", path: `${this.basePath}/${id}` });
|
|
1108
1534
|
}
|
|
1535
|
+
/**
|
|
1536
|
+
* POST /tags/:winnerId/merge/:loserId — fold a duplicate tag into another: records
|
|
1537
|
+
* move to the winner (`retagged`), duplicates are dropped (`dropped`), and the
|
|
1538
|
+
* loser is deleted. Irreversible.
|
|
1539
|
+
*/
|
|
1540
|
+
async merge(winnerId, loserId) {
|
|
1541
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${winnerId}/merge/${loserId}` });
|
|
1542
|
+
}
|
|
1109
1543
|
/** `TagsService.setEntityTags()` returns the final tag set wrapped in `{ tags }`. */
|
|
1110
1544
|
async setEntityTags(entityType, entityId, data) {
|
|
1111
1545
|
return this.http.request({
|
|
@@ -1114,6 +1548,31 @@ var TagResource = class extends ApiResource {
|
|
|
1114
1548
|
body: data
|
|
1115
1549
|
});
|
|
1116
1550
|
}
|
|
1551
|
+
/**
|
|
1552
|
+
* POST /entities/:type/:id/tags — add ONE tag, keeping the record's other tags.
|
|
1553
|
+
* Safe to call concurrently with other tag changes on the same record (unlike
|
|
1554
|
+
* read-modify-write through `setEntityTags()`). Adding a tag the record already
|
|
1555
|
+
* carries is a no-op. Returns the record's full tag set.
|
|
1556
|
+
*/
|
|
1557
|
+
async addEntityTag(entityType, entityId, ref) {
|
|
1558
|
+
const body = ref.tagId !== void 0 ? { tag_id: ref.tagId } : { tag_name: ref.tagName };
|
|
1559
|
+
return this.http.request({
|
|
1560
|
+
method: "POST",
|
|
1561
|
+
path: `/entities/${entityType}/${entityId}/tags`,
|
|
1562
|
+
body
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* DELETE /entities/:type/:id/tags/:tagId — remove ONE tag, keeping the rest.
|
|
1567
|
+
* Removing a tag the record doesn't carry is a no-op. Returns the record's
|
|
1568
|
+
* full tag set.
|
|
1569
|
+
*/
|
|
1570
|
+
async removeEntityTag(entityType, entityId, tagId) {
|
|
1571
|
+
return this.http.request({
|
|
1572
|
+
method: "DELETE",
|
|
1573
|
+
path: `/entities/${entityType}/${entityId}/tags/${tagId}`
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1117
1576
|
async getEntityTags(entityType, entityId) {
|
|
1118
1577
|
return this.http.request({
|
|
1119
1578
|
method: "GET",
|
|
@@ -1129,6 +1588,111 @@ var TagResource = class extends ApiResource {
|
|
|
1129
1588
|
}
|
|
1130
1589
|
};
|
|
1131
1590
|
|
|
1591
|
+
// src/resources/lists.ts
|
|
1592
|
+
var ListsResource = class extends ApiResource {
|
|
1593
|
+
constructor(http2) {
|
|
1594
|
+
super(http2, "/lists");
|
|
1595
|
+
}
|
|
1596
|
+
/** GET /lists — every list, newest first, each with `member_count`. A bare array. */
|
|
1597
|
+
async list() {
|
|
1598
|
+
return this.http.request({ method: "GET", path: this.basePath });
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* GET /lists/filter-fields — the standard fields, their types and the operators
|
|
1602
|
+
* each accepts for a smart list of this entity type, and whether custom fields
|
|
1603
|
+
* are accepted (E66-S3). A save that breaks these rules is a 400.
|
|
1604
|
+
*/
|
|
1605
|
+
async filterFields(entityType) {
|
|
1606
|
+
return this.http.request({
|
|
1607
|
+
method: "GET",
|
|
1608
|
+
path: `${this.basePath}/filter-fields`,
|
|
1609
|
+
params: { entity_type: entityType }
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
/** GET /lists/:id — one list with an on-demand `member_count`. */
|
|
1613
|
+
async get(id) {
|
|
1614
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}` });
|
|
1615
|
+
}
|
|
1616
|
+
/** POST /lists */
|
|
1617
|
+
async create(data) {
|
|
1618
|
+
return this.http.request({ method: "POST", path: this.basePath, body: data });
|
|
1619
|
+
}
|
|
1620
|
+
/** PATCH /lists/:id — rename, or replace a smart list's definition. */
|
|
1621
|
+
async update(id, data) {
|
|
1622
|
+
return this.http.request({ method: "PATCH", path: `${this.basePath}/${id}`, body: data });
|
|
1623
|
+
}
|
|
1624
|
+
/** DELETE /lists/:id — soft delete; the records on the list are untouched. */
|
|
1625
|
+
async delete(id) {
|
|
1626
|
+
await this.http.request({ method: "DELETE", path: `${this.basePath}/${id}` });
|
|
1627
|
+
}
|
|
1628
|
+
/** POST /lists/:id/members — static lists only; idempotent on duplicates. */
|
|
1629
|
+
async addMembers(id, members) {
|
|
1630
|
+
return this.http.request({
|
|
1631
|
+
method: "POST",
|
|
1632
|
+
path: `${this.basePath}/${id}/members`,
|
|
1633
|
+
body: { members }
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
/** DELETE /lists/:id/members — static lists only. */
|
|
1637
|
+
async removeMembers(id, members) {
|
|
1638
|
+
return this.http.request({
|
|
1639
|
+
method: "DELETE",
|
|
1640
|
+
path: `${this.basePath}/${id}/members`,
|
|
1641
|
+
body: { members }
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
/** GET /lists/:id/recipients — people (audience, contact and customer lists), with consent. */
|
|
1645
|
+
async getRecipients(id, params) {
|
|
1646
|
+
return this.http.request({
|
|
1647
|
+
method: "GET",
|
|
1648
|
+
path: `${this.basePath}/${id}/recipients`,
|
|
1649
|
+
params
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
/** POST /lists/:id/export — queues a CSV export; a notification links the file when it's ready. */
|
|
1653
|
+
async export(id, options) {
|
|
1654
|
+
return this.http.request({
|
|
1655
|
+
method: "POST",
|
|
1656
|
+
path: `${this.basePath}/${id}/export`,
|
|
1657
|
+
body: options ?? {}
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1662
|
+
// src/resources/lead-sources.ts
|
|
1663
|
+
var LeadSourcesResource = class extends ApiResource {
|
|
1664
|
+
constructor(http2) {
|
|
1665
|
+
super(http2, "/lead-sources");
|
|
1666
|
+
}
|
|
1667
|
+
/** GET /lead-sources — active ones by default, in display order. */
|
|
1668
|
+
async list(options) {
|
|
1669
|
+
const res = await this.http.request({
|
|
1670
|
+
method: "GET",
|
|
1671
|
+
path: this.basePath,
|
|
1672
|
+
...options?.includeInactive ? { params: { includeInactive: "true" } } : {}
|
|
1673
|
+
});
|
|
1674
|
+
return res.data;
|
|
1675
|
+
}
|
|
1676
|
+
/** POST /lead-sources — 409 on a duplicate key. */
|
|
1677
|
+
async create(data) {
|
|
1678
|
+
const res = await this.http.request({ method: "POST", path: this.basePath, body: data });
|
|
1679
|
+
return res.data;
|
|
1680
|
+
}
|
|
1681
|
+
/** PATCH /lead-sources/:id — label, active flag or order; the key can't change. */
|
|
1682
|
+
async update(id, data) {
|
|
1683
|
+
const res = await this.http.request({
|
|
1684
|
+
method: "PATCH",
|
|
1685
|
+
path: `${this.basePath}/${id}`,
|
|
1686
|
+
body: data
|
|
1687
|
+
});
|
|
1688
|
+
return res.data;
|
|
1689
|
+
}
|
|
1690
|
+
/** DELETE /lead-sources/:id — soft delete. */
|
|
1691
|
+
async delete(id) {
|
|
1692
|
+
await this.http.request({ method: "DELETE", path: `${this.basePath}/${id}` });
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
|
|
1132
1696
|
// src/resources/tasks.ts
|
|
1133
1697
|
var TaskResource = class extends ApiResource {
|
|
1134
1698
|
constructor(http2) {
|
|
@@ -1145,9 +1709,14 @@ var TaskResource = class extends ApiResource {
|
|
|
1145
1709
|
async get(id) {
|
|
1146
1710
|
return this.http.request({ method: "GET", path: `${this.basePath}/${id}` });
|
|
1147
1711
|
}
|
|
1148
|
-
/** POST /tasks */
|
|
1149
|
-
async create(data) {
|
|
1150
|
-
return this.http.request({
|
|
1712
|
+
/** POST /tasks. Pass `idempotencyKey` to make a retry safe (E64-S6). */
|
|
1713
|
+
async create(data, options) {
|
|
1714
|
+
return this.http.request({
|
|
1715
|
+
method: "POST",
|
|
1716
|
+
path: this.basePath,
|
|
1717
|
+
body: data,
|
|
1718
|
+
...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
|
|
1719
|
+
});
|
|
1151
1720
|
}
|
|
1152
1721
|
/** PATCH /tasks/:id — non-terminal fields only; terminal status changes use the action methods. */
|
|
1153
1722
|
async update(id, data) {
|
|
@@ -1342,70 +1911,220 @@ var InvoiceSettingsResource = class {
|
|
|
1342
1911
|
path: `${this.basePath}/overdue-offsets`,
|
|
1343
1912
|
body
|
|
1344
1913
|
});
|
|
1345
|
-
return res.offsets;
|
|
1914
|
+
return res.offsets;
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* GET /admin/invoice-settings/attach-pdf — whether invoice emails carry the
|
|
1918
|
+
* invoice PDF (on unless an admin turned it off).
|
|
1919
|
+
*/
|
|
1920
|
+
async getAttachPdf() {
|
|
1921
|
+
const res = await this.http.request({
|
|
1922
|
+
method: "GET",
|
|
1923
|
+
path: `${this.basePath}/attach-pdf`
|
|
1924
|
+
});
|
|
1925
|
+
return res.enabled;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* PUT /admin/invoice-settings/attach-pdf — turn the PDF attachment on
|
|
1929
|
+
* invoice emails on or off. Returns the stored value.
|
|
1930
|
+
*/
|
|
1931
|
+
async setAttachPdf(enabled) {
|
|
1932
|
+
const body = { enabled };
|
|
1933
|
+
const res = await this.http.request({
|
|
1934
|
+
method: "PUT",
|
|
1935
|
+
path: `${this.basePath}/attach-pdf`,
|
|
1936
|
+
body
|
|
1937
|
+
});
|
|
1938
|
+
return res.enabled;
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
|
|
1942
|
+
// src/resources/invoice-reminders.ts
|
|
1943
|
+
var InvoiceRemindersResource = class {
|
|
1944
|
+
constructor(http2) {
|
|
1945
|
+
this.http = http2;
|
|
1946
|
+
this.basePath = "/admin/invoice-reminders";
|
|
1947
|
+
}
|
|
1948
|
+
/** GET /admin/invoice-reminders — the rules, ascending by offset, and the overdue offsets. */
|
|
1949
|
+
async list() {
|
|
1950
|
+
return this.http.request({
|
|
1951
|
+
method: "GET",
|
|
1952
|
+
path: this.basePath
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* PUT /admin/invoice-reminders — replace the WHOLE rule set (a rule left out
|
|
1957
|
+
* is deleted): up to 5 rules at unique offsets. Templates may use only
|
|
1958
|
+
* `{{customerName}}`, `{{invoiceNumber}}`, `{{amountDue}}`, `{{total}}`,
|
|
1959
|
+
* `{{dueDate}}`, `{{daysOverdue}}` and `{{companyName}}`; any other is a 422
|
|
1960
|
+
* `REMINDER_TEMPLATE_INVALID` listing each one. Returns the stored set.
|
|
1961
|
+
*/
|
|
1962
|
+
async update(rules) {
|
|
1963
|
+
return this.http.request({
|
|
1964
|
+
method: "PUT",
|
|
1965
|
+
path: this.basePath,
|
|
1966
|
+
body: { rules }
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* POST /admin/invoice-reminders/preview — render a subject and body exactly
|
|
1971
|
+
* as the reminder email would, against an invoice or a sample. Sends nothing.
|
|
1972
|
+
*/
|
|
1973
|
+
async preview(params) {
|
|
1974
|
+
return this.http.request({
|
|
1975
|
+
method: "POST",
|
|
1976
|
+
path: `${this.basePath}/preview`,
|
|
1977
|
+
body: params
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
|
|
1982
|
+
// src/resources/event-automations.ts
|
|
1983
|
+
var EventAutomationsResource = class {
|
|
1984
|
+
constructor(http2) {
|
|
1985
|
+
this.http = http2;
|
|
1986
|
+
}
|
|
1987
|
+
// Each request spells its path out in full (no computed base path) so the
|
|
1988
|
+
// API's SDK route-parity audit (sdk-route-parity-audit.spec.ts) can read it.
|
|
1989
|
+
/** GET — enabled and disabled automations, oldest first, optionally by event type or owner. */
|
|
1990
|
+
async list(params = {}) {
|
|
1991
|
+
const query = {};
|
|
1992
|
+
if (params.owner_tag) query["owner_tag"] = params.owner_tag;
|
|
1993
|
+
if (params.event_type) query["event_type"] = params.event_type;
|
|
1994
|
+
const res = await this.http.request({
|
|
1995
|
+
method: "GET",
|
|
1996
|
+
path: "/admin/event-automations",
|
|
1997
|
+
params: query
|
|
1998
|
+
});
|
|
1999
|
+
return res.data;
|
|
2000
|
+
}
|
|
2001
|
+
/** GET /:id — 404 when it doesn't exist or was deleted. */
|
|
2002
|
+
async get(id) {
|
|
2003
|
+
return this.http.request({
|
|
2004
|
+
method: "GET",
|
|
2005
|
+
path: `/admin/event-automations/${encodeURIComponent(id)}`
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* POST — create (201). 400 `VALIDATION_FAILED` for a bad body, or a 400
|
|
2010
|
+
* naming the step whose `action_type` isn't allowed in automations.
|
|
2011
|
+
*/
|
|
2012
|
+
async create(input) {
|
|
2013
|
+
return this.http.request({
|
|
2014
|
+
method: "POST",
|
|
2015
|
+
path: "/admin/event-automations",
|
|
2016
|
+
body: input
|
|
2017
|
+
});
|
|
1346
2018
|
}
|
|
1347
2019
|
/**
|
|
1348
|
-
*
|
|
1349
|
-
*
|
|
2020
|
+
* PATCH /:id — only the fields sent change. Sending `actions` replaces the
|
|
2021
|
+
* list: keep each existing step's `id` so a step that already ran isn't run
|
|
2022
|
+
* again. `{ enabled: false }` switches it off.
|
|
1350
2023
|
*/
|
|
1351
|
-
async
|
|
1352
|
-
|
|
1353
|
-
method: "
|
|
1354
|
-
path:
|
|
2024
|
+
async update(id, input) {
|
|
2025
|
+
return this.http.request({
|
|
2026
|
+
method: "PATCH",
|
|
2027
|
+
path: `/admin/event-automations/${encodeURIComponent(id)}`,
|
|
2028
|
+
body: input
|
|
1355
2029
|
});
|
|
1356
|
-
return res.enabled;
|
|
1357
2030
|
}
|
|
1358
|
-
/**
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const body = { enabled };
|
|
1364
|
-
const res = await this.http.request({
|
|
1365
|
-
method: "PUT",
|
|
1366
|
-
path: `${this.basePath}/attach-pdf`,
|
|
1367
|
-
body
|
|
2031
|
+
/** DELETE /:id — soft delete; returns `{ id }` (200). */
|
|
2032
|
+
async delete(id) {
|
|
2033
|
+
return this.http.request({
|
|
2034
|
+
method: "DELETE",
|
|
2035
|
+
path: `/admin/event-automations/${encodeURIComponent(id)}`
|
|
1368
2036
|
});
|
|
1369
|
-
return res.enabled;
|
|
1370
2037
|
}
|
|
1371
2038
|
};
|
|
1372
2039
|
|
|
1373
|
-
// src/resources/
|
|
1374
|
-
var
|
|
2040
|
+
// src/resources/web-forms.ts
|
|
2041
|
+
var WebFormsResource = class {
|
|
1375
2042
|
constructor(http2) {
|
|
1376
2043
|
this.http = http2;
|
|
1377
|
-
this.
|
|
2044
|
+
this.submissions = new WebFormSubmissionsResource(http2);
|
|
1378
2045
|
}
|
|
1379
|
-
|
|
2046
|
+
// Each request spells its path out in full so the API's SDK route-parity
|
|
2047
|
+
// audit (sdk-route-parity-audit.spec.ts) can read it.
|
|
2048
|
+
/** GET — every form not archived, newest first. */
|
|
1380
2049
|
async list() {
|
|
2050
|
+
const res = await this.http.request({ method: "GET", path: "/admin/web-forms" });
|
|
2051
|
+
return res.data;
|
|
2052
|
+
}
|
|
2053
|
+
/** GET /trade-options — the active trades a `trade` question offers, by current name. */
|
|
2054
|
+
async tradeOptions() {
|
|
2055
|
+
const res = await this.http.request({ method: "GET", path: "/admin/web-forms/trade-options" });
|
|
2056
|
+
return res.data;
|
|
2057
|
+
}
|
|
2058
|
+
/** GET /:id */
|
|
2059
|
+
async get(id) {
|
|
2060
|
+
return this.http.request({ method: "GET", path: `/admin/web-forms/${encodeURIComponent(id)}` });
|
|
2061
|
+
}
|
|
2062
|
+
/** POST — create (201) with its default routing. Pass `recipient_agent_ids`. */
|
|
2063
|
+
async create(input) {
|
|
2064
|
+
return this.http.request({ method: "POST", path: "/admin/web-forms", body: input });
|
|
2065
|
+
}
|
|
2066
|
+
/** PATCH /:id — only the fields sent change. `active: false` switches the form off. */
|
|
2067
|
+
async update(id, input) {
|
|
1381
2068
|
return this.http.request({
|
|
1382
|
-
method: "
|
|
1383
|
-
path:
|
|
2069
|
+
method: "PATCH",
|
|
2070
|
+
path: `/admin/web-forms/${encodeURIComponent(id)}`,
|
|
2071
|
+
body: input
|
|
1384
2072
|
});
|
|
1385
2073
|
}
|
|
1386
|
-
/**
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
*/
|
|
1393
|
-
async update(rules) {
|
|
2074
|
+
/** DELETE /:id — archive: the public URL stops working and routing stops. Returns `{ id }`. */
|
|
2075
|
+
async archive(id) {
|
|
2076
|
+
return this.http.request({ method: "DELETE", path: `/admin/web-forms/${encodeURIComponent(id)}` });
|
|
2077
|
+
}
|
|
2078
|
+
/** POST /:id/rotate-key — a new public key; the old embed stops working at once. */
|
|
2079
|
+
async rotateKey(id) {
|
|
1394
2080
|
return this.http.request({
|
|
1395
|
-
method: "
|
|
1396
|
-
path:
|
|
1397
|
-
body: { rules }
|
|
2081
|
+
method: "POST",
|
|
2082
|
+
path: `/admin/web-forms/${encodeURIComponent(id)}/rotate-key`
|
|
1398
2083
|
});
|
|
1399
2084
|
}
|
|
2085
|
+
};
|
|
2086
|
+
var WebFormSubmissionsResource = class {
|
|
2087
|
+
constructor(http2) {
|
|
2088
|
+
this.http = http2;
|
|
2089
|
+
}
|
|
2090
|
+
/** GET /submissions — the first page, newest first, with routing state. Use `listPage` to read on. */
|
|
2091
|
+
async list(params = {}) {
|
|
2092
|
+
return (await this.listPage(params)).data;
|
|
2093
|
+
}
|
|
1400
2094
|
/**
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
2095
|
+
* GET /submissions — one page plus `next`, which is null on the last page.
|
|
2096
|
+
* Pass `next`'s `before` and `before_id` back in to read the following page.
|
|
1403
2097
|
*/
|
|
1404
|
-
async
|
|
2098
|
+
async listPage(params = {}) {
|
|
2099
|
+
const query = {};
|
|
2100
|
+
for (const [k, v] of Object.entries(params)) if (v !== void 0) query[k] = v;
|
|
2101
|
+
const res = await this.http.request({
|
|
2102
|
+
method: "GET",
|
|
2103
|
+
path: "/admin/web-forms/submissions",
|
|
2104
|
+
params: query
|
|
2105
|
+
});
|
|
2106
|
+
return { data: res.data, next: res.next ?? null };
|
|
2107
|
+
}
|
|
2108
|
+
/** GET /submissions/:id — answers as seen, plus each routing step. */
|
|
2109
|
+
async get(id) {
|
|
2110
|
+
return this.http.request({
|
|
2111
|
+
method: "GET",
|
|
2112
|
+
path: `/admin/web-forms/submissions/${encodeURIComponent(id)}`
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
/** PATCH /submissions/:id — set triage status. */
|
|
2116
|
+
async update(id, input) {
|
|
2117
|
+
return this.http.request({
|
|
2118
|
+
method: "PATCH",
|
|
2119
|
+
path: `/admin/web-forms/submissions/${encodeURIComponent(id)}`,
|
|
2120
|
+
body: input
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
/** POST /submissions/:id/route — retry failed steps, run never-run ones (not email, text or webhook: `skipped`); safe to repeat. */
|
|
2124
|
+
async route(id) {
|
|
1405
2125
|
return this.http.request({
|
|
1406
2126
|
method: "POST",
|
|
1407
|
-
path:
|
|
1408
|
-
body: params
|
|
2127
|
+
path: `/admin/web-forms/submissions/${encodeURIComponent(id)}/route`
|
|
1409
2128
|
});
|
|
1410
2129
|
}
|
|
1411
2130
|
};
|
|
@@ -1577,23 +2296,119 @@ var WorkflowsResource = class {
|
|
|
1577
2296
|
var ServiceSchedulesResource = class {
|
|
1578
2297
|
constructor(http2) {
|
|
1579
2298
|
this.http = http2;
|
|
1580
|
-
|
|
2299
|
+
}
|
|
2300
|
+
// Each request spells its path out in full (no computed base path) so the
|
|
2301
|
+
// API's SDK route-parity audit (sdk-route-parity-audit.spec.ts) can read it.
|
|
2302
|
+
/** GET — the schedules of one customer or opportunity. */
|
|
2303
|
+
async list(subject_type, subject_id) {
|
|
2304
|
+
return this.http.request({
|
|
2305
|
+
method: "GET",
|
|
2306
|
+
path: "/service-schedules",
|
|
2307
|
+
params: { subject_type, subject_id }
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
/** GET /:id — 404 when it doesn't exist or was deleted. */
|
|
2311
|
+
async get(id) {
|
|
2312
|
+
return this.http.request({
|
|
2313
|
+
method: "GET",
|
|
2314
|
+
path: `/service-schedules/${encodeURIComponent(id)}`
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* POST — create (201). The horizon's visits are generated straight away.
|
|
2319
|
+
* 400 `INVALID_RECURRENCE_RULE`, `SERVICE_TRADE_REQUIRED` or a validation error.
|
|
2320
|
+
*/
|
|
2321
|
+
async create(input) {
|
|
2322
|
+
return this.http.request({
|
|
2323
|
+
method: "POST",
|
|
2324
|
+
path: "/service-schedules",
|
|
2325
|
+
body: input
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* PATCH /:id — only the fields sent change. A cadence, time, crew, duration
|
|
2330
|
+
* or horizon change regenerates future visits from today (`effective_from`,
|
|
2331
|
+
* which must be today) and reports it in `regeneration`; a visit someone changed is never
|
|
2332
|
+
* touched and is listed under `regeneration.protected`. The edit and its
|
|
2333
|
+
* regeneration succeed or fail together. 400 `INVALID_EFFECTIVE_FROM` for any
|
|
2334
|
+
* date but today.
|
|
2335
|
+
*/
|
|
2336
|
+
async update(id, input) {
|
|
2337
|
+
return this.http.request({
|
|
2338
|
+
method: "PATCH",
|
|
2339
|
+
path: `/service-schedules/${encodeURIComponent(id)}`,
|
|
2340
|
+
body: input
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
/** POST /:id/preview-update — what `update` with the same input would do. Writes nothing. */
|
|
2344
|
+
async previewUpdate(id, input) {
|
|
2345
|
+
return this.http.request({
|
|
2346
|
+
method: "POST",
|
|
2347
|
+
path: `/service-schedules/${encodeURIComponent(id)}/preview-update`,
|
|
2348
|
+
body: input
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
/** PATCH /:id/status — pause, resume (`active`) or end. Resume generates the horizon at once. */
|
|
2352
|
+
async setStatus(id, status) {
|
|
2353
|
+
return this.http.request({
|
|
2354
|
+
method: "PATCH",
|
|
2355
|
+
path: `/service-schedules/${encodeURIComponent(id)}/status`,
|
|
2356
|
+
body: { status }
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
/** DELETE /:id — soft-delete (204). */
|
|
2360
|
+
async delete(id) {
|
|
2361
|
+
await this.http.request({
|
|
2362
|
+
method: "DELETE",
|
|
2363
|
+
path: `/service-schedules/${encodeURIComponent(id)}`
|
|
2364
|
+
});
|
|
1581
2365
|
}
|
|
1582
2366
|
/**
|
|
1583
|
-
* POST /
|
|
1584
|
-
*
|
|
1585
|
-
*
|
|
1586
|
-
*
|
|
1587
|
-
*
|
|
1588
|
-
*
|
|
2367
|
+
* POST /conflicts/preview — before booking people, see who is already on
|
|
2368
|
+
* another event (`conflicts`) and who would be outside their working hours
|
|
2369
|
+
* (`working_hours`). Read-only and warn-only: nothing is ever blocked. Give
|
|
2370
|
+
* exactly one of `windows` (single booking) or `schedule` (a cadence). 400 on
|
|
2371
|
+
* invalid input, INVALID_RECURRENCE_RULE, INVALID_TIMEZONE or
|
|
2372
|
+
* TOO_MANY_WINDOWS (> 400).
|
|
1589
2373
|
*/
|
|
1590
2374
|
async previewConflicts(request) {
|
|
1591
2375
|
return this.http.request({
|
|
1592
2376
|
method: "POST",
|
|
1593
|
-
path:
|
|
2377
|
+
path: "/service-schedules/conflicts/preview",
|
|
1594
2378
|
body: request
|
|
1595
2379
|
});
|
|
1596
2380
|
}
|
|
2381
|
+
/** GET /settings/crew-roles — which agent roles count as assignable crew. */
|
|
2382
|
+
async getCrewRoles() {
|
|
2383
|
+
return this.http.request({
|
|
2384
|
+
method: "GET",
|
|
2385
|
+
path: "/service-schedules/settings/crew-roles"
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
/** PUT /settings/crew-roles — set the assignable roles (an override of the default). */
|
|
2389
|
+
async setCrewRoles(roles) {
|
|
2390
|
+
return this.http.request({
|
|
2391
|
+
method: "PUT",
|
|
2392
|
+
path: "/service-schedules/settings/crew-roles",
|
|
2393
|
+
body: { roles }
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
/** GET /settings/crew — assignable crew with their home-base status. */
|
|
2397
|
+
async listCrew() {
|
|
2398
|
+
const res = await this.http.request({
|
|
2399
|
+
method: "GET",
|
|
2400
|
+
path: "/service-schedules/settings/crew"
|
|
2401
|
+
});
|
|
2402
|
+
return res.crew;
|
|
2403
|
+
}
|
|
2404
|
+
/** PUT /settings/crew/:agentId/home-address — set and geocode a crew member's home base. */
|
|
2405
|
+
async setCrewHomeAddress(agentId, address) {
|
|
2406
|
+
return this.http.request({
|
|
2407
|
+
method: "PUT",
|
|
2408
|
+
path: `/service-schedules/settings/crew/${encodeURIComponent(agentId)}/home-address`,
|
|
2409
|
+
body: { address }
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
1597
2412
|
};
|
|
1598
2413
|
|
|
1599
2414
|
// src/resources/billing-schedules.ts
|
|
@@ -1798,6 +2613,52 @@ var CustomFieldsResource = class {
|
|
|
1798
2613
|
params
|
|
1799
2614
|
});
|
|
1800
2615
|
}
|
|
2616
|
+
/** GET /admin/custom-fields — the same list through the admin route (Manage grant). */
|
|
2617
|
+
async listAll(params) {
|
|
2618
|
+
return this.http.request({
|
|
2619
|
+
method: "GET",
|
|
2620
|
+
path: "/admin/custom-fields",
|
|
2621
|
+
params
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
/** GET /admin/custom-fields/:id */
|
|
2625
|
+
async get(id) {
|
|
2626
|
+
return this.http.request({
|
|
2627
|
+
method: "GET",
|
|
2628
|
+
path: `/admin/custom-fields/${id}`
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* POST /admin/custom-fields — define a new field. 400 for `job`, `vendor` or
|
|
2633
|
+
* `agent` (those records can't hold values yet, PRD D6). A `fieldKey` must be
|
|
2634
|
+
* unique per entity type: check `listAll({ entity_type, include_inactive: true })` first.
|
|
2635
|
+
*/
|
|
2636
|
+
async create(data) {
|
|
2637
|
+
return this.http.request({
|
|
2638
|
+
method: "POST",
|
|
2639
|
+
path: "/admin/custom-fields",
|
|
2640
|
+
body: data
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
/** PATCH /admin/custom-fields/:id — `fieldKey` and `entityType` can't change. */
|
|
2644
|
+
async update(id, data) {
|
|
2645
|
+
return this.http.request({
|
|
2646
|
+
method: "PATCH",
|
|
2647
|
+
path: `/admin/custom-fields/${id}`,
|
|
2648
|
+
body: data
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* DELETE /admin/custom-fields/:id — deactivates the definition (204). Records
|
|
2653
|
+
* keep their stored values; `list({ include_inactive: true })` still shows it,
|
|
2654
|
+
* and `update(id, { active: true })` brings it back.
|
|
2655
|
+
*/
|
|
2656
|
+
async deactivate(id) {
|
|
2657
|
+
await this.http.request({
|
|
2658
|
+
method: "DELETE",
|
|
2659
|
+
path: `/admin/custom-fields/${id}`
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
1801
2662
|
};
|
|
1802
2663
|
|
|
1803
2664
|
// src/resources/ortto.ts
|
|
@@ -1873,6 +2734,121 @@ var OrttoResource = class {
|
|
|
1873
2734
|
}
|
|
1874
2735
|
};
|
|
1875
2736
|
|
|
2737
|
+
// src/resources/webhook-subscriptions.ts
|
|
2738
|
+
var WebhookSubscriptionsResource = class extends ApiResource {
|
|
2739
|
+
constructor(http2) {
|
|
2740
|
+
super(http2, "/admin/webhooks");
|
|
2741
|
+
}
|
|
2742
|
+
/** POST /admin/webhooks — the returned `secret` is shown only this once. */
|
|
2743
|
+
async create(data) {
|
|
2744
|
+
return this.http.request({ method: "POST", path: this.basePath, body: data });
|
|
2745
|
+
}
|
|
2746
|
+
/** GET /admin/webhooks */
|
|
2747
|
+
async list(params) {
|
|
2748
|
+
return this.http.request({
|
|
2749
|
+
method: "GET",
|
|
2750
|
+
path: this.basePath,
|
|
2751
|
+
params
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2754
|
+
/** GET /admin/webhooks/:id */
|
|
2755
|
+
async get(id) {
|
|
2756
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}` });
|
|
2757
|
+
}
|
|
2758
|
+
/** PATCH /admin/webhooks/:id */
|
|
2759
|
+
async update(id, data) {
|
|
2760
|
+
return this.http.request({ method: "PATCH", path: `${this.basePath}/${id}`, body: data });
|
|
2761
|
+
}
|
|
2762
|
+
/** DELETE /admin/webhooks/:id */
|
|
2763
|
+
async delete(id) {
|
|
2764
|
+
return this.http.request({ method: "DELETE", path: `${this.basePath}/${id}` });
|
|
2765
|
+
}
|
|
2766
|
+
/** POST /admin/webhooks/:id/test — sends one signed test event to the URL now. */
|
|
2767
|
+
async test(id) {
|
|
2768
|
+
return this.http.request({ method: "POST", path: `${this.basePath}/${id}/test` });
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* POST /admin/webhooks/:id/rotate-secret — replaces the signing secret and
|
|
2772
|
+
* returns the new one, shown only this once. New deliveries are signed with
|
|
2773
|
+
* it, but deliveries already queued (and their retries, for several minutes)
|
|
2774
|
+
* keep the old one. `verifyWebhookSignature` checks one secret, so for a few
|
|
2775
|
+
* minutes have your receiver try the new secret and fall back to the old one.
|
|
2776
|
+
*/
|
|
2777
|
+
async rotateSecret(id) {
|
|
2778
|
+
return this.http.request({
|
|
2779
|
+
method: "POST",
|
|
2780
|
+
path: `${this.basePath}/${id}/rotate-secret`
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
/** GET /admin/webhooks/:id/deliveries — newest first. */
|
|
2784
|
+
async listDeliveries(id, params) {
|
|
2785
|
+
return this.http.request({
|
|
2786
|
+
method: "GET",
|
|
2787
|
+
path: `${this.basePath}/${id}/deliveries`,
|
|
2788
|
+
params
|
|
2789
|
+
});
|
|
2790
|
+
}
|
|
2791
|
+
/**
|
|
2792
|
+
* POST /admin/webhooks/:id/deliveries/:deliveryId/retry — queues a new
|
|
2793
|
+
* delivery of that event (202). The receiver sees the event again, so make
|
|
2794
|
+
* it idempotent on the event id.
|
|
2795
|
+
*/
|
|
2796
|
+
async retryDelivery(id, deliveryId) {
|
|
2797
|
+
return this.http.request({
|
|
2798
|
+
method: "POST",
|
|
2799
|
+
path: `${this.basePath}/${id}/deliveries/${deliveryId}/retry`
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
|
|
2804
|
+
// src/resources/users.ts
|
|
2805
|
+
var UsersResource = class extends ApiResource {
|
|
2806
|
+
constructor(http2) {
|
|
2807
|
+
super(http2, "/users");
|
|
2808
|
+
}
|
|
2809
|
+
/** GET /users — paged, with email, role, status and org fields (read grant on users). */
|
|
2810
|
+
async list(params) {
|
|
2811
|
+
return this.http.request({
|
|
2812
|
+
method: "GET",
|
|
2813
|
+
path: this.basePath,
|
|
2814
|
+
params
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* GET /users/directory — active users' ids, names and roles, for pickers and
|
|
2819
|
+
* for resolving an assignee. Every role can read it.
|
|
2820
|
+
*/
|
|
2821
|
+
async directory(params) {
|
|
2822
|
+
return this.http.request({
|
|
2823
|
+
method: "GET",
|
|
2824
|
+
path: `${this.basePath}/directory`,
|
|
2825
|
+
params
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
/**
|
|
2829
|
+
* GET /users/:id/working-hours — the user's own weekly hours and the tenant
|
|
2830
|
+
* default. A non-admin caller may read only their own (403 otherwise).
|
|
2831
|
+
*/
|
|
2832
|
+
async getWorkingHours(id) {
|
|
2833
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}/working-hours` });
|
|
2834
|
+
}
|
|
2835
|
+
};
|
|
2836
|
+
|
|
2837
|
+
// src/resources/teams.ts
|
|
2838
|
+
var TeamsResource = class extends ApiResource {
|
|
2839
|
+
constructor(http2) {
|
|
2840
|
+
super(http2, "/teams");
|
|
2841
|
+
}
|
|
2842
|
+
/** GET /teams — every team, with `member_count`. */
|
|
2843
|
+
async list() {
|
|
2844
|
+
return this.http.request({ method: "GET", path: this.basePath });
|
|
2845
|
+
}
|
|
2846
|
+
/** GET /teams/:id — one team, with `members`. */
|
|
2847
|
+
async get(id) {
|
|
2848
|
+
return this.http.request({ method: "GET", path: `${this.basePath}/${id}` });
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
|
|
1876
2852
|
// src/webhooks.ts
|
|
1877
2853
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
1878
2854
|
var WEBHOOK_EVENT_TYPES = [
|
|
@@ -1908,7 +2884,8 @@ var WEBHOOK_EVENT_TYPES = [
|
|
|
1908
2884
|
"integration_connection.deactivated",
|
|
1909
2885
|
"customer_payment_method.invalidated",
|
|
1910
2886
|
"invoice.autopay_succeeded",
|
|
1911
|
-
"invoice.autopay_failed"
|
|
2887
|
+
"invoice.autopay_failed",
|
|
2888
|
+
"received"
|
|
1912
2889
|
];
|
|
1913
2890
|
function verifyWebhookSignature(payload, signature, secret) {
|
|
1914
2891
|
if (!signature || !secret || !payload) {
|
|
@@ -2110,6 +3087,18 @@ var PlanesClient = class {
|
|
|
2110
3087
|
}
|
|
2111
3088
|
return this._tags;
|
|
2112
3089
|
}
|
|
3090
|
+
get lists() {
|
|
3091
|
+
if (!this._lists) {
|
|
3092
|
+
this._lists = new ListsResource(this.http);
|
|
3093
|
+
}
|
|
3094
|
+
return this._lists;
|
|
3095
|
+
}
|
|
3096
|
+
get leadSources() {
|
|
3097
|
+
if (!this._leadSources) {
|
|
3098
|
+
this._leadSources = new LeadSourcesResource(this.http);
|
|
3099
|
+
}
|
|
3100
|
+
return this._leadSources;
|
|
3101
|
+
}
|
|
2113
3102
|
get tasks() {
|
|
2114
3103
|
if (!this._tasks) {
|
|
2115
3104
|
this._tasks = new TaskResource(this.http);
|
|
@@ -2165,6 +3154,20 @@ var PlanesClient = class {
|
|
|
2165
3154
|
}
|
|
2166
3155
|
return this._invoiceReminders;
|
|
2167
3156
|
}
|
|
3157
|
+
/** Event automations, admin only — `eventAutomations.list/get/create/update/delete` (E60-S2). */
|
|
3158
|
+
get eventAutomations() {
|
|
3159
|
+
if (!this._eventAutomations) {
|
|
3160
|
+
this._eventAutomations = new EventAutomationsResource(this.http);
|
|
3161
|
+
}
|
|
3162
|
+
return this._eventAutomations;
|
|
3163
|
+
}
|
|
3164
|
+
/** Web forms and their submissions, admin only — `webForms.list/get/create/update/archive/rotateKey`, `webForms.submissions.list/get/update/route` (E60-S6). */
|
|
3165
|
+
get webForms() {
|
|
3166
|
+
if (!this._webForms) {
|
|
3167
|
+
this._webForms = new WebFormsResource(this.http);
|
|
3168
|
+
}
|
|
3169
|
+
return this._webForms;
|
|
3170
|
+
}
|
|
2168
3171
|
/** Published sequences a contact can be enrolled in (E51-F2-S6). */
|
|
2169
3172
|
get contactSequences() {
|
|
2170
3173
|
if (!this._contactSequences) {
|
|
@@ -2215,6 +3218,30 @@ var PlanesClient = class {
|
|
|
2215
3218
|
}
|
|
2216
3219
|
return this._ortto;
|
|
2217
3220
|
}
|
|
3221
|
+
/**
|
|
3222
|
+
* Webhook subscriptions — where entity events are delivered (E64-S10). Admin
|
|
3223
|
+
* grants and the `open_api` feature. Not `webhooks`: that is the event router.
|
|
3224
|
+
*/
|
|
3225
|
+
get webhookSubscriptions() {
|
|
3226
|
+
if (!this._webhookSubscriptions) {
|
|
3227
|
+
this._webhookSubscriptions = new WebhookSubscriptionsResource(this.http);
|
|
3228
|
+
}
|
|
3229
|
+
return this._webhookSubscriptions;
|
|
3230
|
+
}
|
|
3231
|
+
/** The tenant's users, read-only: list and directory (E64-S10). */
|
|
3232
|
+
get users() {
|
|
3233
|
+
if (!this._users) {
|
|
3234
|
+
this._users = new UsersResource(this.http);
|
|
3235
|
+
}
|
|
3236
|
+
return this._users;
|
|
3237
|
+
}
|
|
3238
|
+
/** Teams, read-only (E64-S10). */
|
|
3239
|
+
get teams() {
|
|
3240
|
+
if (!this._teams) {
|
|
3241
|
+
this._teams = new TeamsResource(this.http);
|
|
3242
|
+
}
|
|
3243
|
+
return this._teams;
|
|
3244
|
+
}
|
|
2218
3245
|
/**
|
|
2219
3246
|
* Ergonomic webhook event router — `client.webhooks.on(pattern, handler)`
|
|
2220
3247
|
* plus `client.webhooks.process({ body, signature, secret })`. Auth-independent
|
|
@@ -2254,14 +3281,14 @@ var BILLING_ADJUSTMENT_STATUSES = ["pending", "consumed", "cancelled"];
|
|
|
2254
3281
|
var BILLING_ADJUSTMENT_TARGETS = ["next_invoice"];
|
|
2255
3282
|
|
|
2256
3283
|
// src/types/contact-types.ts
|
|
2257
|
-
var CONTACT_SORT_KEYS = ["name", "created_at", "recent"];
|
|
3284
|
+
var CONTACT_SORT_KEYS = ["name", "created_at", "recent", "updated_at"];
|
|
2258
3285
|
var CONTACT_LIST_INCLUDES = ["channels", "links"];
|
|
2259
3286
|
|
|
2260
3287
|
// src/types/customer-types.ts
|
|
2261
|
-
var CUSTOMER_SORT_KEYS = ["created_at", "last_name", "first_name", "email", "phone", "source", "recent"];
|
|
3288
|
+
var CUSTOMER_SORT_KEYS = ["created_at", "last_name", "first_name", "email", "phone", "source", "recent", "updated_at"];
|
|
2262
3289
|
|
|
2263
3290
|
// src/types/opportunity-types.ts
|
|
2264
|
-
var OPPORTUNITY_SORT_KEYS = ["created_at", "total", "title", "customer_name", "stage", "priority", "recent"];
|
|
3291
|
+
var OPPORTUNITY_SORT_KEYS = ["created_at", "total", "title", "customer_name", "stage", "priority", "recent", "updated_at"];
|
|
2265
3292
|
var OPPORTUNITY_STATES = ["open", "closed", "won", "lost"];
|
|
2266
3293
|
|
|
2267
3294
|
// src/types/job-types.ts
|
|
@@ -2289,7 +3316,8 @@ var MODULE_KEYS = [
|
|
|
2289
3316
|
"contacts",
|
|
2290
3317
|
"tasks",
|
|
2291
3318
|
"comments",
|
|
2292
|
-
"conversations"
|
|
3319
|
+
"conversations",
|
|
3320
|
+
"web_forms"
|
|
2293
3321
|
];
|
|
2294
3322
|
|
|
2295
3323
|
// src/types/plan-types.ts
|
|
@@ -2351,6 +3379,12 @@ var WORKFLOW_EVENT_RULE_EVENT_TYPES = [
|
|
|
2351
3379
|
var WORKFLOW_EVENT_RULE_MATCH_ALL_EVENT_TYPES = [
|
|
2352
3380
|
"calendar_event.created"
|
|
2353
3381
|
];
|
|
3382
|
+
var WORKFLOW_EVENT_RULE_CATCH_UP_EVENT_TYPES = [
|
|
3383
|
+
"calendar_event.created"
|
|
3384
|
+
];
|
|
3385
|
+
|
|
3386
|
+
// src/types/task-types.ts
|
|
3387
|
+
var TASK_SORT_KEYS = ["due_at", "created_at", "updated_at"];
|
|
2354
3388
|
|
|
2355
3389
|
// src/agent.ts
|
|
2356
3390
|
import * as http from "http";
|
|
@@ -2550,6 +3584,7 @@ export {
|
|
|
2550
3584
|
ESTIMATE_STATUSES,
|
|
2551
3585
|
EnrollmentsResource,
|
|
2552
3586
|
EstimateResource,
|
|
3587
|
+
EventAutomationsResource,
|
|
2553
3588
|
FEATURE_KEYS,
|
|
2554
3589
|
GmailResource,
|
|
2555
3590
|
InventoryResource,
|
|
@@ -2558,6 +3593,8 @@ export {
|
|
|
2558
3593
|
InvoiceSettingsResource,
|
|
2559
3594
|
JOB_SORT_KEYS,
|
|
2560
3595
|
JobResource,
|
|
3596
|
+
LeadSourcesResource,
|
|
3597
|
+
ListsResource,
|
|
2561
3598
|
MODULE_KEYS,
|
|
2562
3599
|
MeResource,
|
|
2563
3600
|
MemoryTokenStorage,
|
|
@@ -2581,13 +3618,20 @@ export {
|
|
|
2581
3618
|
RolesResource,
|
|
2582
3619
|
ServerError,
|
|
2583
3620
|
ServiceSchedulesResource,
|
|
3621
|
+
TASK_SORT_KEYS,
|
|
2584
3622
|
TagResource,
|
|
2585
3623
|
TaskResource,
|
|
3624
|
+
TeamsResource,
|
|
3625
|
+
UsersResource,
|
|
2586
3626
|
ValidationError,
|
|
2587
3627
|
WEBHOOK_EVENT_TYPES,
|
|
3628
|
+
WORKFLOW_EVENT_RULE_CATCH_UP_EVENT_TYPES,
|
|
2588
3629
|
WORKFLOW_EVENT_RULE_EVENT_TYPES,
|
|
2589
3630
|
WORKFLOW_EVENT_RULE_MATCH_ALL_EVENT_TYPES,
|
|
3631
|
+
WebFormSubmissionsResource,
|
|
3632
|
+
WebFormsResource,
|
|
2590
3633
|
WebhookRouter,
|
|
3634
|
+
WebhookSubscriptionsResource,
|
|
2591
3635
|
WebhookVerificationError,
|
|
2592
3636
|
WorkflowEventRulesResource,
|
|
2593
3637
|
WorkflowsResource,
|