@exayard/cli 0.2.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -8
- package/dist/bin.js +3824 -221
- package/dist/index.d.ts +20 -5
- package/dist/index.js +3659 -211
- package/package.json +10 -7
package/dist/index.js
CHANGED
|
@@ -30,12 +30,2505 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
+
// ../sdk/dist/_generated/operations.js
|
|
34
|
+
var require_operations = __commonJS({
|
|
35
|
+
"../sdk/dist/_generated/operations.js"(exports2) {
|
|
36
|
+
"use strict";
|
|
37
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
38
|
+
exports2.OPERATION_METHODS = exports2.OPERATIONS = void 0;
|
|
39
|
+
exports2.OPERATIONS = {
|
|
40
|
+
add_symbol_line_item: {
|
|
41
|
+
method: "POST",
|
|
42
|
+
path: "/symbols/{id}/line-items",
|
|
43
|
+
tag: "Takeoff",
|
|
44
|
+
summary: "Add a line item to a symbol",
|
|
45
|
+
body: true,
|
|
46
|
+
response: "json",
|
|
47
|
+
params: [
|
|
48
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
49
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
50
|
+
{ name: "lineItem", in: "body", required: true, kind: "object" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
archive_conversation: {
|
|
54
|
+
method: "DELETE",
|
|
55
|
+
path: "/conversations/{id}",
|
|
56
|
+
tag: "Conversations",
|
|
57
|
+
summary: "Archive a conversation",
|
|
58
|
+
body: false,
|
|
59
|
+
response: "empty",
|
|
60
|
+
params: [
|
|
61
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Conversation id: the id the API returns for a conversation (a string of 32 lowercase letters and digits)." },
|
|
62
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
archive_labor_crew: {
|
|
66
|
+
method: "DELETE",
|
|
67
|
+
path: "/labor/crews/{id}",
|
|
68
|
+
tag: "Labor",
|
|
69
|
+
summary: "Archive a labor crew",
|
|
70
|
+
body: false,
|
|
71
|
+
response: "empty",
|
|
72
|
+
params: [
|
|
73
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
74
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
archive_labor_equipment: {
|
|
78
|
+
method: "DELETE",
|
|
79
|
+
path: "/labor/equipment/{id}",
|
|
80
|
+
tag: "Labor",
|
|
81
|
+
summary: "Archive a labor equipment entry",
|
|
82
|
+
body: false,
|
|
83
|
+
response: "empty",
|
|
84
|
+
params: [
|
|
85
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
86
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
archive_labor_rate: {
|
|
90
|
+
method: "DELETE",
|
|
91
|
+
path: "/labor/rates/{id}",
|
|
92
|
+
tag: "Labor",
|
|
93
|
+
summary: "Archive a labor rate",
|
|
94
|
+
body: false,
|
|
95
|
+
response: "empty",
|
|
96
|
+
params: [
|
|
97
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
98
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
archive_list: {
|
|
102
|
+
method: "DELETE",
|
|
103
|
+
path: "/lists/{id}",
|
|
104
|
+
tag: "Lists",
|
|
105
|
+
summary: "Archive (soft-delete) a list",
|
|
106
|
+
body: false,
|
|
107
|
+
response: "empty",
|
|
108
|
+
params: [
|
|
109
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
110
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
archive_page: {
|
|
114
|
+
method: "DELETE",
|
|
115
|
+
path: "/pages/{id}",
|
|
116
|
+
tag: "Pages",
|
|
117
|
+
summary: "Archive (soft-delete) a page",
|
|
118
|
+
body: false,
|
|
119
|
+
response: "empty",
|
|
120
|
+
params: [
|
|
121
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." }
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
archive_product: {
|
|
125
|
+
method: "DELETE",
|
|
126
|
+
path: "/products/{id}",
|
|
127
|
+
tag: "Products",
|
|
128
|
+
summary: "Archive (soft-delete) a product",
|
|
129
|
+
body: false,
|
|
130
|
+
response: "empty",
|
|
131
|
+
params: [
|
|
132
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
133
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
archive_project: {
|
|
137
|
+
method: "DELETE",
|
|
138
|
+
path: "/projects/{id}",
|
|
139
|
+
tag: "Projects",
|
|
140
|
+
summary: "Archive a project",
|
|
141
|
+
body: false,
|
|
142
|
+
response: "empty",
|
|
143
|
+
params: [
|
|
144
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
145
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
archive_vendor: {
|
|
149
|
+
method: "DELETE",
|
|
150
|
+
path: "/vendors/{id}",
|
|
151
|
+
tag: "Vendors",
|
|
152
|
+
summary: "Archive (soft-delete) a vendor",
|
|
153
|
+
body: false,
|
|
154
|
+
response: "empty",
|
|
155
|
+
params: [
|
|
156
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Vendor id: the id the API returns for a vendor (a string of 32 lowercase letters and digits)." },
|
|
157
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
attach_file_to_project: {
|
|
161
|
+
method: "POST",
|
|
162
|
+
path: "/projects/{id}/files/{fileId}/attach",
|
|
163
|
+
tag: "Files",
|
|
164
|
+
summary: "Attach an existing file to a project",
|
|
165
|
+
body: true,
|
|
166
|
+
response: "empty",
|
|
167
|
+
params: [
|
|
168
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
169
|
+
{ name: "fileId", in: "path", required: true, kind: "string", description: "File id: the id the API returns for a file (a string of 32 lowercase letters and digits)." },
|
|
170
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
batch_create_products: {
|
|
174
|
+
method: "POST",
|
|
175
|
+
path: "/products/batch",
|
|
176
|
+
tag: "Products",
|
|
177
|
+
summary: "Batch-create products",
|
|
178
|
+
body: true,
|
|
179
|
+
response: "json",
|
|
180
|
+
params: [
|
|
181
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
182
|
+
{ name: "listId", in: "body", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
183
|
+
{ name: "products", in: "body", required: true, kind: "array" }
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
cancel_assessment: {
|
|
187
|
+
method: "POST",
|
|
188
|
+
path: "/assessments/{id}/cancel",
|
|
189
|
+
tag: "Takeoff",
|
|
190
|
+
summary: "Cancel a running assessment",
|
|
191
|
+
body: true,
|
|
192
|
+
response: "empty",
|
|
193
|
+
params: [
|
|
194
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff assessment id: the id the API returns for a takeoff assessment (a string of 32 lowercase letters and digits)." },
|
|
195
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
confirm_file_upload: {
|
|
199
|
+
method: "POST",
|
|
200
|
+
path: "/files/{id}/confirm",
|
|
201
|
+
tag: "Files",
|
|
202
|
+
summary: "Confirm a file upload after the client PUT succeeds",
|
|
203
|
+
confirm: "This operation uses AI usage.",
|
|
204
|
+
body: true,
|
|
205
|
+
response: "json",
|
|
206
|
+
params: [
|
|
207
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "File id: the id the API returns for a file (a string of 32 lowercase letters and digits)." },
|
|
208
|
+
{ name: "r2Key", in: "body", required: true, kind: "string" }
|
|
209
|
+
]
|
|
210
|
+
},
|
|
211
|
+
create_agent_session: {
|
|
212
|
+
method: "POST",
|
|
213
|
+
path: "/ai-sessions",
|
|
214
|
+
tag: "Account",
|
|
215
|
+
summary: "Start a computer-use agent session on a project page",
|
|
216
|
+
confirm: "This operation uses AI usage.",
|
|
217
|
+
body: true,
|
|
218
|
+
response: "json",
|
|
219
|
+
params: [
|
|
220
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
221
|
+
{ name: "pageId", in: "body", required: true, kind: "string", description: "Id of the page the agent will operate on." },
|
|
222
|
+
{ name: "vendor", in: "body", required: true, kind: "string", enum: ["claude", "openai", "gemini"] }
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
create_assessment: {
|
|
226
|
+
method: "POST",
|
|
227
|
+
path: "/projects/{id}/assessments",
|
|
228
|
+
tag: "Takeoff",
|
|
229
|
+
summary: "Start a new AI assessment",
|
|
230
|
+
confirm: "This operation uses AI usage.",
|
|
231
|
+
body: true,
|
|
232
|
+
response: "json",
|
|
233
|
+
params: [
|
|
234
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
235
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
236
|
+
{ name: "conversationId", in: "body", required: false, kind: "string" },
|
|
237
|
+
{ name: "fileIds", in: "body", required: false, kind: "array" },
|
|
238
|
+
{ name: "pageIds", in: "body", required: false, kind: "array" },
|
|
239
|
+
{ name: "imageUrls", in: "body", required: false, kind: "array" },
|
|
240
|
+
{ name: "types", in: "body", required: false, kind: "array" }
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
create_bid: {
|
|
244
|
+
method: "POST",
|
|
245
|
+
path: "/projects/{id}/bids",
|
|
246
|
+
tag: "Estimates",
|
|
247
|
+
summary: "Generate a project-scoped bid document (blocking)",
|
|
248
|
+
confirm: "This operation uses AI usage.",
|
|
249
|
+
body: true,
|
|
250
|
+
response: "json",
|
|
251
|
+
params: [
|
|
252
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
253
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
254
|
+
{ name: "notes", in: "body", required: false, kind: "string", description: "Additional context appended to the prompt" },
|
|
255
|
+
{ name: "measurementContext", in: "body", required: false, kind: "string", description: "Pre-serialized takeoff measurements + line items (markdown). If omitted, the endpoint loads the project takeoff itself and serializes it on the server." },
|
|
256
|
+
{ name: "ignoreMissingVariables", in: "body", required: false, kind: "boolean", description: "When auto-loading takeoff data, skip line items with missing formula variables instead of failing with 422. Ignored if measurementContext is provided." },
|
|
257
|
+
{ name: "share", in: "body", required: false, kind: "boolean", description: "Publish the saved bid on a public link that anyone holding the URL can read. Defaults to false \u2014 the document stays private to your organization and documentUrl points at the signed-in path. Set true only when the customer asked for a shareable link; you can also share or unshare later via PATCH /v1/documents/{id}/share." }
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
create_conversation: {
|
|
261
|
+
method: "POST",
|
|
262
|
+
path: "/conversations",
|
|
263
|
+
tag: "Conversations",
|
|
264
|
+
summary: "Create a conversation",
|
|
265
|
+
body: true,
|
|
266
|
+
response: "json",
|
|
267
|
+
params: [
|
|
268
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
269
|
+
{ name: "projectId", in: "body", required: false, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
270
|
+
{ name: "title", in: "body", required: false, kind: "string" },
|
|
271
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["ai_chat", "project_activity"] },
|
|
272
|
+
{ name: "model", in: "body", required: false, kind: "string" },
|
|
273
|
+
{ name: "visibility", in: "body", required: false, kind: "string", enum: ["private", "shared"] }
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
create_custom_property: {
|
|
277
|
+
method: "POST",
|
|
278
|
+
path: "/custom-properties",
|
|
279
|
+
tag: "Custom properties",
|
|
280
|
+
summary: "Create a custom property",
|
|
281
|
+
body: true,
|
|
282
|
+
response: "json",
|
|
283
|
+
params: [
|
|
284
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
285
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
286
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["text", "textarea", "number", "date", "checkbox", "multiCheckbox", "select", "url", "email", "phone", "currency", "percentage"] },
|
|
287
|
+
{ name: "recordTypes", in: "body", required: true, kind: "array" },
|
|
288
|
+
{ name: "options", in: "body", required: false, kind: "array" },
|
|
289
|
+
{ name: "presetId", in: "body", required: false, kind: "string" }
|
|
290
|
+
]
|
|
291
|
+
},
|
|
292
|
+
create_estimate: {
|
|
293
|
+
method: "POST",
|
|
294
|
+
path: "/projects/{id}/estimates",
|
|
295
|
+
tag: "Estimates",
|
|
296
|
+
summary: "Generate an estimate and persist it as a document (blocking)",
|
|
297
|
+
confirm: "This operation uses AI usage.",
|
|
298
|
+
body: true,
|
|
299
|
+
response: "json",
|
|
300
|
+
params: [
|
|
301
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
302
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
303
|
+
{ name: "query", in: "body", required: true, kind: "string", description: "Natural-language prompt describing the scope of the estimate" },
|
|
304
|
+
{ name: "notes", in: "body", required: false, kind: "string", description: "Additional notes appended to the prompt" },
|
|
305
|
+
{ name: "measurementContext", in: "body", required: false, kind: "string", description: "Pre-serialized takeoff data to ground the estimator (see GET /v1/projects/{id}/takeoff-summary)" },
|
|
306
|
+
{ name: "share", in: "body", required: false, kind: "boolean", description: "Publish the saved estimate on a public link that anyone holding the URL can read. Defaults to false \u2014 the document stays private to your organization and documentUrl points at the signed-in path. Set true only when the customer asked for a shareable link; you can also share or unshare later via PATCH /v1/documents/{id}/share." }
|
|
307
|
+
]
|
|
308
|
+
},
|
|
309
|
+
create_folder: {
|
|
310
|
+
method: "POST",
|
|
311
|
+
path: "/folders",
|
|
312
|
+
tag: "Folders",
|
|
313
|
+
summary: "Create a folder",
|
|
314
|
+
body: true,
|
|
315
|
+
response: "json",
|
|
316
|
+
params: [
|
|
317
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
318
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
319
|
+
{ name: "parentId", in: "body", required: true, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." }
|
|
320
|
+
]
|
|
321
|
+
},
|
|
322
|
+
create_group: {
|
|
323
|
+
method: "POST",
|
|
324
|
+
path: "/projects/{id}/groups",
|
|
325
|
+
tag: "Takeoff",
|
|
326
|
+
summary: "Create a takeoff group",
|
|
327
|
+
body: true,
|
|
328
|
+
response: "json",
|
|
329
|
+
params: [
|
|
330
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
331
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
332
|
+
{ name: "parentId", in: "body", required: false, kind: "string", description: "Takeoff group id: the id the API returns for a takeoff group (a string of 32 lowercase letters and digits)." }
|
|
333
|
+
]
|
|
334
|
+
},
|
|
335
|
+
create_labor_crew: {
|
|
336
|
+
method: "POST",
|
|
337
|
+
path: "/labor/crews",
|
|
338
|
+
tag: "Labor",
|
|
339
|
+
summary: "Create a labor crew",
|
|
340
|
+
body: true,
|
|
341
|
+
response: "json",
|
|
342
|
+
params: [
|
|
343
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
344
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
345
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
346
|
+
{ name: "members", in: "body", required: true, kind: "array" },
|
|
347
|
+
{ name: "equipmentIds", in: "body", required: false, kind: "array" },
|
|
348
|
+
{ name: "dailyCapacity", in: "body", required: false, kind: "object" }
|
|
349
|
+
]
|
|
350
|
+
},
|
|
351
|
+
create_labor_equipment: {
|
|
352
|
+
method: "POST",
|
|
353
|
+
path: "/labor/equipment",
|
|
354
|
+
tag: "Labor",
|
|
355
|
+
summary: "Create a labor equipment entry",
|
|
356
|
+
body: true,
|
|
357
|
+
response: "json",
|
|
358
|
+
params: [
|
|
359
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
360
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
361
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
362
|
+
{ name: "category", in: "body", required: true, kind: "string" },
|
|
363
|
+
{ name: "dailyRate", in: "body", required: true, kind: "number" },
|
|
364
|
+
{ name: "hourlyRate", in: "body", required: false, kind: "number" },
|
|
365
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
366
|
+
{ name: "isOwned", in: "body", required: false, kind: "boolean" }
|
|
367
|
+
]
|
|
368
|
+
},
|
|
369
|
+
create_labor_rate: {
|
|
370
|
+
method: "POST",
|
|
371
|
+
path: "/labor/rates",
|
|
372
|
+
tag: "Labor",
|
|
373
|
+
summary: "Create a labor rate",
|
|
374
|
+
body: true,
|
|
375
|
+
response: "json",
|
|
376
|
+
params: [
|
|
377
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
378
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
379
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
380
|
+
{ name: "hourlyRate", in: "body", required: true, kind: "number" },
|
|
381
|
+
{ name: "burdenRate", in: "body", required: false, kind: "number" },
|
|
382
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
383
|
+
{ name: "effectiveDate", in: "body", required: false, kind: "number" },
|
|
384
|
+
{ name: "isDefault", in: "body", required: false, kind: "boolean" }
|
|
385
|
+
]
|
|
386
|
+
},
|
|
387
|
+
create_layer: {
|
|
388
|
+
method: "POST",
|
|
389
|
+
path: "/projects/{id}/layers",
|
|
390
|
+
tag: "Takeoff",
|
|
391
|
+
summary: "Create a takeoff layer",
|
|
392
|
+
body: true,
|
|
393
|
+
response: "json",
|
|
394
|
+
params: [
|
|
395
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
396
|
+
{ name: "name", in: "body", required: true, kind: "string" }
|
|
397
|
+
]
|
|
398
|
+
},
|
|
399
|
+
create_list: {
|
|
400
|
+
method: "POST",
|
|
401
|
+
path: "/lists",
|
|
402
|
+
tag: "Lists",
|
|
403
|
+
summary: "Create a list",
|
|
404
|
+
body: true,
|
|
405
|
+
response: "json",
|
|
406
|
+
params: [
|
|
407
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
408
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
409
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
410
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["product", "takeoffTemplate"] },
|
|
411
|
+
{ name: "privacy", in: "body", required: false, kind: "string", enum: ["private", "organization", "public", "system"] },
|
|
412
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
413
|
+
{ name: "locale", in: "body", required: true, kind: "string", enum: ["af-ZA", "id-ID", "ms-MY", "ca-ES", "cs-CZ", "da-DK", "de-DE", "en-US", "es-ES", "es-419", "fr-CA", "fr-FR", "hr-HR", "it-IT", "hu-HU", "nl-NL", "no-NO", "pl-PL", "pt-BR", "pt-PT", "ro-RO", "sk-SK", "fi-FI", "sv-SE", "vi-VN", "tr-TR", "el-GR", "ru-RU", "sr-SP", "uk-UA", "hy-AM", "he-IL", "ur-PK", "ar-SA", "hi-IN", "th-TH", "ko-KR", "ja-JP", "zh-CN", "zh-TW", null], description: "Supported BCP-47 locale code" }
|
|
414
|
+
]
|
|
415
|
+
},
|
|
416
|
+
create_measurement: {
|
|
417
|
+
method: "POST",
|
|
418
|
+
path: "/pages/{id}/measurements",
|
|
419
|
+
tag: "Takeoff",
|
|
420
|
+
summary: "Create a measurement on a page",
|
|
421
|
+
body: true,
|
|
422
|
+
response: "json",
|
|
423
|
+
params: [
|
|
424
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." },
|
|
425
|
+
{ name: "symbolId", in: "body", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
426
|
+
{ name: "shapeId", in: "body", required: true, kind: "string", description: "Canvas shape ID" },
|
|
427
|
+
{ name: "value", in: "body", required: true, kind: "number", description: "The measured quantity in RAW PAGE PIXELS \u2014 the same coordinate space as the geometry points, NEVER feet, metres, or any other real-world unit. Linear measurements are a length in px, areas are px\xB2, counts are a tally. Exayard converts to real units at display time using the page scale, so a real-world number sent here is converted a second time and reads wrong. When geometry is supplied the server measures it and rejects a value that disagrees by 10\xD7 or more, naming both numbers. To send a real-world quantity, convert it to page pixels first (multiply by the page's pixels-per-unit; square that factor for an area)." },
|
|
428
|
+
{ name: "geometry", in: "body", required: false, kind: "object", description: 'Measurement geometry: { "points": [{ "x": number, "y": number, "bulge"?: number }, ...], "isClosed": boolean, "holes"?: Point[][], "pathD"?: string }. When geometry is provided, "points" and "isClosed" are required; other keys (e.g. { "length": 280.9 }) are rejected.' },
|
|
429
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
430
|
+
{ name: "layerId", in: "body", required: false, kind: "string", description: "Takeoff layer id: the id the API returns for a takeoff layer (a string of 32 lowercase letters and digits)." }
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
create_message: {
|
|
434
|
+
method: "POST",
|
|
435
|
+
path: "/projects/{id}/messages",
|
|
436
|
+
tag: "Messages",
|
|
437
|
+
summary: "Post a message to a project",
|
|
438
|
+
body: true,
|
|
439
|
+
response: "json",
|
|
440
|
+
params: [
|
|
441
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
442
|
+
{ name: "content", in: "body", required: true, kind: "string" },
|
|
443
|
+
{ name: "parentMessageId", in: "body", required: false, kind: "string", description: "Chat message id: the id the API returns for a chat message (a string of 32 lowercase letters and digits)." },
|
|
444
|
+
{ name: "pageId", in: "body", required: false, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." },
|
|
445
|
+
{ name: "location", in: "body", required: false, kind: "object" },
|
|
446
|
+
{ name: "mentions", in: "body", required: false, kind: "array" }
|
|
447
|
+
]
|
|
448
|
+
},
|
|
449
|
+
create_organization: {
|
|
450
|
+
method: "POST",
|
|
451
|
+
path: "/organizations",
|
|
452
|
+
tag: "Account",
|
|
453
|
+
summary: "Create a company your app's company owns",
|
|
454
|
+
body: true,
|
|
455
|
+
response: "json",
|
|
456
|
+
params: [
|
|
457
|
+
{ name: "name", in: "body", required: true, kind: "string", description: "The company name, 1 to 200 characters." },
|
|
458
|
+
{ name: "country", in: "body", required: false, kind: "string", description: "ISO 3166-1 alpha-2 country code. It sets the company's starting tax, units and currency." },
|
|
459
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
460
|
+
{ name: "measurementSystem", in: "body", required: false, kind: "string", enum: ["imperial", "metric"] }
|
|
461
|
+
]
|
|
462
|
+
},
|
|
463
|
+
create_product: {
|
|
464
|
+
method: "POST",
|
|
465
|
+
path: "/products",
|
|
466
|
+
tag: "Products",
|
|
467
|
+
summary: "Create a product",
|
|
468
|
+
body: true,
|
|
469
|
+
response: "json",
|
|
470
|
+
params: [
|
|
471
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
472
|
+
{ name: "listId", in: "body", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
473
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
474
|
+
{ name: "sku", in: "body", required: false, kind: "string" },
|
|
475
|
+
{ name: "unitOfMeasure", in: "body", required: false, kind: "string" },
|
|
476
|
+
{ name: "costPerUnit", in: "body", required: false, kind: "number" },
|
|
477
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
478
|
+
{ name: "markupPercent", in: "body", required: false, kind: "number" },
|
|
479
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" },
|
|
480
|
+
{ name: "position", in: "body", required: false, kind: "integer" }
|
|
481
|
+
]
|
|
482
|
+
},
|
|
483
|
+
create_project: {
|
|
484
|
+
method: "POST",
|
|
485
|
+
path: "/projects",
|
|
486
|
+
tag: "Projects",
|
|
487
|
+
summary: "Create a project",
|
|
488
|
+
body: true,
|
|
489
|
+
response: "json",
|
|
490
|
+
params: [
|
|
491
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
492
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
493
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
494
|
+
]
|
|
495
|
+
},
|
|
496
|
+
create_symbol: {
|
|
497
|
+
method: "POST",
|
|
498
|
+
path: "/projects/{id}/symbols",
|
|
499
|
+
tag: "Takeoff",
|
|
500
|
+
summary: "Create a takeoff symbol",
|
|
501
|
+
body: true,
|
|
502
|
+
response: "json",
|
|
503
|
+
params: [
|
|
504
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
505
|
+
{ name: "isTemplate", in: "body", required: false, kind: "boolean" },
|
|
506
|
+
{ name: "listId", in: "body", required: false, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
507
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
508
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["count", "linear", "area"] },
|
|
509
|
+
{ name: "color", in: "body", required: true, kind: "string" },
|
|
510
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
511
|
+
{ name: "subcategory", in: "body", required: false, kind: "string" },
|
|
512
|
+
{ name: "groupId", in: "body", required: false, kind: "string", description: "Takeoff group id: the id the API returns for a takeoff group (a string of 32 lowercase letters and digits)." },
|
|
513
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
514
|
+
]
|
|
515
|
+
},
|
|
516
|
+
create_vendor: {
|
|
517
|
+
method: "POST",
|
|
518
|
+
path: "/vendors",
|
|
519
|
+
tag: "Vendors",
|
|
520
|
+
summary: "Create a vendor",
|
|
521
|
+
body: true,
|
|
522
|
+
response: "json",
|
|
523
|
+
params: [
|
|
524
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
525
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
526
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["supplier", "subcontractor", "rental"] },
|
|
527
|
+
{ name: "kind", in: "body", required: false, kind: "string", enum: ["sub", "supplier", "surety", "insurer", "agency", "owner", "architect", "engineer", "other", null] },
|
|
528
|
+
{ name: "contactName", in: "body", required: false, kind: "string" },
|
|
529
|
+
{ name: "email", in: "body", required: false, kind: "string" },
|
|
530
|
+
{ name: "phone", in: "body", required: false, kind: "string" },
|
|
531
|
+
{ name: "website", in: "body", required: false, kind: "string" },
|
|
532
|
+
{ name: "address", in: "body", required: false, kind: "object" },
|
|
533
|
+
{ name: "paymentTerms", in: "body", required: false, kind: "string" },
|
|
534
|
+
{ name: "accountNumber", in: "body", required: false, kind: "string" },
|
|
535
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
536
|
+
{ name: "tags", in: "body", required: false, kind: "array" },
|
|
537
|
+
{ name: "isPreferred", in: "body", required: false, kind: "boolean" }
|
|
538
|
+
]
|
|
539
|
+
},
|
|
540
|
+
create_vendor_quote: {
|
|
541
|
+
method: "POST",
|
|
542
|
+
path: "/projects/{id}/quotes",
|
|
543
|
+
tag: "Vendor quotes",
|
|
544
|
+
summary: "Create a vendor quote request",
|
|
545
|
+
body: true,
|
|
546
|
+
response: "json",
|
|
547
|
+
params: [
|
|
548
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
549
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
550
|
+
{ name: "vendorId", in: "body", required: true, kind: "string" },
|
|
551
|
+
{ name: "quoteNumber", in: "body", required: true, kind: "string" },
|
|
552
|
+
{ name: "lineItems", in: "body", required: false, kind: "array" },
|
|
553
|
+
{ name: "subtotal", in: "body", required: false, kind: "number", description: "Sum of the line items' totalPrice when lineItems are sent; absent counts as 0" },
|
|
554
|
+
{ name: "tax", in: "body", required: false, kind: "number" },
|
|
555
|
+
{ name: "shipping", in: "body", required: false, kind: "number" },
|
|
556
|
+
{ name: "total", in: "body", required: false, kind: "number", description: "Must equal subtotal + tax + shipping (an absent amount counts as 0); absent counts as 0" },
|
|
557
|
+
{ name: "validUntil", in: "body", required: false, kind: "number", description: "Quote expiry as epoch milliseconds" },
|
|
558
|
+
{ name: "notes", in: "body", required: false, kind: "string" }
|
|
559
|
+
]
|
|
560
|
+
},
|
|
561
|
+
create_webhook_endpoint: {
|
|
562
|
+
method: "POST",
|
|
563
|
+
path: "/webhook_endpoints",
|
|
564
|
+
tag: "Webhooks",
|
|
565
|
+
summary: "Create a webhook endpoint",
|
|
566
|
+
body: true,
|
|
567
|
+
response: "json",
|
|
568
|
+
params: [
|
|
569
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
570
|
+
{ name: "url", in: "body", required: true, kind: "string" },
|
|
571
|
+
{ name: "events", in: "body", required: true, kind: "array" },
|
|
572
|
+
{ name: "description", in: "body", required: false, kind: "string" }
|
|
573
|
+
]
|
|
574
|
+
},
|
|
575
|
+
create_widget: {
|
|
576
|
+
method: "POST",
|
|
577
|
+
path: "/widgets",
|
|
578
|
+
tag: "Widgets",
|
|
579
|
+
summary: "Create a widget",
|
|
580
|
+
body: true,
|
|
581
|
+
response: "json",
|
|
582
|
+
params: [
|
|
583
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
584
|
+
{ name: "name", in: "body", required: true, kind: "string" },
|
|
585
|
+
{ name: "config", in: "body", required: false, kind: "object" }
|
|
586
|
+
]
|
|
587
|
+
},
|
|
588
|
+
delete_organization: {
|
|
589
|
+
method: "DELETE",
|
|
590
|
+
path: "/organizations/{id}",
|
|
591
|
+
tag: "Account",
|
|
592
|
+
summary: "Release a company your app's company owns",
|
|
593
|
+
body: false,
|
|
594
|
+
response: "json",
|
|
595
|
+
params: [
|
|
596
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "The id of a company your app's company owns, from GET /v1/organizations or its creation." }
|
|
597
|
+
]
|
|
598
|
+
},
|
|
599
|
+
delete_webhook_endpoint: {
|
|
600
|
+
method: "DELETE",
|
|
601
|
+
path: "/webhook_endpoints/{id}",
|
|
602
|
+
tag: "Webhooks",
|
|
603
|
+
summary: "Delete a webhook endpoint",
|
|
604
|
+
body: false,
|
|
605
|
+
response: "empty",
|
|
606
|
+
params: [
|
|
607
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
608
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
609
|
+
]
|
|
610
|
+
},
|
|
611
|
+
disconnect_integration: {
|
|
612
|
+
method: "DELETE",
|
|
613
|
+
path: "/integrations/{id}",
|
|
614
|
+
tag: "Integrations",
|
|
615
|
+
summary: "Disconnect an integration",
|
|
616
|
+
body: false,
|
|
617
|
+
response: "empty",
|
|
618
|
+
params: [
|
|
619
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
620
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
621
|
+
]
|
|
622
|
+
},
|
|
623
|
+
export_project: {
|
|
624
|
+
method: "GET",
|
|
625
|
+
path: "/projects/{id}/export",
|
|
626
|
+
tag: "Projects",
|
|
627
|
+
summary: "Export a project as structured JSON",
|
|
628
|
+
body: false,
|
|
629
|
+
response: "json",
|
|
630
|
+
params: [
|
|
631
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
632
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
633
|
+
]
|
|
634
|
+
},
|
|
635
|
+
find_symbol_elements: {
|
|
636
|
+
method: "POST",
|
|
637
|
+
path: "/pages/{id}/elements/find",
|
|
638
|
+
tag: "Takeoff",
|
|
639
|
+
summary: "Detect instances of an element on a page via Claude Vision",
|
|
640
|
+
body: true,
|
|
641
|
+
response: "json",
|
|
642
|
+
params: [
|
|
643
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." },
|
|
644
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
645
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
646
|
+
{ name: "elementDescription", in: "body", required: true, kind: "string", description: 'Natural-language description of what to find (e.g. "electrical outlets", "doors")' },
|
|
647
|
+
{ name: "measurementType", in: "body", required: true, kind: "string", enum: ["count", "linear", "area"], description: "How detections should be shaped (count marker / linear line / area rectangle)" },
|
|
648
|
+
{ name: "persist", in: "body", required: false, kind: "boolean", description: "When true, writes the detected shapes to the realtime canvas via the sync-worker. Returns persistedShapeIds on success." }
|
|
649
|
+
]
|
|
650
|
+
},
|
|
651
|
+
generate_estimate: {
|
|
652
|
+
method: "POST",
|
|
653
|
+
path: "/projects/{id}/estimates/generate",
|
|
654
|
+
tag: "Estimates",
|
|
655
|
+
summary: "Stream an AI-generated cost estimate",
|
|
656
|
+
body: true,
|
|
657
|
+
response: "stream",
|
|
658
|
+
params: [
|
|
659
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
660
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
661
|
+
{ name: "query", in: "body", required: true, kind: "string", description: "Natural-language prompt describing the scope of the estimate" },
|
|
662
|
+
{ name: "notes", in: "body", required: false, kind: "string", description: "Additional notes appended to the prompt" },
|
|
663
|
+
{ name: "measurementContext", in: "body", required: false, kind: "string", description: "Pre-serialized takeoff data to ground the estimator (see GET /v1/projects/{id}/takeoff-summary)" }
|
|
664
|
+
]
|
|
665
|
+
},
|
|
666
|
+
get_assessment: {
|
|
667
|
+
method: "GET",
|
|
668
|
+
path: "/assessments/{id}",
|
|
669
|
+
tag: "Takeoff",
|
|
670
|
+
summary: "Get an assessment by ID",
|
|
671
|
+
body: false,
|
|
672
|
+
response: "json",
|
|
673
|
+
params: [
|
|
674
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff assessment id: the id the API returns for a takeoff assessment (a string of 32 lowercase letters and digits)." }
|
|
675
|
+
]
|
|
676
|
+
},
|
|
677
|
+
get_best_price_for_product: {
|
|
678
|
+
method: "GET",
|
|
679
|
+
path: "/products/{id}/best-price",
|
|
680
|
+
tag: "Vendor prices",
|
|
681
|
+
summary: "Best vendor price for a product",
|
|
682
|
+
body: false,
|
|
683
|
+
response: "json",
|
|
684
|
+
params: [
|
|
685
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
686
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
687
|
+
{ name: "quantity", in: "query", required: false, kind: "number" }
|
|
688
|
+
]
|
|
689
|
+
},
|
|
690
|
+
get_contact: {
|
|
691
|
+
method: "GET",
|
|
692
|
+
path: "/contacts/{id}",
|
|
693
|
+
tag: "Contacts",
|
|
694
|
+
summary: "Get a contact by ID",
|
|
695
|
+
body: false,
|
|
696
|
+
response: "json",
|
|
697
|
+
params: [
|
|
698
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Contact id: the id the API returns for a contact (a string of 32 lowercase letters and digits)." },
|
|
699
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
700
|
+
]
|
|
701
|
+
},
|
|
702
|
+
get_conversation: {
|
|
703
|
+
method: "GET",
|
|
704
|
+
path: "/conversations/{id}",
|
|
705
|
+
tag: "Conversations",
|
|
706
|
+
summary: "Get a conversation by ID",
|
|
707
|
+
body: false,
|
|
708
|
+
response: "json",
|
|
709
|
+
params: [
|
|
710
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Conversation id: the id the API returns for a conversation (a string of 32 lowercase letters and digits)." },
|
|
711
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
712
|
+
]
|
|
713
|
+
},
|
|
714
|
+
get_credit_balance: {
|
|
715
|
+
method: "GET",
|
|
716
|
+
path: "/credits/balance",
|
|
717
|
+
tag: "Credits",
|
|
718
|
+
summary: "Get credit balance for an organization",
|
|
719
|
+
body: false,
|
|
720
|
+
response: "json",
|
|
721
|
+
params: [
|
|
722
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
723
|
+
]
|
|
724
|
+
},
|
|
725
|
+
get_credit_usage_history: {
|
|
726
|
+
method: "GET",
|
|
727
|
+
path: "/credits/usage",
|
|
728
|
+
tag: "Credits",
|
|
729
|
+
summary: "Get credit usage history",
|
|
730
|
+
body: false,
|
|
731
|
+
response: "json",
|
|
732
|
+
params: [
|
|
733
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
734
|
+
{ name: "limit", in: "query", required: false, kind: "integer" },
|
|
735
|
+
{ name: "cursor", in: "query", required: false, kind: "number" },
|
|
736
|
+
{ name: "dateFrom", in: "query", required: false, kind: "number" },
|
|
737
|
+
{ name: "dateTo", in: "query", required: false, kind: "number" },
|
|
738
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["issuance", "consumption"] },
|
|
739
|
+
{ name: "kind", in: "query", required: false, kind: "string", enum: ["monthly", "gifted", "purchased"] }
|
|
740
|
+
]
|
|
741
|
+
},
|
|
742
|
+
get_credit_usage_stats: {
|
|
743
|
+
method: "GET",
|
|
744
|
+
path: "/credits/stats",
|
|
745
|
+
tag: "Credits",
|
|
746
|
+
summary: "Get credit usage stats",
|
|
747
|
+
body: false,
|
|
748
|
+
response: "json",
|
|
749
|
+
params: [
|
|
750
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
751
|
+
{ name: "dateFrom", in: "query", required: false, kind: "number" },
|
|
752
|
+
{ name: "dateTo", in: "query", required: false, kind: "number" }
|
|
753
|
+
]
|
|
754
|
+
},
|
|
755
|
+
get_custom_property: {
|
|
756
|
+
method: "GET",
|
|
757
|
+
path: "/custom-properties/{id}",
|
|
758
|
+
tag: "Custom properties",
|
|
759
|
+
summary: "Get a custom property",
|
|
760
|
+
body: false,
|
|
761
|
+
response: "json",
|
|
762
|
+
params: [
|
|
763
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Custom property id: the id the API returns for a custom property (a string of 32 lowercase letters and digits)." },
|
|
764
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
765
|
+
]
|
|
766
|
+
},
|
|
767
|
+
get_document: {
|
|
768
|
+
method: "GET",
|
|
769
|
+
path: "/documents/{id}",
|
|
770
|
+
tag: "Documents",
|
|
771
|
+
summary: "Get a single document",
|
|
772
|
+
body: false,
|
|
773
|
+
response: "json",
|
|
774
|
+
params: [
|
|
775
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Document id: the id the API returns for a document (a string of 32 lowercase letters and digits)." },
|
|
776
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
777
|
+
]
|
|
778
|
+
},
|
|
779
|
+
get_file: {
|
|
780
|
+
method: "GET",
|
|
781
|
+
path: "/files/{id}",
|
|
782
|
+
tag: "Files",
|
|
783
|
+
summary: "Get a file with its pages",
|
|
784
|
+
body: false,
|
|
785
|
+
response: "json",
|
|
786
|
+
params: [
|
|
787
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "File id: the id the API returns for a file (a string of 32 lowercase letters and digits)." },
|
|
788
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
789
|
+
]
|
|
790
|
+
},
|
|
791
|
+
get_file_url: {
|
|
792
|
+
method: "GET",
|
|
793
|
+
path: "/files/{id}/url",
|
|
794
|
+
tag: "Files",
|
|
795
|
+
summary: "Get a presigned download URL for a file",
|
|
796
|
+
body: false,
|
|
797
|
+
response: "json",
|
|
798
|
+
params: [
|
|
799
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "File id: the id the API returns for a file (a string of 32 lowercase letters and digits)." },
|
|
800
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
801
|
+
]
|
|
802
|
+
},
|
|
803
|
+
get_folder: {
|
|
804
|
+
method: "GET",
|
|
805
|
+
path: "/folders/{id}",
|
|
806
|
+
tag: "Folders",
|
|
807
|
+
summary: "Get a folder by ID",
|
|
808
|
+
body: false,
|
|
809
|
+
response: "json",
|
|
810
|
+
params: [
|
|
811
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." }
|
|
812
|
+
]
|
|
813
|
+
},
|
|
814
|
+
get_labor_crew: {
|
|
815
|
+
method: "GET",
|
|
816
|
+
path: "/labor/crews/{id}",
|
|
817
|
+
tag: "Labor",
|
|
818
|
+
summary: "Get a labor crew",
|
|
819
|
+
body: false,
|
|
820
|
+
response: "json",
|
|
821
|
+
params: [
|
|
822
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
823
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
824
|
+
]
|
|
825
|
+
},
|
|
826
|
+
get_labor_equipment: {
|
|
827
|
+
method: "GET",
|
|
828
|
+
path: "/labor/equipment/{id}",
|
|
829
|
+
tag: "Labor",
|
|
830
|
+
summary: "Get a labor equipment entry",
|
|
831
|
+
body: false,
|
|
832
|
+
response: "json",
|
|
833
|
+
params: [
|
|
834
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
835
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
836
|
+
]
|
|
837
|
+
},
|
|
838
|
+
get_labor_rate: {
|
|
839
|
+
method: "GET",
|
|
840
|
+
path: "/labor/rates/{id}",
|
|
841
|
+
tag: "Labor",
|
|
842
|
+
summary: "Get a labor rate",
|
|
843
|
+
body: false,
|
|
844
|
+
response: "json",
|
|
845
|
+
params: [
|
|
846
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
847
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
848
|
+
]
|
|
849
|
+
},
|
|
850
|
+
get_latest_assessment: {
|
|
851
|
+
method: "GET",
|
|
852
|
+
path: "/projects/{id}/assessments/latest",
|
|
853
|
+
tag: "Takeoff",
|
|
854
|
+
summary: "Get the most recent assessment with elements",
|
|
855
|
+
body: false,
|
|
856
|
+
response: "json",
|
|
857
|
+
params: [
|
|
858
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." }
|
|
859
|
+
]
|
|
860
|
+
},
|
|
861
|
+
get_list: {
|
|
862
|
+
method: "GET",
|
|
863
|
+
path: "/lists/{id}",
|
|
864
|
+
tag: "Lists",
|
|
865
|
+
summary: "Get a single list",
|
|
866
|
+
body: false,
|
|
867
|
+
response: "json",
|
|
868
|
+
params: [
|
|
869
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
870
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
871
|
+
]
|
|
872
|
+
},
|
|
873
|
+
get_me: {
|
|
874
|
+
method: "GET",
|
|
875
|
+
path: "/me",
|
|
876
|
+
tag: "Account",
|
|
877
|
+
summary: "Get the authenticated caller",
|
|
878
|
+
body: false,
|
|
879
|
+
response: "json",
|
|
880
|
+
params: []
|
|
881
|
+
},
|
|
882
|
+
get_measurement: {
|
|
883
|
+
method: "GET",
|
|
884
|
+
path: "/pages/{pageId}/measurements/{shapeId}",
|
|
885
|
+
tag: "Takeoff",
|
|
886
|
+
summary: "Get a measurement by shape ID",
|
|
887
|
+
body: false,
|
|
888
|
+
response: "json",
|
|
889
|
+
params: [
|
|
890
|
+
{ name: "pageId", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." },
|
|
891
|
+
{ name: "shapeId", in: "path", required: true, kind: "string" }
|
|
892
|
+
]
|
|
893
|
+
},
|
|
894
|
+
get_page: {
|
|
895
|
+
method: "GET",
|
|
896
|
+
path: "/pages/{id}",
|
|
897
|
+
tag: "Pages",
|
|
898
|
+
summary: "Get a single page",
|
|
899
|
+
body: false,
|
|
900
|
+
response: "json",
|
|
901
|
+
params: [
|
|
902
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." }
|
|
903
|
+
]
|
|
904
|
+
},
|
|
905
|
+
get_page_content: {
|
|
906
|
+
method: "GET",
|
|
907
|
+
path: "/pages/{id}/content",
|
|
908
|
+
tag: "Pages",
|
|
909
|
+
summary: "Extracted text and AI description for a page",
|
|
910
|
+
body: false,
|
|
911
|
+
response: "json",
|
|
912
|
+
params: [
|
|
913
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." }
|
|
914
|
+
]
|
|
915
|
+
},
|
|
916
|
+
get_prices_for_product: {
|
|
917
|
+
method: "GET",
|
|
918
|
+
path: "/products/{id}/prices",
|
|
919
|
+
tag: "Vendor prices",
|
|
920
|
+
summary: "List vendor prices for a product",
|
|
921
|
+
body: false,
|
|
922
|
+
response: "json",
|
|
923
|
+
params: [
|
|
924
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
925
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
926
|
+
]
|
|
927
|
+
},
|
|
928
|
+
get_product: {
|
|
929
|
+
method: "GET",
|
|
930
|
+
path: "/products/{id}",
|
|
931
|
+
tag: "Products",
|
|
932
|
+
summary: "Get a single product",
|
|
933
|
+
body: false,
|
|
934
|
+
response: "json",
|
|
935
|
+
params: [
|
|
936
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
937
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
938
|
+
]
|
|
939
|
+
},
|
|
940
|
+
get_project: {
|
|
941
|
+
method: "GET",
|
|
942
|
+
path: "/projects/{id}",
|
|
943
|
+
tag: "Projects",
|
|
944
|
+
summary: "Get a single project",
|
|
945
|
+
body: false,
|
|
946
|
+
response: "json",
|
|
947
|
+
params: [
|
|
948
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
949
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
950
|
+
]
|
|
951
|
+
},
|
|
952
|
+
get_project_relations: {
|
|
953
|
+
method: "GET",
|
|
954
|
+
path: "/projects/{id}/relations",
|
|
955
|
+
tag: "Projects",
|
|
956
|
+
summary: "Get a project with page/estimate counts",
|
|
957
|
+
body: false,
|
|
958
|
+
response: "json",
|
|
959
|
+
params: [
|
|
960
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
961
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
962
|
+
]
|
|
963
|
+
},
|
|
964
|
+
get_spreadsheet: {
|
|
965
|
+
method: "GET",
|
|
966
|
+
path: "/spreadsheets/{id}",
|
|
967
|
+
tag: "Spreadsheets",
|
|
968
|
+
summary: "Get a single spreadsheet",
|
|
969
|
+
body: false,
|
|
970
|
+
response: "json",
|
|
971
|
+
params: [
|
|
972
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Spreadsheet id: the id the API returns for a spreadsheet (a string of 32 lowercase letters and digits)." },
|
|
973
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
974
|
+
]
|
|
975
|
+
},
|
|
976
|
+
get_symbol: {
|
|
977
|
+
method: "GET",
|
|
978
|
+
path: "/symbols/{id}",
|
|
979
|
+
tag: "Takeoff",
|
|
980
|
+
summary: "Get a takeoff symbol",
|
|
981
|
+
body: false,
|
|
982
|
+
response: "json",
|
|
983
|
+
params: [
|
|
984
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
985
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
986
|
+
]
|
|
987
|
+
},
|
|
988
|
+
get_symbol_line_item: {
|
|
989
|
+
method: "GET",
|
|
990
|
+
path: "/symbols/{symbolId}/line-items/{id}",
|
|
991
|
+
tag: "Takeoff",
|
|
992
|
+
summary: "Get a single line item on a symbol",
|
|
993
|
+
body: false,
|
|
994
|
+
response: "json",
|
|
995
|
+
params: [
|
|
996
|
+
{ name: "symbolId", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
997
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
998
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
999
|
+
]
|
|
1000
|
+
},
|
|
1001
|
+
get_takeoff_summary: {
|
|
1002
|
+
method: "GET",
|
|
1003
|
+
path: "/projects/{id}/takeoff-summary",
|
|
1004
|
+
tag: "Takeoff",
|
|
1005
|
+
summary: "Aggregated totals across takeoff symbols in a project",
|
|
1006
|
+
body: false,
|
|
1007
|
+
response: "json",
|
|
1008
|
+
params: [
|
|
1009
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." }
|
|
1010
|
+
]
|
|
1011
|
+
},
|
|
1012
|
+
get_unread_notification_count: {
|
|
1013
|
+
method: "GET",
|
|
1014
|
+
path: "/notifications/unread-count",
|
|
1015
|
+
tag: "Notifications",
|
|
1016
|
+
summary: "Get unread notification count",
|
|
1017
|
+
body: false,
|
|
1018
|
+
response: "json",
|
|
1019
|
+
params: []
|
|
1020
|
+
},
|
|
1021
|
+
get_usage: {
|
|
1022
|
+
method: "GET",
|
|
1023
|
+
path: "/usage",
|
|
1024
|
+
tag: "Credits",
|
|
1025
|
+
summary: "Get AI usage by app, key, end user or operation",
|
|
1026
|
+
body: false,
|
|
1027
|
+
response: "json",
|
|
1028
|
+
params: [
|
|
1029
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1030
|
+
{ name: "groupBy", in: "query", required: true, kind: "string", enum: ["app", "key", "end_user", "operation"], description: "What to group by: app, key, end user (the Exayard-End-User header) or operation." },
|
|
1031
|
+
{ name: "dateFrom", in: "query", required: false, kind: "number", description: "Start of the window, epoch ms, inclusive. Default: this billing month." },
|
|
1032
|
+
{ name: "dateTo", in: "query", required: false, kind: "number", description: "End of the window, epoch ms, exclusive. Default: now when dateFrom is given, else the end of this billing month." },
|
|
1033
|
+
{ name: "appId", in: "query", required: false, kind: "string", description: "Only the usage of this app." },
|
|
1034
|
+
{ name: "appKeyId", in: "query", required: false, kind: "string", description: "Only the usage of this key." },
|
|
1035
|
+
{ name: "endUser", in: "query", required: false, kind: "string", description: "Only the usage of this end user." }
|
|
1036
|
+
]
|
|
1037
|
+
},
|
|
1038
|
+
get_vendor: {
|
|
1039
|
+
method: "GET",
|
|
1040
|
+
path: "/vendors/{id}",
|
|
1041
|
+
tag: "Vendors",
|
|
1042
|
+
summary: "Get a single vendor",
|
|
1043
|
+
body: false,
|
|
1044
|
+
response: "json",
|
|
1045
|
+
params: [
|
|
1046
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Vendor id: the id the API returns for a vendor (a string of 32 lowercase letters and digits)." },
|
|
1047
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1048
|
+
]
|
|
1049
|
+
},
|
|
1050
|
+
get_vendor_quote: {
|
|
1051
|
+
method: "GET",
|
|
1052
|
+
path: "/quotes/{id}",
|
|
1053
|
+
tag: "Vendor quotes",
|
|
1054
|
+
summary: "Get a vendor quote",
|
|
1055
|
+
body: false,
|
|
1056
|
+
response: "json",
|
|
1057
|
+
params: [
|
|
1058
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1059
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1060
|
+
]
|
|
1061
|
+
},
|
|
1062
|
+
get_widget: {
|
|
1063
|
+
method: "GET",
|
|
1064
|
+
path: "/widgets/{id}",
|
|
1065
|
+
tag: "Widgets",
|
|
1066
|
+
summary: "Get a widget",
|
|
1067
|
+
body: false,
|
|
1068
|
+
response: "json",
|
|
1069
|
+
params: [
|
|
1070
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Widget id: the id the API returns for a widget (a string of 32 lowercase letters and digits)." },
|
|
1071
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1072
|
+
]
|
|
1073
|
+
},
|
|
1074
|
+
list_assessments: {
|
|
1075
|
+
method: "GET",
|
|
1076
|
+
path: "/projects/{id}/assessments",
|
|
1077
|
+
tag: "Takeoff",
|
|
1078
|
+
summary: "List assessments for a project",
|
|
1079
|
+
body: false,
|
|
1080
|
+
response: "json",
|
|
1081
|
+
params: [
|
|
1082
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." }
|
|
1083
|
+
]
|
|
1084
|
+
},
|
|
1085
|
+
list_contacts: {
|
|
1086
|
+
method: "GET",
|
|
1087
|
+
path: "/contacts",
|
|
1088
|
+
tag: "Contacts",
|
|
1089
|
+
summary: "List contacts captured by widgets",
|
|
1090
|
+
body: false,
|
|
1091
|
+
response: "json",
|
|
1092
|
+
params: [
|
|
1093
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1094
|
+
{ name: "widgetId", in: "query", required: false, kind: "string" },
|
|
1095
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1096
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1097
|
+
]
|
|
1098
|
+
},
|
|
1099
|
+
list_conversation_files: {
|
|
1100
|
+
method: "GET",
|
|
1101
|
+
path: "/conversations/{id}/files",
|
|
1102
|
+
tag: "Conversations",
|
|
1103
|
+
summary: "List files uploaded in a conversation",
|
|
1104
|
+
body: false,
|
|
1105
|
+
response: "json",
|
|
1106
|
+
params: [
|
|
1107
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Conversation id: the id the API returns for a conversation (a string of 32 lowercase letters and digits)." },
|
|
1108
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1109
|
+
{ name: "imagesOnly", in: "query", required: false, kind: "boolean" }
|
|
1110
|
+
]
|
|
1111
|
+
},
|
|
1112
|
+
list_conversations: {
|
|
1113
|
+
method: "GET",
|
|
1114
|
+
path: "/conversations",
|
|
1115
|
+
tag: "Conversations",
|
|
1116
|
+
summary: "List conversations for the caller",
|
|
1117
|
+
body: false,
|
|
1118
|
+
response: "json",
|
|
1119
|
+
params: [
|
|
1120
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1121
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["ai_chat", "project_activity", "whatsapp_chat"] },
|
|
1122
|
+
{ name: "channel", in: "query", required: false, kind: "string", enum: ["app", "email", "whatsapp"] },
|
|
1123
|
+
{ name: "limit", in: "query", required: false, kind: "integer" },
|
|
1124
|
+
{ name: "cursor", in: "query", required: false, kind: "number" }
|
|
1125
|
+
]
|
|
1126
|
+
},
|
|
1127
|
+
list_custom_properties: {
|
|
1128
|
+
method: "GET",
|
|
1129
|
+
path: "/custom-properties",
|
|
1130
|
+
tag: "Custom properties",
|
|
1131
|
+
summary: "List custom properties in an organization",
|
|
1132
|
+
body: false,
|
|
1133
|
+
response: "json",
|
|
1134
|
+
params: [
|
|
1135
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1136
|
+
{ name: "recordType", in: "query", required: false, kind: "string", enum: ["project", "countTakeoff", "linearTakeoff", "areaTakeoff", "product"] }
|
|
1137
|
+
]
|
|
1138
|
+
},
|
|
1139
|
+
list_documents: {
|
|
1140
|
+
method: "GET",
|
|
1141
|
+
path: "/projects/{id}/documents",
|
|
1142
|
+
tag: "Documents",
|
|
1143
|
+
summary: "List documents in a project",
|
|
1144
|
+
body: false,
|
|
1145
|
+
response: "json",
|
|
1146
|
+
params: [
|
|
1147
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1148
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1149
|
+
]
|
|
1150
|
+
},
|
|
1151
|
+
list_files: {
|
|
1152
|
+
method: "GET",
|
|
1153
|
+
path: "/files",
|
|
1154
|
+
tag: "Files",
|
|
1155
|
+
summary: "List files in a project",
|
|
1156
|
+
body: false,
|
|
1157
|
+
response: "json",
|
|
1158
|
+
params: [
|
|
1159
|
+
{ name: "projectId", in: "query", required: true, kind: "string" },
|
|
1160
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1161
|
+
]
|
|
1162
|
+
},
|
|
1163
|
+
list_folder_contents: {
|
|
1164
|
+
method: "GET",
|
|
1165
|
+
path: "/folders/contents",
|
|
1166
|
+
tag: "Folders",
|
|
1167
|
+
summary: "List files and folders inside a folder",
|
|
1168
|
+
body: false,
|
|
1169
|
+
response: "json",
|
|
1170
|
+
params: [
|
|
1171
|
+
{ name: "projectId", in: "query", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1172
|
+
{ name: "parentId", in: "query", required: false, kind: "string" }
|
|
1173
|
+
]
|
|
1174
|
+
},
|
|
1175
|
+
list_folders: {
|
|
1176
|
+
method: "GET",
|
|
1177
|
+
path: "/folders",
|
|
1178
|
+
tag: "Folders",
|
|
1179
|
+
summary: "List folders in a project",
|
|
1180
|
+
body: false,
|
|
1181
|
+
response: "json",
|
|
1182
|
+
params: [
|
|
1183
|
+
{ name: "projectId", in: "query", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." }
|
|
1184
|
+
]
|
|
1185
|
+
},
|
|
1186
|
+
list_groups: {
|
|
1187
|
+
method: "GET",
|
|
1188
|
+
path: "/projects/{id}/groups",
|
|
1189
|
+
tag: "Takeoff",
|
|
1190
|
+
summary: "List takeoff groups in a project",
|
|
1191
|
+
body: false,
|
|
1192
|
+
response: "json",
|
|
1193
|
+
params: [
|
|
1194
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." }
|
|
1195
|
+
]
|
|
1196
|
+
},
|
|
1197
|
+
list_installations: {
|
|
1198
|
+
method: "GET",
|
|
1199
|
+
path: "/installations",
|
|
1200
|
+
tag: "Account",
|
|
1201
|
+
summary: "List the companies where the calling app is installed",
|
|
1202
|
+
body: false,
|
|
1203
|
+
response: "json",
|
|
1204
|
+
params: [
|
|
1205
|
+
{ name: "limit", in: "query", required: false, kind: "integer", description: "Companies per page (1 to 100, default 50)." },
|
|
1206
|
+
{ name: "cursor", in: "query", required: false, kind: "string", description: "`nextCursor` from the previous page." }
|
|
1207
|
+
]
|
|
1208
|
+
},
|
|
1209
|
+
list_integrations: {
|
|
1210
|
+
method: "GET",
|
|
1211
|
+
path: "/integrations",
|
|
1212
|
+
tag: "Integrations",
|
|
1213
|
+
summary: "List integrations connected to an organization",
|
|
1214
|
+
body: false,
|
|
1215
|
+
response: "json",
|
|
1216
|
+
params: [
|
|
1217
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1218
|
+
]
|
|
1219
|
+
},
|
|
1220
|
+
list_labor_crews: {
|
|
1221
|
+
method: "GET",
|
|
1222
|
+
path: "/labor/crews",
|
|
1223
|
+
tag: "Labor",
|
|
1224
|
+
summary: "List labor crews",
|
|
1225
|
+
body: false,
|
|
1226
|
+
response: "json",
|
|
1227
|
+
params: [
|
|
1228
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1229
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1230
|
+
]
|
|
1231
|
+
},
|
|
1232
|
+
list_labor_equipment: {
|
|
1233
|
+
method: "GET",
|
|
1234
|
+
path: "/labor/equipment",
|
|
1235
|
+
tag: "Labor",
|
|
1236
|
+
summary: "List labor equipment",
|
|
1237
|
+
body: false,
|
|
1238
|
+
response: "json",
|
|
1239
|
+
params: [
|
|
1240
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1241
|
+
{ name: "category", in: "query", required: false, kind: "string" },
|
|
1242
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1243
|
+
]
|
|
1244
|
+
},
|
|
1245
|
+
list_labor_rates: {
|
|
1246
|
+
method: "GET",
|
|
1247
|
+
path: "/labor/rates",
|
|
1248
|
+
tag: "Labor",
|
|
1249
|
+
summary: "List labor rates",
|
|
1250
|
+
body: false,
|
|
1251
|
+
response: "json",
|
|
1252
|
+
params: [
|
|
1253
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1254
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1255
|
+
]
|
|
1256
|
+
},
|
|
1257
|
+
list_layers: {
|
|
1258
|
+
method: "GET",
|
|
1259
|
+
path: "/projects/{id}/layers",
|
|
1260
|
+
tag: "Takeoff",
|
|
1261
|
+
summary: "List takeoff layers in a project",
|
|
1262
|
+
body: false,
|
|
1263
|
+
response: "json",
|
|
1264
|
+
params: [
|
|
1265
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1266
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1267
|
+
]
|
|
1268
|
+
},
|
|
1269
|
+
list_lists: {
|
|
1270
|
+
method: "GET",
|
|
1271
|
+
path: "/lists",
|
|
1272
|
+
tag: "Lists",
|
|
1273
|
+
summary: "List product lists and takeoff templates",
|
|
1274
|
+
body: false,
|
|
1275
|
+
response: "json",
|
|
1276
|
+
params: [
|
|
1277
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1278
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["product", "takeoffTemplate"] },
|
|
1279
|
+
{ name: "category", in: "query", required: false, kind: "string" },
|
|
1280
|
+
{ name: "includeInactive", in: "query", required: false, kind: "boolean" }
|
|
1281
|
+
]
|
|
1282
|
+
},
|
|
1283
|
+
list_measurements_by_page: {
|
|
1284
|
+
method: "GET",
|
|
1285
|
+
path: "/pages/{id}/measurements",
|
|
1286
|
+
tag: "Takeoff",
|
|
1287
|
+
summary: "List measurements on a page",
|
|
1288
|
+
body: false,
|
|
1289
|
+
response: "json",
|
|
1290
|
+
params: [
|
|
1291
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." }
|
|
1292
|
+
]
|
|
1293
|
+
},
|
|
1294
|
+
list_measurements_by_symbol: {
|
|
1295
|
+
method: "GET",
|
|
1296
|
+
path: "/symbols/{id}/measurements",
|
|
1297
|
+
tag: "Takeoff",
|
|
1298
|
+
summary: "List measurements for a symbol",
|
|
1299
|
+
body: false,
|
|
1300
|
+
response: "json",
|
|
1301
|
+
params: [
|
|
1302
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
1303
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1304
|
+
]
|
|
1305
|
+
},
|
|
1306
|
+
list_messages: {
|
|
1307
|
+
method: "GET",
|
|
1308
|
+
path: "/projects/{id}/messages",
|
|
1309
|
+
tag: "Messages",
|
|
1310
|
+
summary: "List project activity messages",
|
|
1311
|
+
body: false,
|
|
1312
|
+
response: "json",
|
|
1313
|
+
params: [
|
|
1314
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1315
|
+
{ name: "pageId", in: "query", required: false, kind: "string" }
|
|
1316
|
+
]
|
|
1317
|
+
},
|
|
1318
|
+
list_notifications: {
|
|
1319
|
+
method: "GET",
|
|
1320
|
+
path: "/notifications",
|
|
1321
|
+
tag: "Notifications",
|
|
1322
|
+
summary: "List notifications for the caller",
|
|
1323
|
+
body: false,
|
|
1324
|
+
response: "json",
|
|
1325
|
+
params: [
|
|
1326
|
+
{ name: "limit", in: "query", required: false, kind: "integer" },
|
|
1327
|
+
{ name: "cursor", in: "query", required: false, kind: "string" }
|
|
1328
|
+
]
|
|
1329
|
+
},
|
|
1330
|
+
list_org_documents: {
|
|
1331
|
+
method: "GET",
|
|
1332
|
+
path: "/documents",
|
|
1333
|
+
tag: "Documents",
|
|
1334
|
+
summary: "List documents in the organization",
|
|
1335
|
+
body: false,
|
|
1336
|
+
response: "json",
|
|
1337
|
+
params: [
|
|
1338
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1339
|
+
{ name: "type", in: "query", required: false, kind: "string" },
|
|
1340
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1341
|
+
]
|
|
1342
|
+
},
|
|
1343
|
+
list_organizations: {
|
|
1344
|
+
method: "GET",
|
|
1345
|
+
path: "/organizations",
|
|
1346
|
+
tag: "Account",
|
|
1347
|
+
summary: "List the companies your app's company owns",
|
|
1348
|
+
body: false,
|
|
1349
|
+
response: "json",
|
|
1350
|
+
params: [
|
|
1351
|
+
{ name: "limit", in: "query", required: false, kind: "integer", description: "Companies per page (1 to 100, default 50)." },
|
|
1352
|
+
{ name: "cursor", in: "query", required: false, kind: "string", description: "`nextCursor` from the previous page." }
|
|
1353
|
+
]
|
|
1354
|
+
},
|
|
1355
|
+
list_pages: {
|
|
1356
|
+
method: "GET",
|
|
1357
|
+
path: "/projects/{id}/pages",
|
|
1358
|
+
tag: "Pages",
|
|
1359
|
+
summary: "List pages in a project, grouped by file",
|
|
1360
|
+
body: false,
|
|
1361
|
+
response: "json",
|
|
1362
|
+
params: [
|
|
1363
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1364
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1365
|
+
]
|
|
1366
|
+
},
|
|
1367
|
+
list_products: {
|
|
1368
|
+
method: "GET",
|
|
1369
|
+
path: "/products",
|
|
1370
|
+
tag: "Products",
|
|
1371
|
+
summary: "List products in an organization",
|
|
1372
|
+
body: false,
|
|
1373
|
+
response: "json",
|
|
1374
|
+
params: [
|
|
1375
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1376
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1377
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1378
|
+
]
|
|
1379
|
+
},
|
|
1380
|
+
list_products_in_list: {
|
|
1381
|
+
method: "GET",
|
|
1382
|
+
path: "/lists/{id}/products",
|
|
1383
|
+
tag: "Products",
|
|
1384
|
+
summary: "List products in a list",
|
|
1385
|
+
body: false,
|
|
1386
|
+
response: "json",
|
|
1387
|
+
params: [
|
|
1388
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
1389
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1390
|
+
]
|
|
1391
|
+
},
|
|
1392
|
+
list_project_conversations: {
|
|
1393
|
+
method: "GET",
|
|
1394
|
+
path: "/projects/{id}/conversations",
|
|
1395
|
+
tag: "Conversations",
|
|
1396
|
+
summary: "List conversations in a project",
|
|
1397
|
+
body: false,
|
|
1398
|
+
response: "json",
|
|
1399
|
+
params: [
|
|
1400
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1401
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1402
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["ai_chat", "project_activity", "widget_chat"] }
|
|
1403
|
+
]
|
|
1404
|
+
},
|
|
1405
|
+
list_projects: {
|
|
1406
|
+
method: "GET",
|
|
1407
|
+
path: "/projects",
|
|
1408
|
+
tag: "Projects",
|
|
1409
|
+
summary: "List projects in an organization",
|
|
1410
|
+
body: false,
|
|
1411
|
+
response: "json",
|
|
1412
|
+
params: [
|
|
1413
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1414
|
+
{ name: "status", in: "query", required: false, kind: "string", enum: ["lead", "contacted", "qualified", "active", "archived"] },
|
|
1415
|
+
{ name: "search", in: "query", required: false, kind: "string" }
|
|
1416
|
+
]
|
|
1417
|
+
},
|
|
1418
|
+
list_spreadsheets: {
|
|
1419
|
+
method: "GET",
|
|
1420
|
+
path: "/projects/{id}/spreadsheets",
|
|
1421
|
+
tag: "Spreadsheets",
|
|
1422
|
+
summary: "List spreadsheets in a project",
|
|
1423
|
+
body: false,
|
|
1424
|
+
response: "json",
|
|
1425
|
+
params: [
|
|
1426
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1427
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1428
|
+
]
|
|
1429
|
+
},
|
|
1430
|
+
list_symbol_line_items: {
|
|
1431
|
+
method: "GET",
|
|
1432
|
+
path: "/symbols/{id}/line-items",
|
|
1433
|
+
tag: "Takeoff",
|
|
1434
|
+
summary: "List all line items on a takeoff symbol",
|
|
1435
|
+
body: false,
|
|
1436
|
+
response: "json",
|
|
1437
|
+
params: [
|
|
1438
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
1439
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1440
|
+
]
|
|
1441
|
+
},
|
|
1442
|
+
list_symbols: {
|
|
1443
|
+
method: "GET",
|
|
1444
|
+
path: "/projects/{id}/symbols",
|
|
1445
|
+
tag: "Takeoff",
|
|
1446
|
+
summary: "List takeoff symbols in a project",
|
|
1447
|
+
body: false,
|
|
1448
|
+
response: "json",
|
|
1449
|
+
params: [
|
|
1450
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1451
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["count", "linear", "area"] },
|
|
1452
|
+
{ name: "source", in: "query", required: false, kind: "string", enum: ["ai", "manual"] },
|
|
1453
|
+
{ name: "category", in: "query", required: false, kind: "string" }
|
|
1454
|
+
]
|
|
1455
|
+
},
|
|
1456
|
+
list_vendor_quotes: {
|
|
1457
|
+
method: "GET",
|
|
1458
|
+
path: "/projects/{id}/quotes",
|
|
1459
|
+
tag: "Vendor quotes",
|
|
1460
|
+
summary: "List vendor quotes on a project",
|
|
1461
|
+
body: false,
|
|
1462
|
+
response: "json",
|
|
1463
|
+
params: [
|
|
1464
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1465
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1466
|
+
{ name: "status", in: "query", required: false, kind: "string", enum: ["requested", "received", "accepted", "rejected", "expired"] }
|
|
1467
|
+
]
|
|
1468
|
+
},
|
|
1469
|
+
list_vendors: {
|
|
1470
|
+
method: "GET",
|
|
1471
|
+
path: "/vendors",
|
|
1472
|
+
tag: "Vendors",
|
|
1473
|
+
summary: "List vendors in an organization",
|
|
1474
|
+
body: false,
|
|
1475
|
+
response: "json",
|
|
1476
|
+
params: [
|
|
1477
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1478
|
+
{ name: "type", in: "query", required: false, kind: "string", enum: ["supplier", "subcontractor", "rental"] },
|
|
1479
|
+
{ name: "kind", in: "query", required: false, kind: "string", enum: ["sub", "supplier", "surety", "insurer", "agency", "owner", "architect", "engineer", "other"] },
|
|
1480
|
+
{ name: "preferredOnly", in: "query", required: false, kind: "boolean" },
|
|
1481
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1482
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1483
|
+
]
|
|
1484
|
+
},
|
|
1485
|
+
list_webhook_deliveries: {
|
|
1486
|
+
method: "GET",
|
|
1487
|
+
path: "/webhook_endpoints/{id}/deliveries",
|
|
1488
|
+
tag: "Webhooks",
|
|
1489
|
+
summary: "List deliveries for a webhook endpoint",
|
|
1490
|
+
body: false,
|
|
1491
|
+
response: "json",
|
|
1492
|
+
params: [
|
|
1493
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1494
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1495
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1496
|
+
]
|
|
1497
|
+
},
|
|
1498
|
+
list_webhook_endpoints: {
|
|
1499
|
+
method: "GET",
|
|
1500
|
+
path: "/webhook_endpoints",
|
|
1501
|
+
tag: "Webhooks",
|
|
1502
|
+
summary: "List webhook endpoints in an organization",
|
|
1503
|
+
body: false,
|
|
1504
|
+
response: "json",
|
|
1505
|
+
params: [
|
|
1506
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1507
|
+
]
|
|
1508
|
+
},
|
|
1509
|
+
list_widget_conversations: {
|
|
1510
|
+
method: "GET",
|
|
1511
|
+
path: "/widgets/{id}/conversations",
|
|
1512
|
+
tag: "Conversations",
|
|
1513
|
+
summary: "List a widget\u2019s inbox conversations",
|
|
1514
|
+
body: false,
|
|
1515
|
+
response: "json",
|
|
1516
|
+
params: [
|
|
1517
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Widget id: the id the API returns for a widget (a string of 32 lowercase letters and digits)." },
|
|
1518
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1519
|
+
{ name: "status", in: "query", required: false, kind: "string", enum: ["bot", "open", "snoozed", "closed"] },
|
|
1520
|
+
{ name: "search", in: "query", required: false, kind: "string" },
|
|
1521
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1522
|
+
]
|
|
1523
|
+
},
|
|
1524
|
+
list_widgets: {
|
|
1525
|
+
method: "GET",
|
|
1526
|
+
path: "/widgets",
|
|
1527
|
+
tag: "Widgets",
|
|
1528
|
+
summary: "List widgets in an organization",
|
|
1529
|
+
body: false,
|
|
1530
|
+
response: "json",
|
|
1531
|
+
params: [
|
|
1532
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1533
|
+
]
|
|
1534
|
+
},
|
|
1535
|
+
mark_all_notifications_read: {
|
|
1536
|
+
method: "POST",
|
|
1537
|
+
path: "/notifications/mark-all-read",
|
|
1538
|
+
tag: "Notifications",
|
|
1539
|
+
summary: "Mark all notifications as read",
|
|
1540
|
+
body: false,
|
|
1541
|
+
response: "empty",
|
|
1542
|
+
params: []
|
|
1543
|
+
},
|
|
1544
|
+
mark_notification_read: {
|
|
1545
|
+
method: "POST",
|
|
1546
|
+
path: "/notifications/{id}/read",
|
|
1547
|
+
tag: "Notifications",
|
|
1548
|
+
summary: "Mark a notification as read",
|
|
1549
|
+
body: false,
|
|
1550
|
+
response: "empty",
|
|
1551
|
+
params: [
|
|
1552
|
+
{ name: "id", in: "path", required: true, kind: "string" }
|
|
1553
|
+
]
|
|
1554
|
+
},
|
|
1555
|
+
patch_symbol_line_item: {
|
|
1556
|
+
method: "PATCH",
|
|
1557
|
+
path: "/symbols/{symbolId}/line-items/{id}",
|
|
1558
|
+
tag: "Takeoff",
|
|
1559
|
+
summary: "Update a line item on a symbol",
|
|
1560
|
+
body: true,
|
|
1561
|
+
response: "empty",
|
|
1562
|
+
params: [
|
|
1563
|
+
{ name: "symbolId", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
1564
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1565
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1566
|
+
{ name: "patch", in: "body", required: true, kind: "object", description: "Only the fields to change" }
|
|
1567
|
+
]
|
|
1568
|
+
},
|
|
1569
|
+
presign_file_upload: {
|
|
1570
|
+
method: "POST",
|
|
1571
|
+
path: "/files",
|
|
1572
|
+
tag: "Files",
|
|
1573
|
+
summary: "Start a file upload \u2014 returns a presigned R2 URL",
|
|
1574
|
+
body: true,
|
|
1575
|
+
response: "json",
|
|
1576
|
+
params: [
|
|
1577
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1578
|
+
{ name: "filename", in: "body", required: true, kind: "string" },
|
|
1579
|
+
{ name: "mimeType", in: "body", required: true, kind: "string" },
|
|
1580
|
+
{ name: "fileSize", in: "body", required: true, kind: "integer", description: "File size in bytes (max 500 MB)" },
|
|
1581
|
+
{ name: "folderId", in: "body", required: false, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." },
|
|
1582
|
+
{ name: "dimensions", in: "body", required: false, kind: "object" }
|
|
1583
|
+
]
|
|
1584
|
+
},
|
|
1585
|
+
propose_analysis: {
|
|
1586
|
+
method: "POST",
|
|
1587
|
+
path: "/projects/{id}/analysis/propose",
|
|
1588
|
+
tag: "Takeoff",
|
|
1589
|
+
summary: "Propose an AI-generated analysis from a natural-language prompt",
|
|
1590
|
+
body: true,
|
|
1591
|
+
response: "json",
|
|
1592
|
+
params: [
|
|
1593
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1594
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1595
|
+
{ name: "prompt", in: "body", required: true, kind: "string", description: "Natural-language description of what to measure" },
|
|
1596
|
+
{ name: "pageIds", in: "body", required: false, kind: "array", description: "Page IDs to propose against. Defaults to the project's first page." },
|
|
1597
|
+
{ name: "fileIds", in: "body", required: false, kind: "array", description: "File IDs to scope the proposal to. When set, only pages belonging to these files are considered \u2014 pass the IDs from the [Attached files: ...] note in the user's message so a fresh upload doesn't pull in older project files. Wins over pageIds." }
|
|
1598
|
+
]
|
|
1599
|
+
},
|
|
1600
|
+
provision_cma: {
|
|
1601
|
+
method: "POST",
|
|
1602
|
+
path: "/cma/provision",
|
|
1603
|
+
tag: "Account",
|
|
1604
|
+
summary: "Provision or rotate the caller\u2019s Managed Agents vault",
|
|
1605
|
+
body: false,
|
|
1606
|
+
response: "json",
|
|
1607
|
+
params: []
|
|
1608
|
+
},
|
|
1609
|
+
receive_vendor_quote: {
|
|
1610
|
+
method: "PATCH",
|
|
1611
|
+
path: "/quotes/{id}/receive",
|
|
1612
|
+
tag: "Vendor quotes",
|
|
1613
|
+
summary: "Record a vendor quote response",
|
|
1614
|
+
body: true,
|
|
1615
|
+
response: "json",
|
|
1616
|
+
params: [
|
|
1617
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1618
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1619
|
+
{ name: "lineItems", in: "body", required: true, kind: "array" },
|
|
1620
|
+
{ name: "subtotal", in: "body", required: true, kind: "number", description: "Sum of the line items' totalPrice when lineItems are sent" },
|
|
1621
|
+
{ name: "tax", in: "body", required: false, kind: "number" },
|
|
1622
|
+
{ name: "shipping", in: "body", required: false, kind: "number" },
|
|
1623
|
+
{ name: "total", in: "body", required: true, kind: "number", description: "Must equal subtotal + tax + shipping (an absent amount counts as 0)" },
|
|
1624
|
+
{ name: "validUntil", in: "body", required: false, kind: "number" }
|
|
1625
|
+
]
|
|
1626
|
+
},
|
|
1627
|
+
remove_custom_property: {
|
|
1628
|
+
method: "DELETE",
|
|
1629
|
+
path: "/custom-properties/{id}",
|
|
1630
|
+
tag: "Custom properties",
|
|
1631
|
+
summary: "Archive a custom property",
|
|
1632
|
+
body: false,
|
|
1633
|
+
response: "empty",
|
|
1634
|
+
params: [
|
|
1635
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Custom property id: the id the API returns for a custom property (a string of 32 lowercase letters and digits)." },
|
|
1636
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1637
|
+
]
|
|
1638
|
+
},
|
|
1639
|
+
remove_file: {
|
|
1640
|
+
method: "DELETE",
|
|
1641
|
+
path: "/files/{id}",
|
|
1642
|
+
tag: "Files",
|
|
1643
|
+
summary: "Soft-delete a file",
|
|
1644
|
+
body: false,
|
|
1645
|
+
response: "empty",
|
|
1646
|
+
params: [
|
|
1647
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "File id: the id the API returns for a file (a string of 32 lowercase letters and digits)." }
|
|
1648
|
+
]
|
|
1649
|
+
},
|
|
1650
|
+
remove_folder: {
|
|
1651
|
+
method: "DELETE",
|
|
1652
|
+
path: "/folders/{id}",
|
|
1653
|
+
tag: "Folders",
|
|
1654
|
+
summary: "Delete a folder",
|
|
1655
|
+
body: false,
|
|
1656
|
+
response: "empty",
|
|
1657
|
+
params: [
|
|
1658
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." }
|
|
1659
|
+
]
|
|
1660
|
+
},
|
|
1661
|
+
remove_group: {
|
|
1662
|
+
method: "DELETE",
|
|
1663
|
+
path: "/groups/{id}",
|
|
1664
|
+
tag: "Takeoff",
|
|
1665
|
+
summary: "Delete a takeoff group",
|
|
1666
|
+
body: false,
|
|
1667
|
+
response: "empty",
|
|
1668
|
+
params: [
|
|
1669
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff group id: the id the API returns for a takeoff group (a string of 32 lowercase letters and digits)." },
|
|
1670
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1671
|
+
]
|
|
1672
|
+
},
|
|
1673
|
+
remove_layer: {
|
|
1674
|
+
method: "DELETE",
|
|
1675
|
+
path: "/layers/{id}",
|
|
1676
|
+
tag: "Takeoff",
|
|
1677
|
+
summary: "Delete a takeoff layer",
|
|
1678
|
+
body: false,
|
|
1679
|
+
response: "empty",
|
|
1680
|
+
params: [
|
|
1681
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff layer id: the id the API returns for a takeoff layer (a string of 32 lowercase letters and digits)." },
|
|
1682
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1683
|
+
]
|
|
1684
|
+
},
|
|
1685
|
+
remove_measurement: {
|
|
1686
|
+
method: "DELETE",
|
|
1687
|
+
path: "/pages/{pageId}/measurements/{shapeId}",
|
|
1688
|
+
tag: "Takeoff",
|
|
1689
|
+
summary: "Delete a measurement by shape ID",
|
|
1690
|
+
body: false,
|
|
1691
|
+
response: "empty",
|
|
1692
|
+
params: [
|
|
1693
|
+
{ name: "pageId", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." },
|
|
1694
|
+
{ name: "shapeId", in: "path", required: true, kind: "string" }
|
|
1695
|
+
]
|
|
1696
|
+
},
|
|
1697
|
+
remove_message: {
|
|
1698
|
+
method: "DELETE",
|
|
1699
|
+
path: "/messages/{id}",
|
|
1700
|
+
tag: "Messages",
|
|
1701
|
+
summary: "Delete a message",
|
|
1702
|
+
body: false,
|
|
1703
|
+
response: "empty",
|
|
1704
|
+
params: [
|
|
1705
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Chat message id: the id the API returns for a chat message (a string of 32 lowercase letters and digits)." }
|
|
1706
|
+
]
|
|
1707
|
+
},
|
|
1708
|
+
remove_symbol: {
|
|
1709
|
+
method: "DELETE",
|
|
1710
|
+
path: "/symbols/{id}",
|
|
1711
|
+
tag: "Takeoff",
|
|
1712
|
+
summary: "Delete a takeoff symbol",
|
|
1713
|
+
body: false,
|
|
1714
|
+
response: "empty",
|
|
1715
|
+
params: [
|
|
1716
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
1717
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1718
|
+
]
|
|
1719
|
+
},
|
|
1720
|
+
remove_symbol_line_item: {
|
|
1721
|
+
method: "DELETE",
|
|
1722
|
+
path: "/symbols/{symbolId}/line-items/{id}",
|
|
1723
|
+
tag: "Takeoff",
|
|
1724
|
+
summary: "Remove a line item from a symbol",
|
|
1725
|
+
body: false,
|
|
1726
|
+
response: "empty",
|
|
1727
|
+
params: [
|
|
1728
|
+
{ name: "symbolId", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
1729
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1730
|
+
{ name: "projectId", in: "query", required: true, kind: "string" }
|
|
1731
|
+
]
|
|
1732
|
+
},
|
|
1733
|
+
remove_vendor_price: {
|
|
1734
|
+
method: "DELETE",
|
|
1735
|
+
path: "/products/{productId}/prices/{vendorId}",
|
|
1736
|
+
tag: "Vendor prices",
|
|
1737
|
+
summary: "Remove a vendor price",
|
|
1738
|
+
body: false,
|
|
1739
|
+
response: "empty",
|
|
1740
|
+
params: [
|
|
1741
|
+
{ name: "productId", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
1742
|
+
{ name: "vendorId", in: "path", required: true, kind: "string", description: "Vendor id: the id the API returns for a vendor (a string of 32 lowercase letters and digits)." },
|
|
1743
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1744
|
+
]
|
|
1745
|
+
},
|
|
1746
|
+
remove_widget: {
|
|
1747
|
+
method: "DELETE",
|
|
1748
|
+
path: "/widgets/{id}",
|
|
1749
|
+
tag: "Widgets",
|
|
1750
|
+
summary: "Delete a widget",
|
|
1751
|
+
body: false,
|
|
1752
|
+
response: "empty",
|
|
1753
|
+
params: [
|
|
1754
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Widget id: the id the API returns for a widget (a string of 32 lowercase letters and digits)." },
|
|
1755
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1756
|
+
]
|
|
1757
|
+
},
|
|
1758
|
+
rename_file: {
|
|
1759
|
+
method: "PATCH",
|
|
1760
|
+
path: "/files/{id}",
|
|
1761
|
+
tag: "Files",
|
|
1762
|
+
summary: "Rename or move a file",
|
|
1763
|
+
body: true,
|
|
1764
|
+
response: "empty",
|
|
1765
|
+
params: [
|
|
1766
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "File id: the id the API returns for a file (a string of 32 lowercase letters and digits)." },
|
|
1767
|
+
{ name: "filename", in: "body", required: false, kind: "string" },
|
|
1768
|
+
{ name: "folderId", in: "body", required: false, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." }
|
|
1769
|
+
]
|
|
1770
|
+
},
|
|
1771
|
+
rename_folder: {
|
|
1772
|
+
method: "PATCH",
|
|
1773
|
+
path: "/folders/{id}",
|
|
1774
|
+
tag: "Folders",
|
|
1775
|
+
summary: "Rename or move a folder",
|
|
1776
|
+
body: true,
|
|
1777
|
+
response: "empty",
|
|
1778
|
+
params: [
|
|
1779
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." },
|
|
1780
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
1781
|
+
{ name: "parentId", in: "body", required: false, kind: "string", description: "Folder id: the id the API returns for a folder (a string of 32 lowercase letters and digits)." }
|
|
1782
|
+
]
|
|
1783
|
+
},
|
|
1784
|
+
resend_webhook_delivery: {
|
|
1785
|
+
method: "POST",
|
|
1786
|
+
path: "/webhook_deliveries/{id}/resend",
|
|
1787
|
+
tag: "Webhooks",
|
|
1788
|
+
summary: "Resend a webhook delivery",
|
|
1789
|
+
body: true,
|
|
1790
|
+
response: "json",
|
|
1791
|
+
params: [
|
|
1792
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1793
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1794
|
+
]
|
|
1795
|
+
},
|
|
1796
|
+
restore_list: {
|
|
1797
|
+
method: "POST",
|
|
1798
|
+
path: "/lists/{id}/restore",
|
|
1799
|
+
tag: "Lists",
|
|
1800
|
+
summary: "Restore an archived list",
|
|
1801
|
+
body: true,
|
|
1802
|
+
response: "empty",
|
|
1803
|
+
params: [
|
|
1804
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
1805
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1806
|
+
]
|
|
1807
|
+
},
|
|
1808
|
+
restore_page: {
|
|
1809
|
+
method: "POST",
|
|
1810
|
+
path: "/pages/{id}/restore",
|
|
1811
|
+
tag: "Pages",
|
|
1812
|
+
summary: "Restore an archived page",
|
|
1813
|
+
body: false,
|
|
1814
|
+
response: "empty",
|
|
1815
|
+
params: [
|
|
1816
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." }
|
|
1817
|
+
]
|
|
1818
|
+
},
|
|
1819
|
+
restore_project: {
|
|
1820
|
+
method: "POST",
|
|
1821
|
+
path: "/projects/{id}/restore",
|
|
1822
|
+
tag: "Projects",
|
|
1823
|
+
summary: "Restore an archived project",
|
|
1824
|
+
body: true,
|
|
1825
|
+
response: "empty",
|
|
1826
|
+
params: [
|
|
1827
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1828
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
1829
|
+
]
|
|
1830
|
+
},
|
|
1831
|
+
run_approved_analysis: {
|
|
1832
|
+
method: "POST",
|
|
1833
|
+
path: "/projects/{id}/analysis/run",
|
|
1834
|
+
tag: "Takeoff",
|
|
1835
|
+
summary: "Run an approved analysis (kicks off a background AI task)",
|
|
1836
|
+
confirm: "This operation uses AI usage.",
|
|
1837
|
+
body: true,
|
|
1838
|
+
response: "json",
|
|
1839
|
+
params: [
|
|
1840
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1841
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1842
|
+
{ name: "pageIds", in: "body", required: true, kind: "array" },
|
|
1843
|
+
{ name: "elements", in: "body", required: true, kind: "array" },
|
|
1844
|
+
{ name: "layerName", in: "body", required: false, kind: "string" }
|
|
1845
|
+
]
|
|
1846
|
+
},
|
|
1847
|
+
search_help_articles: {
|
|
1848
|
+
method: "POST",
|
|
1849
|
+
path: "/help/search",
|
|
1850
|
+
tag: "Help",
|
|
1851
|
+
summary: "Search help articles by keyword",
|
|
1852
|
+
body: true,
|
|
1853
|
+
response: "json",
|
|
1854
|
+
params: [
|
|
1855
|
+
{ name: "query", in: "body", required: true, kind: "string" },
|
|
1856
|
+
{ name: "limit", in: "body", required: false, kind: "integer" },
|
|
1857
|
+
{ name: "section", in: "body", required: false, kind: "string" }
|
|
1858
|
+
]
|
|
1859
|
+
},
|
|
1860
|
+
search_products: {
|
|
1861
|
+
method: "GET",
|
|
1862
|
+
path: "/products/search",
|
|
1863
|
+
tag: "Products",
|
|
1864
|
+
summary: "Full-text search products",
|
|
1865
|
+
body: false,
|
|
1866
|
+
response: "json",
|
|
1867
|
+
params: [
|
|
1868
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1869
|
+
{ name: "query", in: "query", required: true, kind: "string" },
|
|
1870
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1871
|
+
]
|
|
1872
|
+
},
|
|
1873
|
+
search_project_files: {
|
|
1874
|
+
method: "GET",
|
|
1875
|
+
path: "/projects/{id}/files/search",
|
|
1876
|
+
tag: "Files",
|
|
1877
|
+
summary: "Search pages in a project by name or indexed content",
|
|
1878
|
+
body: false,
|
|
1879
|
+
response: "json",
|
|
1880
|
+
params: [
|
|
1881
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1882
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1883
|
+
{ name: "query", in: "query", required: false, kind: "string" }
|
|
1884
|
+
]
|
|
1885
|
+
},
|
|
1886
|
+
search_project_line_items: {
|
|
1887
|
+
method: "GET",
|
|
1888
|
+
path: "/projects/{id}/line-items",
|
|
1889
|
+
tag: "Takeoff",
|
|
1890
|
+
summary: "Search line items across all symbols in a project",
|
|
1891
|
+
body: false,
|
|
1892
|
+
response: "json",
|
|
1893
|
+
params: [
|
|
1894
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
1895
|
+
{ name: "query", in: "query", required: false, kind: "string" },
|
|
1896
|
+
{ name: "takeoffType", in: "query", required: false, kind: "string", enum: ["count", "linear", "area"] },
|
|
1897
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1898
|
+
]
|
|
1899
|
+
},
|
|
1900
|
+
search_vendors: {
|
|
1901
|
+
method: "GET",
|
|
1902
|
+
path: "/vendors/search",
|
|
1903
|
+
tag: "Vendors",
|
|
1904
|
+
summary: "Full-text search vendors",
|
|
1905
|
+
body: false,
|
|
1906
|
+
response: "json",
|
|
1907
|
+
params: [
|
|
1908
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1909
|
+
{ name: "query", in: "query", required: true, kind: "string" },
|
|
1910
|
+
{ name: "limit", in: "query", required: false, kind: "integer" }
|
|
1911
|
+
]
|
|
1912
|
+
},
|
|
1913
|
+
send_cma_message: {
|
|
1914
|
+
method: "POST",
|
|
1915
|
+
path: "/cma/chat",
|
|
1916
|
+
tag: "Account",
|
|
1917
|
+
summary: "Send a message to the Managed Agent and return its reply",
|
|
1918
|
+
body: true,
|
|
1919
|
+
response: "json",
|
|
1920
|
+
params: [
|
|
1921
|
+
{ name: "message", in: "body", required: true, kind: "string" },
|
|
1922
|
+
{ name: "sessionId", in: "body", required: false, kind: "string" },
|
|
1923
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "The organization this message is for and is billed to (memberships[].orgId in GET /v1/me). You must be a member of it; on the session of one of your Exayard conversations it must be that conversation's organization. Required unless sessionId is the session of one of your Exayard conversations (else 400). An organization without enough credits is answered 402." }
|
|
1924
|
+
]
|
|
1925
|
+
},
|
|
1926
|
+
send_webhook_listener_test: {
|
|
1927
|
+
method: "POST",
|
|
1928
|
+
path: "/webhook_listeners/{id}/test",
|
|
1929
|
+
tag: "Webhooks",
|
|
1930
|
+
summary: "Send a test event to a webhook listener",
|
|
1931
|
+
body: true,
|
|
1932
|
+
response: "json",
|
|
1933
|
+
params: [
|
|
1934
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1935
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1936
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["project.created", "project.updated", "project.archived", "assessment.started", "assessment.completed", "assessment.cancelled", "assessment.failed", "estimate.generated", "bid.generated", "bid.submitted", "bid.won", "bid.lost", "bid.accepted", "bid.declined", "bid.sent", "bid.viewed", "bid.commented", "file.processed", "addendum.uploaded", "quote.requested", "quote.received", "quote.accepted", "quote.rejected", "quote.expired", "conversation.started", "conversation.handoff_requested", "conversation.closed", "lead.captured", "contact.created", "appointment.booked", "appointment.rescheduled", "appointment.cancelled", "call.completed", "call.missed", "review.requested", "job.written_back", "payment.captured", "payment.failed"] }
|
|
1937
|
+
]
|
|
1938
|
+
},
|
|
1939
|
+
send_webhook_test_event: {
|
|
1940
|
+
method: "POST",
|
|
1941
|
+
path: "/webhook_endpoints/{id}/test",
|
|
1942
|
+
tag: "Webhooks",
|
|
1943
|
+
summary: "Send a test event to a webhook endpoint",
|
|
1944
|
+
body: true,
|
|
1945
|
+
response: "json",
|
|
1946
|
+
params: [
|
|
1947
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
1948
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1949
|
+
{ name: "type", in: "body", required: true, kind: "string", enum: ["project.created", "project.updated", "project.archived", "assessment.started", "assessment.completed", "assessment.cancelled", "assessment.failed", "estimate.generated", "bid.generated", "bid.submitted", "bid.won", "bid.lost", "bid.accepted", "bid.declined", "bid.sent", "bid.viewed", "bid.commented", "file.processed", "addendum.uploaded", "quote.requested", "quote.received", "quote.accepted", "quote.rejected", "quote.expired", "conversation.started", "conversation.handoff_requested", "conversation.closed", "lead.captured", "contact.created", "appointment.booked", "appointment.rescheduled", "appointment.cancelled", "call.completed", "call.missed", "review.requested", "job.written_back", "payment.captured", "payment.failed"] }
|
|
1950
|
+
]
|
|
1951
|
+
},
|
|
1952
|
+
set_document_sharing: {
|
|
1953
|
+
method: "PATCH",
|
|
1954
|
+
path: "/documents/{id}/share",
|
|
1955
|
+
tag: "Documents",
|
|
1956
|
+
summary: "Turn document sharing on or off",
|
|
1957
|
+
body: true,
|
|
1958
|
+
response: "json",
|
|
1959
|
+
params: [
|
|
1960
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Document id: the id the API returns for a document (a string of 32 lowercase letters and digits)." },
|
|
1961
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1962
|
+
{ name: "enabled", in: "body", required: true, kind: "boolean", description: "true mints (or reuses) the public share link and turns sharing on; false revokes access while keeping the secret for later re-enabling." }
|
|
1963
|
+
]
|
|
1964
|
+
},
|
|
1965
|
+
set_vendor_price: {
|
|
1966
|
+
method: "PUT",
|
|
1967
|
+
path: "/products/{productId}/prices/{vendorId}",
|
|
1968
|
+
tag: "Vendor prices",
|
|
1969
|
+
summary: "Create or update a vendor price",
|
|
1970
|
+
body: true,
|
|
1971
|
+
response: "json",
|
|
1972
|
+
params: [
|
|
1973
|
+
{ name: "productId", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
1974
|
+
{ name: "vendorId", in: "path", required: true, kind: "string", description: "Vendor id: the id the API returns for a vendor (a string of 32 lowercase letters and digits)." },
|
|
1975
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1976
|
+
{ name: "pricePerUnit", in: "body", required: true, kind: "number" },
|
|
1977
|
+
{ name: "minOrderQuantity", in: "body", required: false, kind: "number" },
|
|
1978
|
+
{ name: "volumeDiscounts", in: "body", required: false, kind: "array" },
|
|
1979
|
+
{ name: "leadTimeDays", in: "body", required: false, kind: "integer" },
|
|
1980
|
+
{ name: "expirationDate", in: "body", required: false, kind: "number" },
|
|
1981
|
+
{ name: "notes", in: "body", required: false, kind: "string" }
|
|
1982
|
+
]
|
|
1983
|
+
},
|
|
1984
|
+
start_webhook_listener: {
|
|
1985
|
+
method: "POST",
|
|
1986
|
+
path: "/webhook_listeners",
|
|
1987
|
+
tag: "Webhooks",
|
|
1988
|
+
summary: "Start a webhook listener (exayard listen)",
|
|
1989
|
+
body: true,
|
|
1990
|
+
response: "json",
|
|
1991
|
+
params: [
|
|
1992
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
1993
|
+
{ name: "events", in: "body", required: false, kind: "array" },
|
|
1994
|
+
{ name: "name", in: "body", required: false, kind: "string" }
|
|
1995
|
+
]
|
|
1996
|
+
},
|
|
1997
|
+
stop_webhook_listener: {
|
|
1998
|
+
method: "DELETE",
|
|
1999
|
+
path: "/webhook_listeners/{id}",
|
|
2000
|
+
tag: "Webhooks",
|
|
2001
|
+
summary: "Stop a webhook listener",
|
|
2002
|
+
body: false,
|
|
2003
|
+
response: "empty",
|
|
2004
|
+
params: [
|
|
2005
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2006
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." }
|
|
2007
|
+
]
|
|
2008
|
+
},
|
|
2009
|
+
stream_webhook_listener_events: {
|
|
2010
|
+
method: "GET",
|
|
2011
|
+
path: "/webhook_listeners/{id}/events",
|
|
2012
|
+
tag: "Webhooks",
|
|
2013
|
+
summary: "Stream a webhook listener's events",
|
|
2014
|
+
body: false,
|
|
2015
|
+
response: "stream",
|
|
2016
|
+
params: [
|
|
2017
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2018
|
+
{ name: "organizationId", in: "query", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2019
|
+
{ name: "after", in: "query", required: false, kind: "integer" }
|
|
2020
|
+
]
|
|
2021
|
+
},
|
|
2022
|
+
update_contact: {
|
|
2023
|
+
method: "PATCH",
|
|
2024
|
+
path: "/contacts/{id}",
|
|
2025
|
+
tag: "Contacts",
|
|
2026
|
+
summary: "Update a contact\u2019s details or CRM attributes",
|
|
2027
|
+
body: true,
|
|
2028
|
+
response: "empty",
|
|
2029
|
+
params: [
|
|
2030
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Contact id: the id the API returns for a contact (a string of 32 lowercase letters and digits)." },
|
|
2031
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2032
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2033
|
+
{ name: "email", in: "body", required: false, kind: "string" },
|
|
2034
|
+
{ name: "phone", in: "body", required: false, kind: "string" },
|
|
2035
|
+
{ name: "attributes", in: "body", required: false, kind: "object" }
|
|
2036
|
+
]
|
|
2037
|
+
},
|
|
2038
|
+
update_conversation_title: {
|
|
2039
|
+
method: "PATCH",
|
|
2040
|
+
path: "/conversations/{id}",
|
|
2041
|
+
tag: "Conversations",
|
|
2042
|
+
summary: "Update conversation title or linked project",
|
|
2043
|
+
body: true,
|
|
2044
|
+
response: "empty",
|
|
2045
|
+
params: [
|
|
2046
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Conversation id: the id the API returns for a conversation (a string of 32 lowercase letters and digits)." },
|
|
2047
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2048
|
+
{ name: "title", in: "body", required: false, kind: "string" },
|
|
2049
|
+
{ name: "projectId", in: "body", required: false, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." }
|
|
2050
|
+
]
|
|
2051
|
+
},
|
|
2052
|
+
update_custom_property: {
|
|
2053
|
+
method: "PATCH",
|
|
2054
|
+
path: "/custom-properties/{id}",
|
|
2055
|
+
tag: "Custom properties",
|
|
2056
|
+
summary: "Update a custom property",
|
|
2057
|
+
body: true,
|
|
2058
|
+
response: "empty",
|
|
2059
|
+
params: [
|
|
2060
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Custom property id: the id the API returns for a custom property (a string of 32 lowercase letters and digits)." },
|
|
2061
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2062
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2063
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["text", "textarea", "number", "date", "checkbox", "multiCheckbox", "select", "url", "email", "phone", "currency", "percentage"] },
|
|
2064
|
+
{ name: "recordTypes", in: "body", required: false, kind: "array" },
|
|
2065
|
+
{ name: "options", in: "body", required: false, kind: "array" }
|
|
2066
|
+
]
|
|
2067
|
+
},
|
|
2068
|
+
update_group: {
|
|
2069
|
+
method: "PATCH",
|
|
2070
|
+
path: "/groups/{id}",
|
|
2071
|
+
tag: "Takeoff",
|
|
2072
|
+
summary: "Update a takeoff group",
|
|
2073
|
+
body: true,
|
|
2074
|
+
response: "empty",
|
|
2075
|
+
params: [
|
|
2076
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff group id: the id the API returns for a takeoff group (a string of 32 lowercase letters and digits)." },
|
|
2077
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
2078
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2079
|
+
{ name: "position", in: "body", required: false, kind: "number" },
|
|
2080
|
+
{ name: "parentId", in: "body", required: false, kind: "string", description: "Takeoff group id: the id the API returns for a takeoff group (a string of 32 lowercase letters and digits)." }
|
|
2081
|
+
]
|
|
2082
|
+
},
|
|
2083
|
+
update_labor_crew: {
|
|
2084
|
+
method: "PATCH",
|
|
2085
|
+
path: "/labor/crews/{id}",
|
|
2086
|
+
tag: "Labor",
|
|
2087
|
+
summary: "Update a labor crew",
|
|
2088
|
+
body: true,
|
|
2089
|
+
response: "empty",
|
|
2090
|
+
params: [
|
|
2091
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2092
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2093
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2094
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2095
|
+
{ name: "members", in: "body", required: false, kind: "array" },
|
|
2096
|
+
{ name: "equipmentIds", in: "body", required: false, kind: "array" },
|
|
2097
|
+
{ name: "dailyCapacity", in: "body", required: false, kind: "object" }
|
|
2098
|
+
]
|
|
2099
|
+
},
|
|
2100
|
+
update_labor_equipment: {
|
|
2101
|
+
method: "PATCH",
|
|
2102
|
+
path: "/labor/equipment/{id}",
|
|
2103
|
+
tag: "Labor",
|
|
2104
|
+
summary: "Update a labor equipment entry",
|
|
2105
|
+
body: true,
|
|
2106
|
+
response: "empty",
|
|
2107
|
+
params: [
|
|
2108
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2109
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2110
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2111
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2112
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
2113
|
+
{ name: "dailyRate", in: "body", required: false, kind: "number" },
|
|
2114
|
+
{ name: "hourlyRate", in: "body", required: false, kind: "number" },
|
|
2115
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
2116
|
+
{ name: "isOwned", in: "body", required: false, kind: "boolean" }
|
|
2117
|
+
]
|
|
2118
|
+
},
|
|
2119
|
+
update_labor_rate: {
|
|
2120
|
+
method: "PATCH",
|
|
2121
|
+
path: "/labor/rates/{id}",
|
|
2122
|
+
tag: "Labor",
|
|
2123
|
+
summary: "Update a labor rate",
|
|
2124
|
+
body: true,
|
|
2125
|
+
response: "empty",
|
|
2126
|
+
params: [
|
|
2127
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2128
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2129
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2130
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2131
|
+
{ name: "hourlyRate", in: "body", required: false, kind: "number" },
|
|
2132
|
+
{ name: "burdenRate", in: "body", required: false, kind: "number" },
|
|
2133
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
2134
|
+
{ name: "effectiveDate", in: "body", required: false, kind: "number" },
|
|
2135
|
+
{ name: "isDefault", in: "body", required: false, kind: "boolean" }
|
|
2136
|
+
]
|
|
2137
|
+
},
|
|
2138
|
+
update_layer: {
|
|
2139
|
+
method: "PATCH",
|
|
2140
|
+
path: "/layers/{id}",
|
|
2141
|
+
tag: "Takeoff",
|
|
2142
|
+
summary: "Rename a takeoff layer",
|
|
2143
|
+
body: true,
|
|
2144
|
+
response: "empty",
|
|
2145
|
+
params: [
|
|
2146
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff layer id: the id the API returns for a takeoff layer (a string of 32 lowercase letters and digits)." },
|
|
2147
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
2148
|
+
{ name: "name", in: "body", required: true, kind: "string" }
|
|
2149
|
+
]
|
|
2150
|
+
},
|
|
2151
|
+
update_list: {
|
|
2152
|
+
method: "PATCH",
|
|
2153
|
+
path: "/lists/{id}",
|
|
2154
|
+
tag: "Lists",
|
|
2155
|
+
summary: "Update list name or metadata",
|
|
2156
|
+
body: true,
|
|
2157
|
+
response: "json",
|
|
2158
|
+
params: [
|
|
2159
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product list id: the id the API returns for a product list (a string of 32 lowercase letters and digits)." },
|
|
2160
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2161
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2162
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2163
|
+
{ name: "privacy", in: "body", required: false, kind: "string", enum: ["private", "organization", "public", "system"] },
|
|
2164
|
+
{ name: "category", in: "body", required: false, kind: "string" }
|
|
2165
|
+
]
|
|
2166
|
+
},
|
|
2167
|
+
update_measurement: {
|
|
2168
|
+
method: "PATCH",
|
|
2169
|
+
path: "/measurements/{id}",
|
|
2170
|
+
tag: "Takeoff",
|
|
2171
|
+
summary: "Update a measurement",
|
|
2172
|
+
body: true,
|
|
2173
|
+
response: "empty",
|
|
2174
|
+
params: [
|
|
2175
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff measurement id: the id the API returns for a takeoff measurement (a string of 32 lowercase letters and digits)." },
|
|
2176
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
2177
|
+
{ name: "value", in: "body", required: false, kind: "number", description: "The measured quantity in RAW PAGE PIXELS \u2014 the same coordinate space as the geometry points, NEVER feet, metres, or any other real-world unit. Linear measurements are a length in px, areas are px\xB2, counts are a tally. Exayard converts to real units at display time using the page scale, so a real-world number sent here is converted a second time and reads wrong. When geometry is supplied the server measures it and rejects a value that disagrees by 10\xD7 or more, naming both numbers. To send a real-world quantity, convert it to page pixels first (multiply by the page's pixels-per-unit; square that factor for an area)." },
|
|
2178
|
+
{ name: "manualValue", in: "body", required: false, kind: "number", description: "A user's override of the measured quantity, in RAW PAGE PIXELS \u2014 the same coordinate space as the geometry points, NEVER feet, metres, or any other real-world unit. Linear measurements are a length in px, areas are px\xB2, counts are a tally. Exayard converts to real units at display time using the page scale, so a real-world number sent here is converted a second time and reads wrong. When geometry is supplied the server measures it and rejects a value that disagrees by 10\xD7 or more, naming both numbers. To send a real-world quantity, convert it to page pixels first (multiply by the page's pixels-per-unit; square that factor for an area). Send null to clear the override and fall back to the measured value." },
|
|
2179
|
+
{ name: "geometry", in: "body", required: false, kind: "object", description: 'Measurement geometry: { "points": [{ "x": number, "y": number, "bulge"?: number }, ...], "isClosed": boolean, "holes"?: Point[][], "pathD"?: string }. When geometry is provided, "points" and "isClosed" are required; other keys (e.g. { "length": 280.9 }) are rejected.' },
|
|
2180
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
2181
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2182
|
+
{ name: "layerId", in: "body", required: false, kind: "string", description: "Takeoff layer id: the id the API returns for a takeoff layer (a string of 32 lowercase letters and digits)." }
|
|
2183
|
+
]
|
|
2184
|
+
},
|
|
2185
|
+
update_message: {
|
|
2186
|
+
method: "PATCH",
|
|
2187
|
+
path: "/messages/{id}",
|
|
2188
|
+
tag: "Messages",
|
|
2189
|
+
summary: "Update a message",
|
|
2190
|
+
body: true,
|
|
2191
|
+
response: "empty",
|
|
2192
|
+
params: [
|
|
2193
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Chat message id: the id the API returns for a chat message (a string of 32 lowercase letters and digits)." },
|
|
2194
|
+
{ name: "content", in: "body", required: true, kind: "string" }
|
|
2195
|
+
]
|
|
2196
|
+
},
|
|
2197
|
+
update_page: {
|
|
2198
|
+
method: "PATCH",
|
|
2199
|
+
path: "/pages/{id}",
|
|
2200
|
+
tag: "Pages",
|
|
2201
|
+
summary: "Update page name or rotation",
|
|
2202
|
+
body: true,
|
|
2203
|
+
response: "json",
|
|
2204
|
+
params: [
|
|
2205
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Page id: the id the API returns for a page (a string of 32 lowercase letters and digits)." },
|
|
2206
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2207
|
+
{ name: "rotation", in: "body", required: false, kind: "number" }
|
|
2208
|
+
]
|
|
2209
|
+
},
|
|
2210
|
+
update_product: {
|
|
2211
|
+
method: "PATCH",
|
|
2212
|
+
path: "/products/{id}",
|
|
2213
|
+
tag: "Products",
|
|
2214
|
+
summary: "Update a product",
|
|
2215
|
+
body: true,
|
|
2216
|
+
response: "json",
|
|
2217
|
+
params: [
|
|
2218
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Product id: the id the API returns for a product (a string of 32 lowercase letters and digits)." },
|
|
2219
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2220
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2221
|
+
{ name: "sku", in: "body", required: false, kind: "string" },
|
|
2222
|
+
{ name: "unitOfMeasure", in: "body", required: false, kind: "string" },
|
|
2223
|
+
{ name: "costPerUnit", in: "body", required: false, kind: "number" },
|
|
2224
|
+
{ name: "currency", in: "body", required: false, kind: "string", enum: ["USD", "CAD", "MXN", "BRL", "ARS", "CLP", "COP", "PEN", "UYU", "PYG", "BOB", "VES", "GYD", "SRD", "GTQ", "HNL", "NIO", "CRC", "PAB", "BZD", "JMD", "HTG", "DOP", "CUP", "BSD", "BBD", "TTD", "XCD", "AWG", "ANG", "KYD", "BMD", "FKP", "EUR", "GBP", "CHF", "NOK", "SEK", "DKK", "PLN", "CZK", "HUF", "RON", "BGN", "HRK", "RSD", "UAH", "RUB", "TRY", "ISK", "BAM", "MKD", "ALL", "MDL", "BYN", "GEL", "AMD", "AZN", "GIP", "ILS", "AED", "SAR", "QAR", "KWD", "BHD", "OMR", "JOD", "LBP", "EGP", "IQD", "IRR", "SYP", "YER", "ZAR", "NAD", "LSL", "SZL", "BWP", "NGN", "KES", "GHS", "MAD", "TND", "DZD", "LYD", "ETB", "ERN", "DJF", "SOS", "SDG", "SSP", "UGX", "TZS", "RWF", "BIF", "MWK", "ZMW", "ZWG", "MZN", "AOA", "CDF", "MGA", "MUR", "SCR", "KMF", "CVE", "GMD", "GNF", "LRD", "SLE", "MRU", "STN", "XOF", "XAF", "SHP", "JPY", "CNY", "HKD", "MOP", "TWD", "KRW", "KPW", "MNT", "SGD", "INR", "BTN", "PKR", "BDT", "LKR", "NPR", "MVR", "AFN", "IDR", "MYR", "PHP", "THB", "VND", "MMK", "KHR", "LAK", "BND", "KZT", "UZS", "KGS", "TJS", "TMT", "AUD", "NZD", "FJD", "PGK", "SBD", "VUV", "WST", "TOP", "XPF"], description: "ISO 4217 currency code" },
|
|
2225
|
+
{ name: "markupPercent", in: "body", required: false, kind: "number" },
|
|
2226
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
2227
|
+
]
|
|
2228
|
+
},
|
|
2229
|
+
update_project: {
|
|
2230
|
+
method: "PATCH",
|
|
2231
|
+
path: "/projects/{id}",
|
|
2232
|
+
tag: "Projects",
|
|
2233
|
+
summary: "Update a project",
|
|
2234
|
+
body: true,
|
|
2235
|
+
response: "json",
|
|
2236
|
+
params: [
|
|
2237
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
2238
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2239
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2240
|
+
{ name: "status", in: "body", required: false, kind: "string", enum: ["lead", "contacted", "qualified", "active", "archived"] },
|
|
2241
|
+
{ name: "settings", in: "body", required: false, kind: "object" },
|
|
2242
|
+
{ name: "tags", in: "body", required: false, kind: "array" },
|
|
2243
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
2244
|
+
]
|
|
2245
|
+
},
|
|
2246
|
+
update_project_privacy: {
|
|
2247
|
+
method: "POST",
|
|
2248
|
+
path: "/projects/{id}/privacy",
|
|
2249
|
+
tag: "Projects",
|
|
2250
|
+
summary: "Change a project\u2019s privacy setting",
|
|
2251
|
+
body: true,
|
|
2252
|
+
response: "empty",
|
|
2253
|
+
params: [
|
|
2254
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
2255
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2256
|
+
{ name: "privacy", in: "body", required: true, kind: "string", enum: ["private", "unlisted", "public"] }
|
|
2257
|
+
]
|
|
2258
|
+
},
|
|
2259
|
+
update_symbol: {
|
|
2260
|
+
method: "PATCH",
|
|
2261
|
+
path: "/symbols/{id}",
|
|
2262
|
+
tag: "Takeoff",
|
|
2263
|
+
summary: "Update a takeoff symbol",
|
|
2264
|
+
body: true,
|
|
2265
|
+
response: "empty",
|
|
2266
|
+
params: [
|
|
2267
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Takeoff symbol id: the id the API returns for a takeoff symbol (a string of 32 lowercase letters and digits)." },
|
|
2268
|
+
{ name: "projectId", in: "body", required: true, kind: "string", description: "Project id: the id the API returns for a project (a string of 32 lowercase letters and digits)." },
|
|
2269
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2270
|
+
{ name: "color", in: "body", required: false, kind: "string" },
|
|
2271
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["count", "linear", "area"] },
|
|
2272
|
+
{ name: "category", in: "body", required: false, kind: "string" },
|
|
2273
|
+
{ name: "subcategory", in: "body", required: false, kind: "string" },
|
|
2274
|
+
{ name: "customProperties", in: "body", required: false, kind: "object" }
|
|
2275
|
+
]
|
|
2276
|
+
},
|
|
2277
|
+
update_vendor: {
|
|
2278
|
+
method: "PATCH",
|
|
2279
|
+
path: "/vendors/{id}",
|
|
2280
|
+
tag: "Vendors",
|
|
2281
|
+
summary: "Update a vendor",
|
|
2282
|
+
body: true,
|
|
2283
|
+
response: "json",
|
|
2284
|
+
params: [
|
|
2285
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Vendor id: the id the API returns for a vendor (a string of 32 lowercase letters and digits)." },
|
|
2286
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2287
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2288
|
+
{ name: "type", in: "body", required: false, kind: "string", enum: ["supplier", "subcontractor", "rental"] },
|
|
2289
|
+
{ name: "kind", in: "body", required: false, kind: "string", enum: ["sub", "supplier", "surety", "insurer", "agency", "owner", "architect", "engineer", "other", null] },
|
|
2290
|
+
{ name: "contactName", in: "body", required: false, kind: "string" },
|
|
2291
|
+
{ name: "email", in: "body", required: false, kind: "string" },
|
|
2292
|
+
{ name: "phone", in: "body", required: false, kind: "string" },
|
|
2293
|
+
{ name: "website", in: "body", required: false, kind: "string" },
|
|
2294
|
+
{ name: "address", in: "body", required: false, kind: "object" },
|
|
2295
|
+
{ name: "paymentTerms", in: "body", required: false, kind: "string" },
|
|
2296
|
+
{ name: "accountNumber", in: "body", required: false, kind: "string" },
|
|
2297
|
+
{ name: "rating", in: "body", required: false, kind: "number" },
|
|
2298
|
+
{ name: "notes", in: "body", required: false, kind: "string" },
|
|
2299
|
+
{ name: "tags", in: "body", required: false, kind: "array" },
|
|
2300
|
+
{ name: "isPreferred", in: "body", required: false, kind: "boolean" }
|
|
2301
|
+
]
|
|
2302
|
+
},
|
|
2303
|
+
update_vendor_quote_status: {
|
|
2304
|
+
method: "PATCH",
|
|
2305
|
+
path: "/quotes/{id}/status",
|
|
2306
|
+
tag: "Vendor quotes",
|
|
2307
|
+
summary: "Accept, reject, or expire a vendor quote",
|
|
2308
|
+
body: true,
|
|
2309
|
+
response: "json",
|
|
2310
|
+
params: [
|
|
2311
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2312
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2313
|
+
{ name: "status", in: "body", required: true, kind: "string", enum: ["accepted", "rejected", "expired"] }
|
|
2314
|
+
]
|
|
2315
|
+
},
|
|
2316
|
+
update_webhook_endpoint: {
|
|
2317
|
+
method: "PATCH",
|
|
2318
|
+
path: "/webhook_endpoints/{id}",
|
|
2319
|
+
tag: "Webhooks",
|
|
2320
|
+
summary: "Update a webhook endpoint",
|
|
2321
|
+
body: true,
|
|
2322
|
+
response: "empty",
|
|
2323
|
+
params: [
|
|
2324
|
+
{ name: "id", in: "path", required: true, kind: "string" },
|
|
2325
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2326
|
+
{ name: "url", in: "body", required: false, kind: "string" },
|
|
2327
|
+
{ name: "events", in: "body", required: false, kind: "array" },
|
|
2328
|
+
{ name: "description", in: "body", required: false, kind: "string" },
|
|
2329
|
+
{ name: "status", in: "body", required: false, kind: "string", enum: ["active", "paused"] }
|
|
2330
|
+
]
|
|
2331
|
+
},
|
|
2332
|
+
update_widget: {
|
|
2333
|
+
method: "PATCH",
|
|
2334
|
+
path: "/widgets/{id}",
|
|
2335
|
+
tag: "Widgets",
|
|
2336
|
+
summary: "Update a widget",
|
|
2337
|
+
body: true,
|
|
2338
|
+
response: "empty",
|
|
2339
|
+
params: [
|
|
2340
|
+
{ name: "id", in: "path", required: true, kind: "string", description: "Widget id: the id the API returns for a widget (a string of 32 lowercase letters and digits)." },
|
|
2341
|
+
{ name: "organizationId", in: "body", required: false, kind: "string", description: "Organization id (a string of 32 lowercase letters and digits), not the organization name. Call GET /v1/me (or the get_me tool) and use memberships[].orgId. Optional with a company key or an app's key: the key's company is used." },
|
|
2342
|
+
{ name: "name", in: "body", required: false, kind: "string" },
|
|
2343
|
+
{ name: "isActive", in: "body", required: false, kind: "boolean" },
|
|
2344
|
+
{ name: "config", in: "body", required: false, kind: "object" }
|
|
2345
|
+
]
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
exports2.OPERATION_METHODS = {
|
|
2349
|
+
addSymbolLineItem: "add_symbol_line_item",
|
|
2350
|
+
archiveConversation: "archive_conversation",
|
|
2351
|
+
archiveLaborCrew: "archive_labor_crew",
|
|
2352
|
+
archiveLaborEquipment: "archive_labor_equipment",
|
|
2353
|
+
archiveLaborRate: "archive_labor_rate",
|
|
2354
|
+
archiveList: "archive_list",
|
|
2355
|
+
archivePage: "archive_page",
|
|
2356
|
+
archiveProduct: "archive_product",
|
|
2357
|
+
archiveProject: "archive_project",
|
|
2358
|
+
archiveVendor: "archive_vendor",
|
|
2359
|
+
attachFileToProject: "attach_file_to_project",
|
|
2360
|
+
batchCreateProducts: "batch_create_products",
|
|
2361
|
+
cancelAssessment: "cancel_assessment",
|
|
2362
|
+
confirmFileUpload: "confirm_file_upload",
|
|
2363
|
+
createAgentSession: "create_agent_session",
|
|
2364
|
+
createAssessment: "create_assessment",
|
|
2365
|
+
createBid: "create_bid",
|
|
2366
|
+
createConversation: "create_conversation",
|
|
2367
|
+
createCustomProperty: "create_custom_property",
|
|
2368
|
+
createEstimate: "create_estimate",
|
|
2369
|
+
createFolder: "create_folder",
|
|
2370
|
+
createGroup: "create_group",
|
|
2371
|
+
createLaborCrew: "create_labor_crew",
|
|
2372
|
+
createLaborEquipment: "create_labor_equipment",
|
|
2373
|
+
createLaborRate: "create_labor_rate",
|
|
2374
|
+
createLayer: "create_layer",
|
|
2375
|
+
createList: "create_list",
|
|
2376
|
+
createMeasurement: "create_measurement",
|
|
2377
|
+
createMessage: "create_message",
|
|
2378
|
+
createOrganization: "create_organization",
|
|
2379
|
+
createProduct: "create_product",
|
|
2380
|
+
createProject: "create_project",
|
|
2381
|
+
createSymbol: "create_symbol",
|
|
2382
|
+
createVendor: "create_vendor",
|
|
2383
|
+
createVendorQuote: "create_vendor_quote",
|
|
2384
|
+
createWebhookEndpoint: "create_webhook_endpoint",
|
|
2385
|
+
createWidget: "create_widget",
|
|
2386
|
+
deleteOrganization: "delete_organization",
|
|
2387
|
+
deleteWebhookEndpoint: "delete_webhook_endpoint",
|
|
2388
|
+
disconnectIntegration: "disconnect_integration",
|
|
2389
|
+
exportProject: "export_project",
|
|
2390
|
+
findSymbolElements: "find_symbol_elements",
|
|
2391
|
+
generateEstimate: "generate_estimate",
|
|
2392
|
+
getAssessment: "get_assessment",
|
|
2393
|
+
getBestPriceForProduct: "get_best_price_for_product",
|
|
2394
|
+
getContact: "get_contact",
|
|
2395
|
+
getConversation: "get_conversation",
|
|
2396
|
+
getCreditBalance: "get_credit_balance",
|
|
2397
|
+
getCreditUsageHistory: "get_credit_usage_history",
|
|
2398
|
+
getCreditUsageStats: "get_credit_usage_stats",
|
|
2399
|
+
getCustomProperty: "get_custom_property",
|
|
2400
|
+
getDocument: "get_document",
|
|
2401
|
+
getFile: "get_file",
|
|
2402
|
+
getFileUrl: "get_file_url",
|
|
2403
|
+
getFolder: "get_folder",
|
|
2404
|
+
getLaborCrew: "get_labor_crew",
|
|
2405
|
+
getLaborEquipment: "get_labor_equipment",
|
|
2406
|
+
getLaborRate: "get_labor_rate",
|
|
2407
|
+
getLatestAssessment: "get_latest_assessment",
|
|
2408
|
+
getList: "get_list",
|
|
2409
|
+
getMe: "get_me",
|
|
2410
|
+
getMeasurement: "get_measurement",
|
|
2411
|
+
getPage: "get_page",
|
|
2412
|
+
getPageContent: "get_page_content",
|
|
2413
|
+
getPricesForProduct: "get_prices_for_product",
|
|
2414
|
+
getProduct: "get_product",
|
|
2415
|
+
getProject: "get_project",
|
|
2416
|
+
getProjectRelations: "get_project_relations",
|
|
2417
|
+
getSpreadsheet: "get_spreadsheet",
|
|
2418
|
+
getSymbol: "get_symbol",
|
|
2419
|
+
getSymbolLineItem: "get_symbol_line_item",
|
|
2420
|
+
getTakeoffSummary: "get_takeoff_summary",
|
|
2421
|
+
getUnreadNotificationCount: "get_unread_notification_count",
|
|
2422
|
+
getUsage: "get_usage",
|
|
2423
|
+
getVendor: "get_vendor",
|
|
2424
|
+
getVendorQuote: "get_vendor_quote",
|
|
2425
|
+
getWidget: "get_widget",
|
|
2426
|
+
listAssessments: "list_assessments",
|
|
2427
|
+
listContacts: "list_contacts",
|
|
2428
|
+
listConversationFiles: "list_conversation_files",
|
|
2429
|
+
listConversations: "list_conversations",
|
|
2430
|
+
listCustomProperties: "list_custom_properties",
|
|
2431
|
+
listDocuments: "list_documents",
|
|
2432
|
+
listFiles: "list_files",
|
|
2433
|
+
listFolderContents: "list_folder_contents",
|
|
2434
|
+
listFolders: "list_folders",
|
|
2435
|
+
listGroups: "list_groups",
|
|
2436
|
+
listInstallations: "list_installations",
|
|
2437
|
+
listIntegrations: "list_integrations",
|
|
2438
|
+
listLaborCrews: "list_labor_crews",
|
|
2439
|
+
listLaborEquipment: "list_labor_equipment",
|
|
2440
|
+
listLaborRates: "list_labor_rates",
|
|
2441
|
+
listLayers: "list_layers",
|
|
2442
|
+
listLists: "list_lists",
|
|
2443
|
+
listMeasurementsByPage: "list_measurements_by_page",
|
|
2444
|
+
listMeasurementsBySymbol: "list_measurements_by_symbol",
|
|
2445
|
+
listMessages: "list_messages",
|
|
2446
|
+
listNotifications: "list_notifications",
|
|
2447
|
+
listOrgDocuments: "list_org_documents",
|
|
2448
|
+
listOrganizations: "list_organizations",
|
|
2449
|
+
listPages: "list_pages",
|
|
2450
|
+
listProducts: "list_products",
|
|
2451
|
+
listProductsInList: "list_products_in_list",
|
|
2452
|
+
listProjectConversations: "list_project_conversations",
|
|
2453
|
+
listProjects: "list_projects",
|
|
2454
|
+
listSpreadsheets: "list_spreadsheets",
|
|
2455
|
+
listSymbolLineItems: "list_symbol_line_items",
|
|
2456
|
+
listSymbols: "list_symbols",
|
|
2457
|
+
listVendorQuotes: "list_vendor_quotes",
|
|
2458
|
+
listVendors: "list_vendors",
|
|
2459
|
+
listWebhookDeliveries: "list_webhook_deliveries",
|
|
2460
|
+
listWebhookEndpoints: "list_webhook_endpoints",
|
|
2461
|
+
listWidgetConversations: "list_widget_conversations",
|
|
2462
|
+
listWidgets: "list_widgets",
|
|
2463
|
+
markAllNotificationsRead: "mark_all_notifications_read",
|
|
2464
|
+
markNotificationRead: "mark_notification_read",
|
|
2465
|
+
patchSymbolLineItem: "patch_symbol_line_item",
|
|
2466
|
+
presignFileUpload: "presign_file_upload",
|
|
2467
|
+
proposeAnalysis: "propose_analysis",
|
|
2468
|
+
provisionCma: "provision_cma",
|
|
2469
|
+
receiveVendorQuote: "receive_vendor_quote",
|
|
2470
|
+
removeCustomProperty: "remove_custom_property",
|
|
2471
|
+
removeFile: "remove_file",
|
|
2472
|
+
removeFolder: "remove_folder",
|
|
2473
|
+
removeGroup: "remove_group",
|
|
2474
|
+
removeLayer: "remove_layer",
|
|
2475
|
+
removeMeasurement: "remove_measurement",
|
|
2476
|
+
removeMessage: "remove_message",
|
|
2477
|
+
removeSymbol: "remove_symbol",
|
|
2478
|
+
removeSymbolLineItem: "remove_symbol_line_item",
|
|
2479
|
+
removeVendorPrice: "remove_vendor_price",
|
|
2480
|
+
removeWidget: "remove_widget",
|
|
2481
|
+
renameFile: "rename_file",
|
|
2482
|
+
renameFolder: "rename_folder",
|
|
2483
|
+
resendWebhookDelivery: "resend_webhook_delivery",
|
|
2484
|
+
restoreList: "restore_list",
|
|
2485
|
+
restorePage: "restore_page",
|
|
2486
|
+
restoreProject: "restore_project",
|
|
2487
|
+
runApprovedAnalysis: "run_approved_analysis",
|
|
2488
|
+
searchHelpArticles: "search_help_articles",
|
|
2489
|
+
searchProducts: "search_products",
|
|
2490
|
+
searchProjectFiles: "search_project_files",
|
|
2491
|
+
searchProjectLineItems: "search_project_line_items",
|
|
2492
|
+
searchVendors: "search_vendors",
|
|
2493
|
+
sendCmaMessage: "send_cma_message",
|
|
2494
|
+
sendWebhookListenerTest: "send_webhook_listener_test",
|
|
2495
|
+
sendWebhookTestEvent: "send_webhook_test_event",
|
|
2496
|
+
setDocumentSharing: "set_document_sharing",
|
|
2497
|
+
setVendorPrice: "set_vendor_price",
|
|
2498
|
+
startWebhookListener: "start_webhook_listener",
|
|
2499
|
+
stopWebhookListener: "stop_webhook_listener",
|
|
2500
|
+
streamWebhookListenerEvents: "stream_webhook_listener_events",
|
|
2501
|
+
updateContact: "update_contact",
|
|
2502
|
+
updateConversationTitle: "update_conversation_title",
|
|
2503
|
+
updateCustomProperty: "update_custom_property",
|
|
2504
|
+
updateGroup: "update_group",
|
|
2505
|
+
updateLaborCrew: "update_labor_crew",
|
|
2506
|
+
updateLaborEquipment: "update_labor_equipment",
|
|
2507
|
+
updateLaborRate: "update_labor_rate",
|
|
2508
|
+
updateLayer: "update_layer",
|
|
2509
|
+
updateList: "update_list",
|
|
2510
|
+
updateMeasurement: "update_measurement",
|
|
2511
|
+
updateMessage: "update_message",
|
|
2512
|
+
updatePage: "update_page",
|
|
2513
|
+
updateProduct: "update_product",
|
|
2514
|
+
updateProject: "update_project",
|
|
2515
|
+
updateProjectPrivacy: "update_project_privacy",
|
|
2516
|
+
updateSymbol: "update_symbol",
|
|
2517
|
+
updateVendor: "update_vendor",
|
|
2518
|
+
updateVendorQuoteStatus: "update_vendor_quote_status",
|
|
2519
|
+
updateWebhookEndpoint: "update_webhook_endpoint",
|
|
2520
|
+
updateWidget: "update_widget"
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2524
|
+
|
|
33
2525
|
// ../sdk/dist/error.js
|
|
34
2526
|
var require_error = __commonJS({
|
|
35
2527
|
"../sdk/dist/error.js"(exports2) {
|
|
36
2528
|
"use strict";
|
|
37
2529
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
38
|
-
exports2.ExayardError = void 0;
|
|
2530
|
+
exports2.errorFromProblem = exports2.APIConnectionError = exports2.InternalServerError = exports2.RateLimitError = exports2.UnprocessableEntityError = exports2.ConflictError = exports2.NotFoundError = exports2.PermissionDeniedError = exports2.PaymentRequiredError = exports2.AuthenticationError = exports2.BadRequestError = exports2.ExayardError = void 0;
|
|
2531
|
+
var KNOWN_FIELDS = /* @__PURE__ */ new Set(["type", "title", "status", "detail", "instance", "code", "param", "doc_url", "request_id"]);
|
|
39
2532
|
var ExayardError = class extends Error {
|
|
40
2533
|
type;
|
|
41
2534
|
title;
|
|
@@ -46,6 +2539,8 @@ var require_error = __commonJS({
|
|
|
46
2539
|
param;
|
|
47
2540
|
docUrl;
|
|
48
2541
|
requestId;
|
|
2542
|
+
/** Problem extension members, such as `limit` on usage_limit_reached. */
|
|
2543
|
+
extensions;
|
|
49
2544
|
constructor(body) {
|
|
50
2545
|
const title = body.title ?? "error";
|
|
51
2546
|
const status = typeof body.status === "number" ? body.status : 0;
|
|
@@ -53,7 +2548,7 @@ var require_error = __commonJS({
|
|
|
53
2548
|
const detail = body.detail ?? "";
|
|
54
2549
|
const message = detail ? `${title} (${code}): ${detail}` : `HTTP ${status || "unknown"}: ${title}`;
|
|
55
2550
|
super(message);
|
|
56
|
-
this.name =
|
|
2551
|
+
this.name = new.target.name;
|
|
57
2552
|
this.type = body.type ?? "about:blank";
|
|
58
2553
|
this.title = title;
|
|
59
2554
|
this.status = status;
|
|
@@ -63,13 +2558,14 @@ var require_error = __commonJS({
|
|
|
63
2558
|
this.param = body.param;
|
|
64
2559
|
this.docUrl = body.doc_url;
|
|
65
2560
|
this.requestId = body.request_id;
|
|
2561
|
+
this.extensions = Object.fromEntries(Object.entries(body).filter(([key]) => !KNOWN_FIELDS.has(key)));
|
|
66
2562
|
}
|
|
67
2563
|
// Convenience guards for the most common branching points.
|
|
68
2564
|
isRateLimited() {
|
|
69
2565
|
return this.code === "rate_limited" || this.status === 429;
|
|
70
2566
|
}
|
|
71
2567
|
isUnauthenticated() {
|
|
72
|
-
return this.code === "unauthenticated" || this.status === 401;
|
|
2568
|
+
return this.code === "unauthenticated" || this.code === "api_key_expired" || this.status === 401;
|
|
73
2569
|
}
|
|
74
2570
|
isNotFound() {
|
|
75
2571
|
return this.code === "not_found" || this.status === 404;
|
|
@@ -82,6 +2578,341 @@ var require_error = __commonJS({
|
|
|
82
2578
|
}
|
|
83
2579
|
};
|
|
84
2580
|
exports2.ExayardError = ExayardError;
|
|
2581
|
+
var BadRequestError = class extends ExayardError {
|
|
2582
|
+
};
|
|
2583
|
+
exports2.BadRequestError = BadRequestError;
|
|
2584
|
+
var AuthenticationError = class extends ExayardError {
|
|
2585
|
+
};
|
|
2586
|
+
exports2.AuthenticationError = AuthenticationError;
|
|
2587
|
+
var PaymentRequiredError = class extends ExayardError {
|
|
2588
|
+
};
|
|
2589
|
+
exports2.PaymentRequiredError = PaymentRequiredError;
|
|
2590
|
+
var PermissionDeniedError = class extends ExayardError {
|
|
2591
|
+
};
|
|
2592
|
+
exports2.PermissionDeniedError = PermissionDeniedError;
|
|
2593
|
+
var NotFoundError = class extends ExayardError {
|
|
2594
|
+
};
|
|
2595
|
+
exports2.NotFoundError = NotFoundError;
|
|
2596
|
+
var ConflictError = class extends ExayardError {
|
|
2597
|
+
};
|
|
2598
|
+
exports2.ConflictError = ConflictError;
|
|
2599
|
+
var UnprocessableEntityError = class extends ExayardError {
|
|
2600
|
+
};
|
|
2601
|
+
exports2.UnprocessableEntityError = UnprocessableEntityError;
|
|
2602
|
+
var RateLimitError = class extends ExayardError {
|
|
2603
|
+
retryAfter;
|
|
2604
|
+
constructor(body, retryAfter) {
|
|
2605
|
+
super(body);
|
|
2606
|
+
this.retryAfter = retryAfter;
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
exports2.RateLimitError = RateLimitError;
|
|
2610
|
+
var InternalServerError = class extends ExayardError {
|
|
2611
|
+
};
|
|
2612
|
+
exports2.InternalServerError = InternalServerError;
|
|
2613
|
+
var APIConnectionError = class extends ExayardError {
|
|
2614
|
+
};
|
|
2615
|
+
exports2.APIConnectionError = APIConnectionError;
|
|
2616
|
+
var BY_CODE = {
|
|
2617
|
+
invalid_request: BadRequestError,
|
|
2618
|
+
invalid_end_user: BadRequestError,
|
|
2619
|
+
company_required: BadRequestError,
|
|
2620
|
+
company_mismatch: BadRequestError,
|
|
2621
|
+
unauthenticated: AuthenticationError,
|
|
2622
|
+
api_key_expired: AuthenticationError,
|
|
2623
|
+
insufficient_credits: PaymentRequiredError,
|
|
2624
|
+
subscription_required: PaymentRequiredError,
|
|
2625
|
+
upgrade_required: PaymentRequiredError,
|
|
2626
|
+
paid_plan_required: PaymentRequiredError,
|
|
2627
|
+
project_limit_reached: PaymentRequiredError,
|
|
2628
|
+
usage_limit_reached: PaymentRequiredError,
|
|
2629
|
+
forbidden: PermissionDeniedError,
|
|
2630
|
+
insufficient_scope: PermissionDeniedError,
|
|
2631
|
+
app_not_installed: PermissionDeniedError,
|
|
2632
|
+
app_suspended: PermissionDeniedError,
|
|
2633
|
+
partner_company_limit_reached: PermissionDeniedError,
|
|
2634
|
+
not_found: NotFoundError,
|
|
2635
|
+
conflict: ConflictError,
|
|
2636
|
+
idempotency_key_reused: ConflictError,
|
|
2637
|
+
rate_limited: RateLimitError,
|
|
2638
|
+
internal_error: InternalServerError
|
|
2639
|
+
};
|
|
2640
|
+
var BY_STATUS = {
|
|
2641
|
+
400: BadRequestError,
|
|
2642
|
+
401: AuthenticationError,
|
|
2643
|
+
402: PaymentRequiredError,
|
|
2644
|
+
403: PermissionDeniedError,
|
|
2645
|
+
404: NotFoundError,
|
|
2646
|
+
409: ConflictError,
|
|
2647
|
+
422: UnprocessableEntityError,
|
|
2648
|
+
429: RateLimitError
|
|
2649
|
+
};
|
|
2650
|
+
var errorFromProblem = (body, retryAfter) => {
|
|
2651
|
+
const status = typeof body.status === "number" ? body.status : 0;
|
|
2652
|
+
const cls = BY_CODE[body.code] ?? BY_STATUS[status] ?? (status >= 500 ? InternalServerError : ExayardError);
|
|
2653
|
+
if (cls === RateLimitError)
|
|
2654
|
+
return new RateLimitError(body, retryAfter);
|
|
2655
|
+
return new cls(body);
|
|
2656
|
+
};
|
|
2657
|
+
exports2.errorFromProblem = errorFromProblem;
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
// ../sdk/dist/retry.js
|
|
2662
|
+
var require_retry = __commonJS({
|
|
2663
|
+
"../sdk/dist/retry.js"(exports2) {
|
|
2664
|
+
"use strict";
|
|
2665
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2666
|
+
exports2.sleep = exports2.retryDelayMs = exports2.parseRetryAfter = exports2.isRetryableStatus = exports2.MAX_RETRY_AFTER_SECONDS = exports2.DEFAULT_MAX_RETRIES = void 0;
|
|
2667
|
+
exports2.DEFAULT_MAX_RETRIES = 2;
|
|
2668
|
+
exports2.MAX_RETRY_AFTER_SECONDS = 60;
|
|
2669
|
+
var BASE_DELAY_MS = 500;
|
|
2670
|
+
var MAX_BACKOFF_MS = 8e3;
|
|
2671
|
+
var isRetryableStatus = (status) => status === 429 || status >= 500;
|
|
2672
|
+
exports2.isRetryableStatus = isRetryableStatus;
|
|
2673
|
+
var parseRetryAfter = (value, now = Date.now()) => {
|
|
2674
|
+
if (value === null || value === void 0)
|
|
2675
|
+
return void 0;
|
|
2676
|
+
const trimmed = value.trim();
|
|
2677
|
+
if (trimmed === "")
|
|
2678
|
+
return void 0;
|
|
2679
|
+
if (/^\d+(\.\d+)?$/.test(trimmed))
|
|
2680
|
+
return Number(trimmed);
|
|
2681
|
+
const at = Date.parse(trimmed);
|
|
2682
|
+
if (Number.isNaN(at))
|
|
2683
|
+
return void 0;
|
|
2684
|
+
return Math.max(0, (at - now) / 1e3);
|
|
2685
|
+
};
|
|
2686
|
+
exports2.parseRetryAfter = parseRetryAfter;
|
|
2687
|
+
var retryDelayMs = (attempt, retryAfterSeconds, random = Math.random) => {
|
|
2688
|
+
if (retryAfterSeconds !== void 0)
|
|
2689
|
+
return Math.ceil(retryAfterSeconds * 1e3);
|
|
2690
|
+
const backoff = Math.min(MAX_BACKOFF_MS, BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
2691
|
+
return Math.round(backoff * (1 - 0.25 * random()));
|
|
2692
|
+
};
|
|
2693
|
+
exports2.retryDelayMs = retryDelayMs;
|
|
2694
|
+
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
2695
|
+
if (signal?.aborted) {
|
|
2696
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
const timer = setTimeout(() => {
|
|
2700
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2701
|
+
resolve();
|
|
2702
|
+
}, ms);
|
|
2703
|
+
const onAbort = () => {
|
|
2704
|
+
clearTimeout(timer);
|
|
2705
|
+
reject(signal?.reason ?? new Error("Aborted"));
|
|
2706
|
+
};
|
|
2707
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2708
|
+
});
|
|
2709
|
+
exports2.sleep = sleep;
|
|
2710
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
|
|
2713
|
+
// ../sdk/dist/listeners.js
|
|
2714
|
+
var require_listeners = __commonJS({
|
|
2715
|
+
"../sdk/dist/listeners.js"(exports2) {
|
|
2716
|
+
"use strict";
|
|
2717
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2718
|
+
exports2.WebhookListeners = void 0;
|
|
2719
|
+
exports2.parseServerSentEvents = parseServerSentEvents;
|
|
2720
|
+
var error_1 = require_error();
|
|
2721
|
+
var retry_1 = require_retry();
|
|
2722
|
+
async function* parseServerSentEvents(body) {
|
|
2723
|
+
const reader = body.getReader();
|
|
2724
|
+
const decoder = new TextDecoder();
|
|
2725
|
+
let buffer = "";
|
|
2726
|
+
let current = { data: [] };
|
|
2727
|
+
const flush = () => {
|
|
2728
|
+
const out = current;
|
|
2729
|
+
current = { data: [] };
|
|
2730
|
+
if (out.data.length === 0 && out.retry === void 0 && out.event === void 0)
|
|
2731
|
+
return null;
|
|
2732
|
+
return {
|
|
2733
|
+
...out.id !== void 0 ? { id: out.id } : {},
|
|
2734
|
+
...out.event !== void 0 ? { event: out.event } : {},
|
|
2735
|
+
data: out.data.join("\n"),
|
|
2736
|
+
...out.retry !== void 0 ? { retry: out.retry } : {}
|
|
2737
|
+
};
|
|
2738
|
+
};
|
|
2739
|
+
const take = function* (line) {
|
|
2740
|
+
if (line === "") {
|
|
2741
|
+
const event = flush();
|
|
2742
|
+
if (event)
|
|
2743
|
+
yield event;
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
if (line.startsWith(":"))
|
|
2747
|
+
return;
|
|
2748
|
+
const colon = line.indexOf(":");
|
|
2749
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
2750
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
2751
|
+
if (value.startsWith(" "))
|
|
2752
|
+
value = value.slice(1);
|
|
2753
|
+
if (field === "data")
|
|
2754
|
+
current.data.push(value);
|
|
2755
|
+
else if (field === "event")
|
|
2756
|
+
current.event = value;
|
|
2757
|
+
else if (field === "id")
|
|
2758
|
+
current.id = value;
|
|
2759
|
+
else if (field === "retry" && /^\d+$/.test(value))
|
|
2760
|
+
current.retry = Number(value);
|
|
2761
|
+
};
|
|
2762
|
+
try {
|
|
2763
|
+
for (; ; ) {
|
|
2764
|
+
const { done, value } = await reader.read();
|
|
2765
|
+
if (done)
|
|
2766
|
+
break;
|
|
2767
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2768
|
+
let newline = buffer.search(/\r\n|\r|\n/);
|
|
2769
|
+
while (newline !== -1) {
|
|
2770
|
+
const line = buffer.slice(0, newline);
|
|
2771
|
+
const width = buffer.startsWith("\r\n", newline) ? 2 : 1;
|
|
2772
|
+
if (width === 1 && buffer[newline] === "\r" && newline === buffer.length - 1)
|
|
2773
|
+
break;
|
|
2774
|
+
buffer = buffer.slice(newline + width);
|
|
2775
|
+
yield* take(line);
|
|
2776
|
+
newline = buffer.search(/\r\n|\r|\n/);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
buffer += decoder.decode();
|
|
2780
|
+
if (buffer !== "")
|
|
2781
|
+
yield* take(buffer);
|
|
2782
|
+
} finally {
|
|
2783
|
+
reader.releaseLock();
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
var DEFAULT_RETRY_MS = 1e3;
|
|
2787
|
+
var DEFAULT_MAX_RECONNECTS = 10;
|
|
2788
|
+
var WebhookListeners = class {
|
|
2789
|
+
transport;
|
|
2790
|
+
constructor(transport) {
|
|
2791
|
+
this.transport = transport;
|
|
2792
|
+
}
|
|
2793
|
+
/** Start a listener. `events` defaults to every event (`["*"]`). Needs `write:webhooks`. */
|
|
2794
|
+
start(body = {}, opts = {}) {
|
|
2795
|
+
return this.transport.request({
|
|
2796
|
+
method: "POST",
|
|
2797
|
+
path: "/webhook_listeners",
|
|
2798
|
+
body: { ...body, organizationId: this.transport.organizationId(body.organizationId) },
|
|
2799
|
+
options: opts
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
/** Send a test event of a concrete type to the listener; it arrives on the stream with `test: true`. */
|
|
2803
|
+
sendTest(id, body, opts = {}) {
|
|
2804
|
+
return this.transport.request({
|
|
2805
|
+
method: "POST",
|
|
2806
|
+
path: `/webhook_listeners/${encodeURIComponent(id)}/test`,
|
|
2807
|
+
body: { ...body, organizationId: this.transport.organizationId(body.organizationId) },
|
|
2808
|
+
options: opts
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
/** Stop the listener and delete its events. */
|
|
2812
|
+
async stop(id, query = {}, opts = {}) {
|
|
2813
|
+
await this.transport.request({
|
|
2814
|
+
method: "DELETE",
|
|
2815
|
+
path: `/webhook_listeners/${encodeURIComponent(id)}`,
|
|
2816
|
+
query: { organizationId: this.transport.organizationId(query.organizationId) },
|
|
2817
|
+
options: opts
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
/** Open the stream once and return the raw Response (text/event-stream). Most callers want `events()`. */
|
|
2821
|
+
open(id, opts = {}) {
|
|
2822
|
+
return this.transport.request({
|
|
2823
|
+
method: "GET",
|
|
2824
|
+
path: `/webhook_listeners/${encodeURIComponent(id)}/events`,
|
|
2825
|
+
query: { organizationId: this.transport.organizationId(opts.organizationId) },
|
|
2826
|
+
headers: {
|
|
2827
|
+
Accept: "text/event-stream",
|
|
2828
|
+
...opts.after !== void 0 ? { "Last-Event-ID": String(opts.after) } : {}
|
|
2829
|
+
},
|
|
2830
|
+
raw: true,
|
|
2831
|
+
// The stream stays open for minutes: no per-attempt timeout once the answer has started.
|
|
2832
|
+
options: { signal: opts.signal, timeoutMs: 3e4 }
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* The listener's events, in order, for as long as the listener lives: reconnects on `event: reconnect` and on a
|
|
2837
|
+
* dropped stream, resuming after the last event it yielded. Ends when `signal` aborts. Throws NotFoundError when
|
|
2838
|
+
* the listener was stopped or swept, and the last error after `maxReconnects` failed reconnects in a row.
|
|
2839
|
+
*/
|
|
2840
|
+
async *events(id, opts = {}) {
|
|
2841
|
+
let after = opts.after;
|
|
2842
|
+
let retryMs = DEFAULT_RETRY_MS;
|
|
2843
|
+
let failures = 0;
|
|
2844
|
+
let first = true;
|
|
2845
|
+
const maxReconnects = opts.maxReconnects ?? DEFAULT_MAX_RECONNECTS;
|
|
2846
|
+
while (!opts.signal?.aborted) {
|
|
2847
|
+
if (!first)
|
|
2848
|
+
opts.onReconnect?.(after);
|
|
2849
|
+
first = false;
|
|
2850
|
+
let res;
|
|
2851
|
+
try {
|
|
2852
|
+
res = await this.open(id, { after, organizationId: opts.organizationId, signal: opts.signal });
|
|
2853
|
+
} catch (err) {
|
|
2854
|
+
if (opts.signal?.aborted)
|
|
2855
|
+
return;
|
|
2856
|
+
if (err instanceof error_1.NotFoundError || err instanceof error_1.ExayardError && err.status >= 400 && err.status < 500 && err.status !== 429)
|
|
2857
|
+
throw err;
|
|
2858
|
+
failures += 1;
|
|
2859
|
+
if (failures > maxReconnects)
|
|
2860
|
+
throw err;
|
|
2861
|
+
await sleepUnlessAborted(Math.max(retryMs, (0, retry_1.retryDelayMs)(failures)), opts.signal);
|
|
2862
|
+
continue;
|
|
2863
|
+
}
|
|
2864
|
+
if (!res.body)
|
|
2865
|
+
throw new Error("Exayard: the listener stream answered with no body.");
|
|
2866
|
+
failures = 0;
|
|
2867
|
+
let reconnectAtOnce = false;
|
|
2868
|
+
try {
|
|
2869
|
+
for await (const message of parseServerSentEvents(res.body)) {
|
|
2870
|
+
if (message.retry !== void 0)
|
|
2871
|
+
retryMs = message.retry;
|
|
2872
|
+
if (message.event === "reconnect") {
|
|
2873
|
+
reconnectAtOnce = true;
|
|
2874
|
+
break;
|
|
2875
|
+
}
|
|
2876
|
+
if (message.event !== "webhook" || message.data === "")
|
|
2877
|
+
continue;
|
|
2878
|
+
const event = JSON.parse(message.data);
|
|
2879
|
+
after = event.seq;
|
|
2880
|
+
yield event;
|
|
2881
|
+
}
|
|
2882
|
+
} catch (err) {
|
|
2883
|
+
if (opts.signal?.aborted)
|
|
2884
|
+
return;
|
|
2885
|
+
if (err instanceof SyntaxError)
|
|
2886
|
+
throw err;
|
|
2887
|
+
} finally {
|
|
2888
|
+
await res.body.cancel().catch(() => void 0);
|
|
2889
|
+
}
|
|
2890
|
+
if (!reconnectAtOnce)
|
|
2891
|
+
await sleepUnlessAborted(retryMs, opts.signal);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
exports2.WebhookListeners = WebhookListeners;
|
|
2896
|
+
var sleepUnlessAborted = async (ms, signal) => {
|
|
2897
|
+
try {
|
|
2898
|
+
await (0, retry_1.sleep)(ms, signal);
|
|
2899
|
+
} catch {
|
|
2900
|
+
}
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
});
|
|
2904
|
+
|
|
2905
|
+
// ../sdk/dist/options.js
|
|
2906
|
+
var require_options = __commonJS({
|
|
2907
|
+
"../sdk/dist/options.js"(exports2) {
|
|
2908
|
+
"use strict";
|
|
2909
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2910
|
+
exports2.isValidEndUser = exports2.END_USER_MAX_LENGTH = exports2.END_USER_HEADER = void 0;
|
|
2911
|
+
exports2.END_USER_HEADER = "Exayard-End-User";
|
|
2912
|
+
exports2.END_USER_MAX_LENGTH = 128;
|
|
2913
|
+
var END_USER_PATTERN = new RegExp(`^[\\x20-\\x7E]{1,${exports2.END_USER_MAX_LENGTH}}$`);
|
|
2914
|
+
var isValidEndUser = (value) => END_USER_PATTERN.test(value);
|
|
2915
|
+
exports2.isValidEndUser = isValidEndUser;
|
|
85
2916
|
}
|
|
86
2917
|
});
|
|
87
2918
|
|
|
@@ -90,7 +2921,48 @@ var require_webhooks = __commonJS({
|
|
|
90
2921
|
"../sdk/dist/webhooks.js"(exports2) {
|
|
91
2922
|
"use strict";
|
|
92
2923
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
93
|
-
exports2.constructWebhookEvent = exports2.WebhookSignatureError = void 0;
|
|
2924
|
+
exports2.constructWebhookEvent = exports2.signWebhookPayload = exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports2.WebhookSignatureError = exports2.WEBHOOK_SIGNATURE_HEADER = exports2.WEBHOOK_EVENT_TYPES = void 0;
|
|
2925
|
+
exports2.WEBHOOK_EVENT_TYPES = [
|
|
2926
|
+
"project.created",
|
|
2927
|
+
"project.updated",
|
|
2928
|
+
"project.archived",
|
|
2929
|
+
"assessment.started",
|
|
2930
|
+
"assessment.completed",
|
|
2931
|
+
"assessment.cancelled",
|
|
2932
|
+
"assessment.failed",
|
|
2933
|
+
"estimate.generated",
|
|
2934
|
+
"bid.generated",
|
|
2935
|
+
"bid.submitted",
|
|
2936
|
+
"bid.won",
|
|
2937
|
+
"bid.lost",
|
|
2938
|
+
"bid.accepted",
|
|
2939
|
+
"bid.declined",
|
|
2940
|
+
"bid.sent",
|
|
2941
|
+
"bid.viewed",
|
|
2942
|
+
"bid.commented",
|
|
2943
|
+
"file.processed",
|
|
2944
|
+
"addendum.uploaded",
|
|
2945
|
+
"quote.requested",
|
|
2946
|
+
"quote.received",
|
|
2947
|
+
"quote.accepted",
|
|
2948
|
+
"quote.rejected",
|
|
2949
|
+
"quote.expired",
|
|
2950
|
+
"conversation.started",
|
|
2951
|
+
"conversation.handoff_requested",
|
|
2952
|
+
"conversation.closed",
|
|
2953
|
+
"lead.captured",
|
|
2954
|
+
"contact.created",
|
|
2955
|
+
"appointment.booked",
|
|
2956
|
+
"appointment.rescheduled",
|
|
2957
|
+
"appointment.cancelled",
|
|
2958
|
+
"call.completed",
|
|
2959
|
+
"call.missed",
|
|
2960
|
+
"review.requested",
|
|
2961
|
+
"job.written_back",
|
|
2962
|
+
"payment.captured",
|
|
2963
|
+
"payment.failed"
|
|
2964
|
+
];
|
|
2965
|
+
exports2.WEBHOOK_SIGNATURE_HEADER = "Exayard-Signature";
|
|
94
2966
|
var WebhookSignatureError2 = class extends Error {
|
|
95
2967
|
code;
|
|
96
2968
|
constructor(code, message) {
|
|
@@ -100,27 +2972,27 @@ var require_webhooks = __commonJS({
|
|
|
100
2972
|
}
|
|
101
2973
|
};
|
|
102
2974
|
exports2.WebhookSignatureError = WebhookSignatureError2;
|
|
103
|
-
|
|
2975
|
+
exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 5 * 60;
|
|
104
2976
|
var parseSignatureHeader = (header) => {
|
|
105
|
-
const parts = header.split(",").map((s) => s.trim());
|
|
106
2977
|
let t;
|
|
107
|
-
|
|
108
|
-
for (const part of
|
|
2978
|
+
const v1 = [];
|
|
2979
|
+
for (const part of header.split(",").map((s) => s.trim())) {
|
|
109
2980
|
const eq = part.indexOf("=");
|
|
110
2981
|
if (eq === -1)
|
|
111
2982
|
continue;
|
|
112
2983
|
const k = part.slice(0, eq);
|
|
113
2984
|
const v = part.slice(eq + 1);
|
|
114
|
-
if (k === "t")
|
|
115
|
-
t =
|
|
116
|
-
if (k === "v1")
|
|
117
|
-
v1
|
|
2985
|
+
if (k === "t" && /^\d+$/.test(v))
|
|
2986
|
+
t = Number(v);
|
|
2987
|
+
if (k === "v1" && v)
|
|
2988
|
+
v1.push(v);
|
|
118
2989
|
}
|
|
119
|
-
if (t === void 0 ||
|
|
120
|
-
throw new WebhookSignatureError2("malformed_header",
|
|
2990
|
+
if (t === void 0 || v1.length === 0) {
|
|
2991
|
+
throw new WebhookSignatureError2("malformed_header", 'Exayard-Signature must be "t=<unix seconds>,v1=<digest>".');
|
|
121
2992
|
}
|
|
122
2993
|
return { t, v1 };
|
|
123
2994
|
};
|
|
2995
|
+
var toText = (payload) => typeof payload === "string" ? payload : new TextDecoder().decode(payload);
|
|
124
2996
|
var hmacSha256Base64 = async (secret, payload) => {
|
|
125
2997
|
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
126
2998
|
const digest = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
|
|
@@ -138,17 +3010,27 @@ var require_webhooks = __commonJS({
|
|
|
138
3010
|
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
139
3011
|
return result === 0;
|
|
140
3012
|
};
|
|
141
|
-
var
|
|
3013
|
+
var signWebhookPayload2 = async (payload, secret, options = {}) => {
|
|
3014
|
+
const t = options.timestamp ?? Math.floor(Date.now() / 1e3);
|
|
3015
|
+
const v1 = await hmacSha256Base64(secret, `${t}.${toText(payload)}`);
|
|
3016
|
+
return `t=${t},v1=${v1}`;
|
|
3017
|
+
};
|
|
3018
|
+
exports2.signWebhookPayload = signWebhookPayload2;
|
|
3019
|
+
var constructWebhookEvent2 = async (payload, signatureHeader, secret, options = {}) => {
|
|
3020
|
+
if (!signatureHeader)
|
|
3021
|
+
throw new WebhookSignatureError2("malformed_header", "The Exayard-Signature header is missing.");
|
|
142
3022
|
const { t, v1 } = parseSignatureHeader(signatureHeader);
|
|
3023
|
+
const tolerance = options.toleranceSeconds ?? exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
143
3024
|
const nowSec = Math.floor((options.now ?? Date.now()) / 1e3);
|
|
144
|
-
if (Math.abs(nowSec - t) >
|
|
145
|
-
throw new WebhookSignatureError2("replay_window_exceeded", `Signature timestamp ${t} is outside the ${
|
|
3025
|
+
if (Math.abs(nowSec - t) > tolerance) {
|
|
3026
|
+
throw new WebhookSignatureError2("replay_window_exceeded", `Signature timestamp ${t} is outside the ${tolerance}-second window (now=${nowSec}).`);
|
|
146
3027
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
3028
|
+
const text = toText(payload);
|
|
3029
|
+
const expected = await hmacSha256Base64(secret, `${t}.${text}`);
|
|
3030
|
+
if (!v1.some((candidate) => safeCompare(expected, candidate))) {
|
|
149
3031
|
throw new WebhookSignatureError2("invalid_signature", "HMAC digest mismatch. Check the endpoint secret.");
|
|
150
3032
|
}
|
|
151
|
-
return JSON.parse(
|
|
3033
|
+
return JSON.parse(text);
|
|
152
3034
|
};
|
|
153
3035
|
exports2.constructWebhookEvent = constructWebhookEvent2;
|
|
154
3036
|
}
|
|
@@ -160,7 +3042,11 @@ var require_client = __commonJS({
|
|
|
160
3042
|
"use strict";
|
|
161
3043
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
162
3044
|
exports2.Exayard = void 0;
|
|
3045
|
+
var operations_1 = require_operations();
|
|
163
3046
|
var error_1 = require_error();
|
|
3047
|
+
var listeners_1 = require_listeners();
|
|
3048
|
+
var options_1 = require_options();
|
|
3049
|
+
var retry_1 = require_retry();
|
|
164
3050
|
var webhooks_1 = require_webhooks();
|
|
165
3051
|
var UNSAFE_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
166
3052
|
var randomIdempotencyKey = () => {
|
|
@@ -169,24 +3055,91 @@ var require_client = __commonJS({
|
|
|
169
3055
|
}
|
|
170
3056
|
return `idem_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
171
3057
|
};
|
|
3058
|
+
var envApiKey = () => {
|
|
3059
|
+
if (typeof process === "undefined" || !process.env)
|
|
3060
|
+
return void 0;
|
|
3061
|
+
const value = process.env.EXAYARD_API_KEY;
|
|
3062
|
+
return value && value.trim() !== "" ? value.trim() : void 0;
|
|
3063
|
+
};
|
|
3064
|
+
var checkEndUser = (value) => {
|
|
3065
|
+
if (value === void 0)
|
|
3066
|
+
return void 0;
|
|
3067
|
+
if (!(0, options_1.isValidEndUser)(value)) {
|
|
3068
|
+
throw new TypeError(`Exayard: endUser must be 1 to ${options_1.END_USER_MAX_LENGTH} printable characters.`);
|
|
3069
|
+
}
|
|
3070
|
+
return value;
|
|
3071
|
+
};
|
|
3072
|
+
var transportProblem = (code, status, detail, requestId) => ({
|
|
3073
|
+
type: `https://developers.exayard.com/concepts/errors#${code}`,
|
|
3074
|
+
title: code === "transport_error" ? "Transport Error" : code === "timeout" ? "Timeout" : "Connection Error",
|
|
3075
|
+
status,
|
|
3076
|
+
detail,
|
|
3077
|
+
code,
|
|
3078
|
+
doc_url: `https://developers.exayard.com/concepts/errors#${code}`,
|
|
3079
|
+
...requestId ? { request_id: requestId } : {}
|
|
3080
|
+
});
|
|
172
3081
|
var Exayard2 = class {
|
|
173
|
-
|
|
174
|
-
bearerToken;
|
|
3082
|
+
credential;
|
|
175
3083
|
baseUrl;
|
|
176
3084
|
timeoutMs;
|
|
3085
|
+
maxRetries;
|
|
177
3086
|
fetchImpl;
|
|
3087
|
+
defaultOrganizationId;
|
|
3088
|
+
defaultEndUser;
|
|
178
3089
|
constructor(opts = {}) {
|
|
179
|
-
|
|
180
|
-
|
|
3090
|
+
const credential = opts.apiKey ?? opts.bearerToken ?? envApiKey();
|
|
3091
|
+
if (!credential) {
|
|
3092
|
+
throw new Error("Exayard: pass `apiKey` (or set EXAYARD_API_KEY) or `bearerToken` \u2014 see https://developers.exayard.com/api-reference.");
|
|
181
3093
|
}
|
|
182
|
-
this.
|
|
183
|
-
this.bearerToken = opts.bearerToken;
|
|
3094
|
+
this.credential = credential;
|
|
184
3095
|
this.baseUrl = (opts.baseUrl ?? "https://api.exayard.com/v1").replace(/\/$/, "");
|
|
185
3096
|
this.timeoutMs = opts.timeoutMs ?? 6e4;
|
|
186
|
-
this.
|
|
3097
|
+
this.maxRetries = Math.max(0, opts.maxRetries ?? retry_1.DEFAULT_MAX_RETRIES);
|
|
3098
|
+
this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
3099
|
+
this.defaultOrganizationId = opts.organizationId;
|
|
3100
|
+
this.defaultEndUser = checkEndUser(opts.endUser);
|
|
3101
|
+
}
|
|
3102
|
+
/** The company a call names, else the client's default; undefined lets the key imply it. */
|
|
3103
|
+
organizationFor(named) {
|
|
3104
|
+
return named ?? this.defaultOrganizationId;
|
|
3105
|
+
}
|
|
3106
|
+
// One attempt: the per-attempt timeout covers the wait for the answer's headers only, so a stream body is never
|
|
3107
|
+
// cut by it; the caller's signal aborts both (its listener stays while the body may still be read).
|
|
3108
|
+
async attempt(url, init, timeoutMs, signal) {
|
|
3109
|
+
const ac = new AbortController();
|
|
3110
|
+
let timedOut = false;
|
|
3111
|
+
const onAbort = () => ac.abort(signal?.reason);
|
|
3112
|
+
if (signal?.aborted)
|
|
3113
|
+
ac.abort(signal.reason);
|
|
3114
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3115
|
+
const timer = timeoutMs > 0 ? setTimeout(() => (timedOut = true, ac.abort()), timeoutMs) : void 0;
|
|
3116
|
+
try {
|
|
3117
|
+
return await this.fetchImpl(url, { ...init, signal: ac.signal });
|
|
3118
|
+
} catch (err) {
|
|
3119
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3120
|
+
if (signal?.aborted)
|
|
3121
|
+
throw err;
|
|
3122
|
+
throw new error_1.APIConnectionError(timedOut ? transportProblem("timeout", 0, `No answer within ${timeoutMs} ms.`) : transportProblem("connection_error", 0, err?.message || "The connection failed."));
|
|
3123
|
+
} finally {
|
|
3124
|
+
if (timer)
|
|
3125
|
+
clearTimeout(timer);
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
async toError(res) {
|
|
3129
|
+
const retryAfter = (0, retry_1.parseRetryAfter)(res.headers.get("Retry-After"));
|
|
3130
|
+
const ct = res.headers.get("Content-Type") ?? "";
|
|
3131
|
+
if (ct.includes("problem+json") || ct.includes("application/json")) {
|
|
3132
|
+
const body2 = await res.json().catch(() => null);
|
|
3133
|
+
if (body2 && typeof body2 === "object")
|
|
3134
|
+
return (0, error_1.errorFromProblem)({ ...body2, status: body2.status ?? res.status }, retryAfter);
|
|
3135
|
+
}
|
|
3136
|
+
const text = await res.text().catch(() => "");
|
|
3137
|
+
const body = transportProblem("transport_error", res.status, text.slice(0, 500) || `HTTP ${res.status}`, res.headers.get("X-Request-Id") ?? void 0);
|
|
3138
|
+
return (0, error_1.errorFromProblem)({ ...body, code: res.status === 429 ? "rate_limited" : body.code }, retryAfter);
|
|
187
3139
|
}
|
|
188
3140
|
// Internal request primitive. Public resource methods compose this.
|
|
189
3141
|
async request(opts) {
|
|
3142
|
+
const call = opts.options ?? {};
|
|
190
3143
|
const url = new URL(`${this.baseUrl}${opts.path}`);
|
|
191
3144
|
if (opts.query) {
|
|
192
3145
|
for (const [k, v] of Object.entries(opts.query)) {
|
|
@@ -196,86 +3149,138 @@ var require_client = __commonJS({
|
|
|
196
3149
|
}
|
|
197
3150
|
const headers = {
|
|
198
3151
|
Accept: "application/json",
|
|
199
|
-
Authorization: `Bearer ${this.
|
|
3152
|
+
Authorization: `Bearer ${this.credential}`,
|
|
3153
|
+
...opts.headers
|
|
200
3154
|
};
|
|
201
3155
|
if (opts.body !== void 0)
|
|
202
3156
|
headers["Content-Type"] = "application/json";
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
205
|
-
headers[
|
|
206
|
-
|
|
3157
|
+
const endUser = checkEndUser(call.endUser) ?? this.defaultEndUser;
|
|
3158
|
+
if (endUser !== void 0)
|
|
3159
|
+
headers[options_1.END_USER_HEADER] = endUser;
|
|
3160
|
+
if (call.idempotencyKey)
|
|
3161
|
+
headers["Idempotency-Key"] = call.idempotencyKey;
|
|
3162
|
+
else if (UNSAFE_METHODS.has(opts.method))
|
|
207
3163
|
headers["Idempotency-Key"] = randomIdempotencyKey();
|
|
3164
|
+
const init = {
|
|
3165
|
+
method: opts.method,
|
|
3166
|
+
headers,
|
|
3167
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
3168
|
+
};
|
|
3169
|
+
const maxRetries = Math.max(0, call.maxRetries ?? this.maxRetries);
|
|
3170
|
+
const timeoutMs = call.timeoutMs ?? this.timeoutMs;
|
|
3171
|
+
for (let attempt = 0; ; attempt++) {
|
|
3172
|
+
let res;
|
|
3173
|
+
try {
|
|
3174
|
+
res = await this.attempt(url.toString(), init, timeoutMs, call.signal);
|
|
3175
|
+
} catch (err) {
|
|
3176
|
+
if (err instanceof error_1.APIConnectionError && attempt < maxRetries) {
|
|
3177
|
+
await (0, retry_1.sleep)((0, retry_1.retryDelayMs)(attempt + 1), call.signal);
|
|
3178
|
+
continue;
|
|
3179
|
+
}
|
|
3180
|
+
throw err;
|
|
3181
|
+
}
|
|
3182
|
+
if (res.ok) {
|
|
3183
|
+
if (opts.raw)
|
|
3184
|
+
return res;
|
|
3185
|
+
if (res.status === 204)
|
|
3186
|
+
return void 0;
|
|
3187
|
+
const text = await res.text();
|
|
3188
|
+
return text === "" ? void 0 : JSON.parse(text);
|
|
3189
|
+
}
|
|
3190
|
+
if ((0, retry_1.isRetryableStatus)(res.status) && attempt < maxRetries) {
|
|
3191
|
+
const retryAfter = (0, retry_1.parseRetryAfter)(res.headers.get("Retry-After"));
|
|
3192
|
+
if (retryAfter === void 0 || retryAfter <= retry_1.MAX_RETRY_AFTER_SECONDS) {
|
|
3193
|
+
await res.body?.cancel().catch(() => void 0);
|
|
3194
|
+
await (0, retry_1.sleep)((0, retry_1.retryDelayMs)(attempt + 1, retryAfter), call.signal);
|
|
3195
|
+
continue;
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
throw await this.toError(res);
|
|
208
3199
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
|
|
3200
|
+
}
|
|
3201
|
+
/**
|
|
3202
|
+
* Call any API operation by its name: the spec's operationId, which is also
|
|
3203
|
+
* its MCP tool name and its CLI command. The input is the operation's path,
|
|
3204
|
+
* query and body fields merged flat, the same input its MCP tool takes.
|
|
3205
|
+
*/
|
|
3206
|
+
call(name, input, opts = {}) {
|
|
3207
|
+
const op = operations_1.OPERATIONS[name];
|
|
3208
|
+
if (!op)
|
|
3209
|
+
throw new Error(`Exayard: no operation named ${String(name)}`);
|
|
3210
|
+
const args = { ...input };
|
|
3211
|
+
const known = new Set(op.params.map((param) => param.name));
|
|
3212
|
+
const unknown = Object.keys(args).filter((field) => !known.has(field));
|
|
3213
|
+
if (unknown.length > 0)
|
|
3214
|
+
throw new Error(`Exayard: ${name} does not take ${unknown.join(", ")}`);
|
|
3215
|
+
if (known.has("organizationId") && args.organizationId === void 0 && this.defaultOrganizationId) {
|
|
3216
|
+
args.organizationId = this.defaultOrganizationId;
|
|
221
3217
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
3218
|
+
let path = op.path;
|
|
3219
|
+
const query = {};
|
|
3220
|
+
const body = {};
|
|
3221
|
+
for (const param of op.params) {
|
|
3222
|
+
const value = args[param.name];
|
|
3223
|
+
if (param.in === "path") {
|
|
3224
|
+
if (value === void 0 || value === null || value === "")
|
|
3225
|
+
throw new Error(`Exayard: ${name} needs ${param.name}`);
|
|
3226
|
+
path = path.replace(`{${param.name}}`, encodeURIComponent(String(value)));
|
|
3227
|
+
} else if (value !== void 0) {
|
|
3228
|
+
if (param.in === "query")
|
|
3229
|
+
query[param.name] = value;
|
|
3230
|
+
else
|
|
3231
|
+
body[param.name] = value;
|
|
227
3232
|
}
|
|
228
|
-
const text = await res.text().catch(() => "");
|
|
229
|
-
throw new error_1.ExayardError({
|
|
230
|
-
type: "https://developers.exayard.com/concepts/errors#transport_error",
|
|
231
|
-
title: "Transport Error",
|
|
232
|
-
status: res.status,
|
|
233
|
-
detail: text.slice(0, 500) || `HTTP ${res.status}`,
|
|
234
|
-
code: "transport_error",
|
|
235
|
-
doc_url: "https://developers.exayard.com/concepts/errors#transport_error",
|
|
236
|
-
request_id: res.headers.get("X-Request-Id") ?? void 0
|
|
237
|
-
});
|
|
238
3233
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
3234
|
+
return this.request({
|
|
3235
|
+
method: op.method,
|
|
3236
|
+
path,
|
|
3237
|
+
query,
|
|
3238
|
+
body: op.body ? body : void 0,
|
|
3239
|
+
raw: op.response === "stream",
|
|
3240
|
+
options: opts
|
|
3241
|
+
});
|
|
242
3242
|
}
|
|
3243
|
+
/**
|
|
3244
|
+
* Every API operation as a typed method, generated from the API spec:
|
|
3245
|
+
* `exa.api.listVendorQuotes({ id: projectId })`.
|
|
3246
|
+
*/
|
|
3247
|
+
api = Object.fromEntries(Object.entries(operations_1.OPERATION_METHODS).map(([method, name]) => [
|
|
3248
|
+
method,
|
|
3249
|
+
(input, opts) => this.call(name, input, opts)
|
|
3250
|
+
]));
|
|
243
3251
|
// ---------------------------------------------------------------------------
|
|
244
|
-
// Resource surface — a curated
|
|
245
|
-
// most often hit.
|
|
246
|
-
//
|
|
3252
|
+
// Resource surface — a curated layer over the generated operations for the
|
|
3253
|
+
// flows agents and integrations most often hit. Each method is a call() of the
|
|
3254
|
+
// operation it names, so its input and output types come from the API spec.
|
|
3255
|
+
// Everything else is on `exa.api` and `exa.call`. `organizationId` is optional
|
|
3256
|
+
// everywhere: the key implies the company, or the client's default fills it.
|
|
247
3257
|
// ---------------------------------------------------------------------------
|
|
248
3258
|
me = {
|
|
249
|
-
get: () => this.
|
|
3259
|
+
get: () => this.call("get_me", {})
|
|
250
3260
|
};
|
|
251
3261
|
projects = {
|
|
252
|
-
// GET /v1/projects returns a raw array
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
archive: (id, query) => this.request({ method: "POST", path: `/projects/${id}/archive`, query })
|
|
3262
|
+
// GET /v1/projects returns a raw array; there is no { items, next_cursor }
|
|
3263
|
+
// envelope and no limit/cursor pagination yet.
|
|
3264
|
+
list: (query) => this.call("list_projects", query),
|
|
3265
|
+
get: (id, query = {}) => this.call("get_project", { id, ...query }),
|
|
3266
|
+
create: (body, opts = {}) => this.call("create_project", body, opts),
|
|
3267
|
+
export: (id, query = {}) => this.call("export_project", { id, ...query }),
|
|
3268
|
+
archive: (id, query = {}) => this.call("archive_project", { id, ...query })
|
|
260
3269
|
};
|
|
261
3270
|
// File upload is a 3-step dance: presign (get an R2 upload URL) → PUT the
|
|
262
3271
|
// bytes straight to R2 → confirm (which also kicks off PDF page extraction).
|
|
263
3272
|
// `upload` wraps all three. A PDF becomes pages asynchronously after confirm —
|
|
264
3273
|
// poll pages.list() until processingStatus is 'complete' before proposing/running.
|
|
3274
|
+
// The project decides the organization, so none of the three takes one.
|
|
265
3275
|
files = {
|
|
266
|
-
presign: (body) => this.
|
|
267
|
-
|
|
268
|
-
path: "/files",
|
|
269
|
-
body
|
|
270
|
-
}),
|
|
271
|
-
confirm: (fileId, r2Key) => this.request({ method: "POST", path: `/files/${fileId}/confirm`, body: { r2Key } }),
|
|
3276
|
+
presign: (body) => this.call("presign_file_upload", body),
|
|
3277
|
+
confirm: (fileId, r2Key) => this.call("confirm_file_upload", { id: fileId, r2Key }),
|
|
272
3278
|
// Convenience: presign → PUT bytes to the presigned R2 URL → confirm.
|
|
273
3279
|
// The PUT goes directly to R2 (a different host, self-authenticated by the
|
|
274
3280
|
// signed URL) — not through the /v1 request primitive.
|
|
275
3281
|
upload: async (body) => {
|
|
276
3282
|
const buf = body.bytes instanceof ArrayBuffer ? new Uint8Array(body.bytes) : body.bytes;
|
|
277
3283
|
const { fileId, uploadUrl, r2Key } = await this.files.presign({
|
|
278
|
-
organizationId: body.organizationId,
|
|
279
3284
|
projectId: body.projectId,
|
|
280
3285
|
filename: body.filename,
|
|
281
3286
|
mimeType: body.mimeType,
|
|
@@ -306,49 +3311,120 @@ var require_client = __commonJS({
|
|
|
306
3311
|
// List a project's pages (grouped by file). Use to discover pageIds + poll
|
|
307
3312
|
// each file's processingStatus until extraction finishes.
|
|
308
3313
|
pages = {
|
|
309
|
-
list: (projectId, query) => this.
|
|
3314
|
+
list: (projectId, query = {}) => this.call("list_pages", { id: projectId, ...query })
|
|
310
3315
|
};
|
|
311
3316
|
// AI takeoff assessments. Reads resolve the org from the project/assessment
|
|
312
|
-
// (no organizationId). `
|
|
3317
|
+
// (no organizationId). `run` kicks off an AI run on an existing project's
|
|
313
3318
|
// pages — pair with the assessment.completed webhook + latest()/takeoffSummary
|
|
314
3319
|
// to pull results once it finishes.
|
|
315
3320
|
assessments = {
|
|
316
|
-
list: (projectId) => this.
|
|
317
|
-
latest: (projectId) => this.
|
|
318
|
-
get: (id) => this.
|
|
319
|
-
takeoffSummary: (projectId) => this.
|
|
3321
|
+
list: (projectId) => this.call("list_assessments", { id: projectId }),
|
|
3322
|
+
latest: (projectId) => this.call("get_latest_assessment", { id: projectId }),
|
|
3323
|
+
get: (id) => this.call("get_assessment", { id }),
|
|
3324
|
+
takeoffSummary: (projectId) => this.call("get_takeoff_summary", { id: projectId }),
|
|
320
3325
|
// Ask the AI what to measure: a natural-language prompt + pageIds → a
|
|
321
3326
|
// proposed `elements` array (id/name/category/hexColor) you can pass
|
|
322
3327
|
// straight to run(). The bridge for callers who don't already know the
|
|
323
|
-
// elements.
|
|
324
|
-
propose: (projectId, body) => this.
|
|
3328
|
+
// elements.
|
|
3329
|
+
propose: (projectId, body) => this.call("propose_analysis", { id: projectId, ...body }),
|
|
325
3330
|
// Run an AI takeoff analysis on an existing project and let it complete
|
|
326
3331
|
// end-to-end (no human approval step). You specify exactly what to detect
|
|
327
3332
|
// via `elements` (each: id, name, category area|linear|count, hexColor) on
|
|
328
|
-
// the given pageIds. Returns { assessmentId,
|
|
329
|
-
//
|
|
3333
|
+
// the given pageIds. Returns { assessmentId, pageIds }; track completion via
|
|
3334
|
+
// the assessment.completed webhook + latest()/takeoffSummary.
|
|
330
3335
|
//
|
|
331
|
-
// (
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
3336
|
+
// (The auto-detect POST /assessments path pauses at awaiting_approval,
|
|
3337
|
+
// which needs a human, so it isn't wrapped here; it is exa.api.createAssessment.)
|
|
3338
|
+
run: (projectId, body, opts = {}) => this.call("run_approved_analysis", { id: projectId, ...body }, opts)
|
|
3339
|
+
};
|
|
3340
|
+
// Generate an estimate from a project's takeoff and persist it as a document.
|
|
3341
|
+
// Blocking: the estimator typically runs 30–60s, which is at the default
|
|
3342
|
+
// 60s client timeout — raise `timeoutMs` when constructing the client.
|
|
3343
|
+
//
|
|
3344
|
+
// The saved document is PRIVATE to your organization. `share: true` also
|
|
3345
|
+
// publishes it on a public link anyone holding the URL can read, and makes
|
|
3346
|
+
// `documentUrl` the public path instead of the signed-in one. Set it only
|
|
3347
|
+
// when the customer asked for a shareable link; `documents.setSharing` can
|
|
3348
|
+
// share or unshare later either way.
|
|
3349
|
+
//
|
|
3350
|
+
// (The streaming sibling POST /v1/projects/{id}/estimates/generate persists
|
|
3351
|
+
// no document and has no `share` flag; it is exa.api.generateEstimate, which
|
|
3352
|
+
// returns the event-stream Response.)
|
|
3353
|
+
estimates = {
|
|
3354
|
+
create: (projectId, body, opts = {}) => this.call("create_estimate", { id: projectId, ...body }, opts)
|
|
3355
|
+
};
|
|
3356
|
+
// Same estimator wrapped with bid-formatting instructions, persisted as a
|
|
3357
|
+
// bid document. Same blocking timing and same private-by-default `share`
|
|
3358
|
+
// semantics as estimates.create. When `measurementContext` is omitted the
|
|
3359
|
+
// takeoff is auto-loaded server-side, which can 422 on line items with
|
|
3360
|
+
// missing formula variables — retry with `ignoreMissingVariables: true` to
|
|
3361
|
+
// skip them (the skipped rows come back in `warnings`).
|
|
3362
|
+
bids = {
|
|
3363
|
+
create: (projectId, body, opts = {}) => this.call("create_bid", { id: projectId, ...body }, opts)
|
|
3364
|
+
};
|
|
3365
|
+
// Documents (estimates, bids, and anything else saved to a project) plus the
|
|
3366
|
+
// sharing switch. Documents are private to the organization until someone
|
|
3367
|
+
// enables sharing; enabling mints a secret and publishes a link readable by
|
|
3368
|
+
// anyone on the internet who has the URL — no sign-in, no expiry.
|
|
3369
|
+
//
|
|
3370
|
+
// Disabling revokes access but KEEPS the secret, so re-enabling later revives
|
|
3371
|
+
// the same URL. There is no rotate operation: a leaked link means the
|
|
3372
|
+
// document is burned, not that a disable/enable cycle gives you a fresh one.
|
|
3373
|
+
documents = {
|
|
3374
|
+
// Both list endpoints return a raw array (no { items, next_cursor }
|
|
3375
|
+
// envelope), like projects.list. They're here because setSharing needs a
|
|
3376
|
+
// document id and only documents you just generated hand you one directly.
|
|
3377
|
+
list: (query) => this.call("list_org_documents", query),
|
|
3378
|
+
listByProject: (projectId, query = {}) => this.call("list_documents", { id: projectId, ...query }),
|
|
3379
|
+
// Current sharing state rides the document body: `shareEnabled` and
|
|
3380
|
+
// `shareSecret` come back on GET.
|
|
3381
|
+
get: (id, query = {}) => this.call("get_document", { id, ...query }),
|
|
3382
|
+
// A document id belonging to another organization answers 404 exactly like
|
|
3383
|
+
// an id that doesn't exist.
|
|
3384
|
+
setSharing: (id, body, opts = {}) => this.call("set_document_sharing", { id, ...body }, opts)
|
|
3385
|
+
};
|
|
3386
|
+
// Vendor quotes. Request a quote from a vendor for a project, advance it to
|
|
3387
|
+
// `received` once the vendor responds (with priced line items), then record a
|
|
3388
|
+
// terminal accepted | rejected | expired decision. Each transition fires a
|
|
3389
|
+
// quote.* webhook. Note: there is no product UI for vendor quotes yet — these
|
|
3390
|
+
// are visible only via this API and the webhooks.
|
|
3391
|
+
quotes = {
|
|
3392
|
+
list: (projectId, query) => this.call("list_vendor_quotes", { id: projectId, ...query }),
|
|
3393
|
+
get: (id, query = {}) => this.call("get_vendor_quote", { id, ...query }),
|
|
3394
|
+
create: (projectId, body, opts = {}) => this.call("create_vendor_quote", { id: projectId, ...body }, opts),
|
|
3395
|
+
receive: (id, body, opts = {}) => this.call("receive_vendor_quote", { id, ...body }, opts),
|
|
3396
|
+
updateStatus: (id, body, opts = {}) => this.call("update_vendor_quote_status", { id, ...body }, opts)
|
|
340
3397
|
};
|
|
341
3398
|
help = {
|
|
342
|
-
search: (body) => this.
|
|
3399
|
+
search: (body) => this.call("search_help_articles", body)
|
|
343
3400
|
};
|
|
344
3401
|
webhooks = {
|
|
345
|
-
listEndpoints: (query) => this.
|
|
346
|
-
createEndpoint: (body) => this.
|
|
347
|
-
deleteEndpoint: (id, query) => this.
|
|
348
|
-
listDeliveries: (id, query) => this.
|
|
3402
|
+
listEndpoints: (query = {}) => this.call("list_webhook_endpoints", query),
|
|
3403
|
+
createEndpoint: (body) => this.call("create_webhook_endpoint", body),
|
|
3404
|
+
deleteEndpoint: (id, query = {}) => this.call("delete_webhook_endpoint", { id, ...query }),
|
|
3405
|
+
listDeliveries: (id, query = {}) => this.call("list_webhook_deliveries", { id, ...query }),
|
|
3406
|
+
/** Send a test event of one type to an endpoint, signed as usual, its body marked `"test": true`. */
|
|
3407
|
+
sendTestEvent: (id, body, opts = {}) => this.request({
|
|
3408
|
+
method: "POST",
|
|
3409
|
+
path: `/webhook_endpoints/${encodeURIComponent(id)}/test`,
|
|
3410
|
+
body: { ...body, organizationId: this.organizationFor(body.organizationId) },
|
|
3411
|
+
options: opts
|
|
3412
|
+
}),
|
|
3413
|
+
/** Send a delivery again, as a new delivery with the same event id and body. */
|
|
3414
|
+
resendDelivery: (deliveryId, body = {}, opts = {}) => this.request({
|
|
3415
|
+
method: "POST",
|
|
3416
|
+
path: `/webhook_deliveries/${encodeURIComponent(deliveryId)}/resend`,
|
|
3417
|
+
body: { organizationId: this.organizationFor(body.organizationId) },
|
|
3418
|
+
options: opts
|
|
3419
|
+
}),
|
|
3420
|
+
/** Receive the company's events over a stream, without a public endpoint (what `exayard listen` runs on). */
|
|
3421
|
+
listeners: new listeners_1.WebhookListeners({
|
|
3422
|
+
request: (opts) => this.request(opts),
|
|
3423
|
+
organizationId: (named) => this.organizationFor(named)
|
|
3424
|
+
}),
|
|
349
3425
|
/**
|
|
350
|
-
* Parse + verify an inbound webhook delivery
|
|
351
|
-
* on any signature failure — always catch and return 400 to let us retry.
|
|
3426
|
+
* Parse + verify an inbound webhook delivery: `constructEvent(rawBody, signatureHeader, secret)`. Throws
|
|
3427
|
+
* WebhookSignatureError on any signature failure — always catch and return 400 to let us retry.
|
|
352
3428
|
*/
|
|
353
3429
|
constructEvent: webhooks_1.constructWebhookEvent
|
|
354
3430
|
};
|
|
@@ -362,22 +3438,95 @@ var require_dist = __commonJS({
|
|
|
362
3438
|
"../sdk/dist/index.js"(exports2) {
|
|
363
3439
|
"use strict";
|
|
364
3440
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
365
|
-
exports2.WebhookSignatureError = exports2.constructWebhookEvent = exports2.ExayardError = exports2.Exayard = void 0;
|
|
3441
|
+
exports2.parseRetryAfter = exports2.DEFAULT_MAX_RETRIES = exports2.WebhookListeners = exports2.parseServerSentEvents = exports2.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports2.WEBHOOK_SIGNATURE_HEADER = exports2.WEBHOOK_EVENT_TYPES = exports2.WebhookSignatureError = exports2.signWebhookPayload = exports2.constructWebhookEvent = exports2.UnprocessableEntityError = exports2.RateLimitError = exports2.PermissionDeniedError = exports2.PaymentRequiredError = exports2.NotFoundError = exports2.InternalServerError = exports2.ExayardError = exports2.errorFromProblem = exports2.ConflictError = exports2.BadRequestError = exports2.AuthenticationError = exports2.APIConnectionError = exports2.isValidEndUser = exports2.END_USER_MAX_LENGTH = exports2.END_USER_HEADER = exports2.OPERATIONS = exports2.Exayard = void 0;
|
|
366
3442
|
var client_1 = require_client();
|
|
367
3443
|
Object.defineProperty(exports2, "Exayard", { enumerable: true, get: function() {
|
|
368
3444
|
return client_1.Exayard;
|
|
369
3445
|
} });
|
|
3446
|
+
var operations_1 = require_operations();
|
|
3447
|
+
Object.defineProperty(exports2, "OPERATIONS", { enumerable: true, get: function() {
|
|
3448
|
+
return operations_1.OPERATIONS;
|
|
3449
|
+
} });
|
|
3450
|
+
var options_1 = require_options();
|
|
3451
|
+
Object.defineProperty(exports2, "END_USER_HEADER", { enumerable: true, get: function() {
|
|
3452
|
+
return options_1.END_USER_HEADER;
|
|
3453
|
+
} });
|
|
3454
|
+
Object.defineProperty(exports2, "END_USER_MAX_LENGTH", { enumerable: true, get: function() {
|
|
3455
|
+
return options_1.END_USER_MAX_LENGTH;
|
|
3456
|
+
} });
|
|
3457
|
+
Object.defineProperty(exports2, "isValidEndUser", { enumerable: true, get: function() {
|
|
3458
|
+
return options_1.isValidEndUser;
|
|
3459
|
+
} });
|
|
370
3460
|
var error_1 = require_error();
|
|
3461
|
+
Object.defineProperty(exports2, "APIConnectionError", { enumerable: true, get: function() {
|
|
3462
|
+
return error_1.APIConnectionError;
|
|
3463
|
+
} });
|
|
3464
|
+
Object.defineProperty(exports2, "AuthenticationError", { enumerable: true, get: function() {
|
|
3465
|
+
return error_1.AuthenticationError;
|
|
3466
|
+
} });
|
|
3467
|
+
Object.defineProperty(exports2, "BadRequestError", { enumerable: true, get: function() {
|
|
3468
|
+
return error_1.BadRequestError;
|
|
3469
|
+
} });
|
|
3470
|
+
Object.defineProperty(exports2, "ConflictError", { enumerable: true, get: function() {
|
|
3471
|
+
return error_1.ConflictError;
|
|
3472
|
+
} });
|
|
3473
|
+
Object.defineProperty(exports2, "errorFromProblem", { enumerable: true, get: function() {
|
|
3474
|
+
return error_1.errorFromProblem;
|
|
3475
|
+
} });
|
|
371
3476
|
Object.defineProperty(exports2, "ExayardError", { enumerable: true, get: function() {
|
|
372
3477
|
return error_1.ExayardError;
|
|
373
3478
|
} });
|
|
3479
|
+
Object.defineProperty(exports2, "InternalServerError", { enumerable: true, get: function() {
|
|
3480
|
+
return error_1.InternalServerError;
|
|
3481
|
+
} });
|
|
3482
|
+
Object.defineProperty(exports2, "NotFoundError", { enumerable: true, get: function() {
|
|
3483
|
+
return error_1.NotFoundError;
|
|
3484
|
+
} });
|
|
3485
|
+
Object.defineProperty(exports2, "PaymentRequiredError", { enumerable: true, get: function() {
|
|
3486
|
+
return error_1.PaymentRequiredError;
|
|
3487
|
+
} });
|
|
3488
|
+
Object.defineProperty(exports2, "PermissionDeniedError", { enumerable: true, get: function() {
|
|
3489
|
+
return error_1.PermissionDeniedError;
|
|
3490
|
+
} });
|
|
3491
|
+
Object.defineProperty(exports2, "RateLimitError", { enumerable: true, get: function() {
|
|
3492
|
+
return error_1.RateLimitError;
|
|
3493
|
+
} });
|
|
3494
|
+
Object.defineProperty(exports2, "UnprocessableEntityError", { enumerable: true, get: function() {
|
|
3495
|
+
return error_1.UnprocessableEntityError;
|
|
3496
|
+
} });
|
|
374
3497
|
var webhooks_1 = require_webhooks();
|
|
375
3498
|
Object.defineProperty(exports2, "constructWebhookEvent", { enumerable: true, get: function() {
|
|
376
3499
|
return webhooks_1.constructWebhookEvent;
|
|
377
3500
|
} });
|
|
3501
|
+
Object.defineProperty(exports2, "signWebhookPayload", { enumerable: true, get: function() {
|
|
3502
|
+
return webhooks_1.signWebhookPayload;
|
|
3503
|
+
} });
|
|
378
3504
|
Object.defineProperty(exports2, "WebhookSignatureError", { enumerable: true, get: function() {
|
|
379
3505
|
return webhooks_1.WebhookSignatureError;
|
|
380
3506
|
} });
|
|
3507
|
+
Object.defineProperty(exports2, "WEBHOOK_EVENT_TYPES", { enumerable: true, get: function() {
|
|
3508
|
+
return webhooks_1.WEBHOOK_EVENT_TYPES;
|
|
3509
|
+
} });
|
|
3510
|
+
Object.defineProperty(exports2, "WEBHOOK_SIGNATURE_HEADER", { enumerable: true, get: function() {
|
|
3511
|
+
return webhooks_1.WEBHOOK_SIGNATURE_HEADER;
|
|
3512
|
+
} });
|
|
3513
|
+
Object.defineProperty(exports2, "DEFAULT_WEBHOOK_TOLERANCE_SECONDS", { enumerable: true, get: function() {
|
|
3514
|
+
return webhooks_1.DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
3515
|
+
} });
|
|
3516
|
+
var listeners_1 = require_listeners();
|
|
3517
|
+
Object.defineProperty(exports2, "parseServerSentEvents", { enumerable: true, get: function() {
|
|
3518
|
+
return listeners_1.parseServerSentEvents;
|
|
3519
|
+
} });
|
|
3520
|
+
Object.defineProperty(exports2, "WebhookListeners", { enumerable: true, get: function() {
|
|
3521
|
+
return listeners_1.WebhookListeners;
|
|
3522
|
+
} });
|
|
3523
|
+
var retry_1 = require_retry();
|
|
3524
|
+
Object.defineProperty(exports2, "DEFAULT_MAX_RETRIES", { enumerable: true, get: function() {
|
|
3525
|
+
return retry_1.DEFAULT_MAX_RETRIES;
|
|
3526
|
+
} });
|
|
3527
|
+
Object.defineProperty(exports2, "parseRetryAfter", { enumerable: true, get: function() {
|
|
3528
|
+
return retry_1.parseRetryAfter;
|
|
3529
|
+
} });
|
|
381
3530
|
}
|
|
382
3531
|
});
|
|
383
3532
|
|
|
@@ -392,13 +3541,22 @@ __export(index_exports, {
|
|
|
392
3541
|
module.exports = __toCommonJS(index_exports);
|
|
393
3542
|
|
|
394
3543
|
// src/commands.ts
|
|
395
|
-
var
|
|
3544
|
+
var import_sdk3 = __toESM(require_dist());
|
|
396
3545
|
|
|
397
3546
|
// src/config.ts
|
|
398
3547
|
var import_node_fs = require("fs");
|
|
399
3548
|
var import_node_os = require("os");
|
|
400
3549
|
var import_node_path = require("path");
|
|
401
3550
|
var configPath = () => (0, import_node_path.join)((0, import_node_os.homedir)(), ".exayard", "config.json");
|
|
3551
|
+
var readConfigFile = () => {
|
|
3552
|
+
const path = configPath();
|
|
3553
|
+
if (!(0, import_node_fs.existsSync)(path)) return {};
|
|
3554
|
+
try {
|
|
3555
|
+
return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
3556
|
+
} catch {
|
|
3557
|
+
return {};
|
|
3558
|
+
}
|
|
3559
|
+
};
|
|
402
3560
|
var readConfig = () => {
|
|
403
3561
|
const fromEnv = {
|
|
404
3562
|
apiKey: process.env.EXAYARD_API_KEY,
|
|
@@ -407,37 +3565,341 @@ var readConfig = () => {
|
|
|
407
3565
|
};
|
|
408
3566
|
const path = configPath();
|
|
409
3567
|
if (!(0, import_node_fs.existsSync)(path)) return fromEnv;
|
|
410
|
-
|
|
411
|
-
const raw = JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
412
|
-
return { ...raw, ...Object.fromEntries(Object.entries(fromEnv).filter(([, v]) => v !== void 0 && v !== "")) };
|
|
413
|
-
} catch {
|
|
414
|
-
return fromEnv;
|
|
415
|
-
}
|
|
3568
|
+
return { ...readConfigFile(), ...Object.fromEntries(Object.entries(fromEnv).filter(([, v]) => v !== void 0 && v !== "")) };
|
|
416
3569
|
};
|
|
417
3570
|
var writeConfig = (config) => {
|
|
418
3571
|
const path = configPath();
|
|
419
|
-
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
|
|
420
|
-
(0, import_node_fs.writeFileSync)(path, JSON.stringify(config, null, 2));
|
|
3572
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true, mode: 448 });
|
|
421
3573
|
try {
|
|
422
|
-
(0, import_node_fs.chmodSync)(path,
|
|
3574
|
+
(0, import_node_fs.chmodSync)((0, import_node_path.dirname)(path), 448);
|
|
423
3575
|
} catch {
|
|
424
3576
|
}
|
|
3577
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
3578
|
+
(0, import_node_fs.writeFileSync)(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
3579
|
+
try {
|
|
3580
|
+
(0, import_node_fs.chmodSync)(tmp, 384);
|
|
3581
|
+
} catch {
|
|
3582
|
+
}
|
|
3583
|
+
(0, import_node_fs.renameSync)(tmp, path);
|
|
3584
|
+
};
|
|
3585
|
+
var saveOAuthSession = (session) => {
|
|
3586
|
+
writeConfig({ ...readConfigFile(), oauth: session });
|
|
425
3587
|
};
|
|
426
3588
|
var clearConfig = () => {
|
|
427
3589
|
try {
|
|
428
|
-
(
|
|
3590
|
+
writeConfig({});
|
|
429
3591
|
} catch {
|
|
430
3592
|
}
|
|
431
3593
|
};
|
|
432
3594
|
|
|
3595
|
+
// src/listen.ts
|
|
3596
|
+
var import_node_os2 = require("os");
|
|
3597
|
+
var import_sdk = __toESM(require_dist());
|
|
3598
|
+
var organizationOf = (payload) => {
|
|
3599
|
+
try {
|
|
3600
|
+
const parsed = JSON.parse(payload);
|
|
3601
|
+
return typeof parsed.organizationId === "string" ? parsed.organizationId : void 0;
|
|
3602
|
+
} catch {
|
|
3603
|
+
return void 0;
|
|
3604
|
+
}
|
|
3605
|
+
};
|
|
3606
|
+
var forwardEvent = async (event, forwardTo, secret, fetchImpl = fetch) => {
|
|
3607
|
+
const organizationId = organizationOf(event.payload);
|
|
3608
|
+
try {
|
|
3609
|
+
const res = await fetchImpl(forwardTo, {
|
|
3610
|
+
method: "POST",
|
|
3611
|
+
headers: {
|
|
3612
|
+
"Content-Type": "application/json",
|
|
3613
|
+
"User-Agent": "Exayard-CLI-Listen",
|
|
3614
|
+
"Exayard-Signature": await (0, import_sdk.signWebhookPayload)(event.payload, secret),
|
|
3615
|
+
"Exayard-Event-Id": event.eventId,
|
|
3616
|
+
"Exayard-Event-Type": event.type,
|
|
3617
|
+
...organizationId ? { "Exayard-Organization-Id": organizationId } : {}
|
|
3618
|
+
},
|
|
3619
|
+
body: event.payload,
|
|
3620
|
+
redirect: "manual"
|
|
3621
|
+
});
|
|
3622
|
+
await res.body?.cancel().catch(() => void 0);
|
|
3623
|
+
return { status: res.status };
|
|
3624
|
+
} catch (err) {
|
|
3625
|
+
return { error: err.message || "request failed" };
|
|
3626
|
+
}
|
|
3627
|
+
};
|
|
3628
|
+
var runListen = async (exa, opts) => {
|
|
3629
|
+
const listener = await exa.webhooks.listeners.start({
|
|
3630
|
+
organizationId: opts.organizationId,
|
|
3631
|
+
...opts.events && opts.events.length > 0 ? { events: opts.events } : {},
|
|
3632
|
+
name: (opts.name ?? (0, import_node_os2.hostname)()).slice(0, 100)
|
|
3633
|
+
});
|
|
3634
|
+
const target = opts.forwardTo ? ` and forwarding to ${opts.forwardTo}` : "";
|
|
3635
|
+
if (opts.json) opts.write(JSON.stringify({ ready: true, listenerId: listener.id, secret: listener.secret, events: listener.events }));
|
|
3636
|
+
else {
|
|
3637
|
+
opts.writeErr(`Ready. Listening for ${listener.events.join(", ")}${target}. Press Ctrl-C to stop.`);
|
|
3638
|
+
opts.writeErr(`Your webhook signing secret is ${listener.secret}`);
|
|
3639
|
+
}
|
|
3640
|
+
let code = 0;
|
|
3641
|
+
try {
|
|
3642
|
+
for await (const event of exa.webhooks.listeners.events(listener.id, { organizationId: opts.organizationId, signal: opts.signal })) {
|
|
3643
|
+
if (!opts.forwardTo) {
|
|
3644
|
+
opts.write(opts.json ? JSON.stringify({ seq: event.seq, type: event.type, eventId: event.eventId, payload: event.payload }) : event.payload);
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
if (!opts.json) opts.writeErr(`--> ${event.type}${event.test ? " (test)" : ""} [${event.eventId}]`);
|
|
3648
|
+
const result = await forwardEvent(event, opts.forwardTo, listener.secret, opts.fetch);
|
|
3649
|
+
if (opts.json) opts.write(JSON.stringify({ seq: event.seq, type: event.type, eventId: event.eventId, ...result }));
|
|
3650
|
+
else if ("status" in result) opts.writeErr(`<-- [${result.status}] POST ${opts.forwardTo}`);
|
|
3651
|
+
else opts.writeErr(`<-- failed POST ${opts.forwardTo}: ${result.error}`);
|
|
3652
|
+
}
|
|
3653
|
+
} catch (err) {
|
|
3654
|
+
if (!opts.signal.aborted) {
|
|
3655
|
+
opts.writeErr(`The listener stopped: ${err.message}`);
|
|
3656
|
+
code = 1;
|
|
3657
|
+
}
|
|
3658
|
+
} finally {
|
|
3659
|
+
await exa.webhooks.listeners.stop(listener.id, { organizationId: opts.organizationId }, { maxRetries: 1 }).catch(() => void 0);
|
|
3660
|
+
}
|
|
3661
|
+
if (!opts.json && code === 0) opts.writeErr("Stopped.");
|
|
3662
|
+
return code;
|
|
3663
|
+
};
|
|
3664
|
+
|
|
3665
|
+
// src/oauth.ts
|
|
3666
|
+
var import_node_crypto = require("crypto");
|
|
3667
|
+
var import_node_child_process = require("child_process");
|
|
3668
|
+
var import_node_http = require("http");
|
|
3669
|
+
var OAUTH_SCOPES = ["openid", "profile", "email", "offline_access"];
|
|
3670
|
+
var REFRESH_MARGIN_MS = 6e4;
|
|
3671
|
+
var OAuthLoginError = class extends Error {
|
|
3672
|
+
constructor(message) {
|
|
3673
|
+
super(message);
|
|
3674
|
+
this.name = "OAuthLoginError";
|
|
3675
|
+
}
|
|
3676
|
+
};
|
|
3677
|
+
var base64url = (bytes) => bytes.toString("base64url");
|
|
3678
|
+
var pkcePair = () => {
|
|
3679
|
+
const verifier = base64url((0, import_node_crypto.randomBytes)(32));
|
|
3680
|
+
return { verifier, challenge: base64url((0, import_node_crypto.createHash)("sha256").update(verifier).digest()) };
|
|
3681
|
+
};
|
|
3682
|
+
var getJson = async (fetchImpl, url, what) => {
|
|
3683
|
+
let res;
|
|
3684
|
+
try {
|
|
3685
|
+
res = await fetchImpl(url, { headers: { Accept: "application/json" } });
|
|
3686
|
+
} catch (err) {
|
|
3687
|
+
throw new OAuthLoginError(`Could not reach ${url} (${what}): ${err.message}`);
|
|
3688
|
+
}
|
|
3689
|
+
if (!res.ok) throw new OAuthLoginError(`${url} (${what}) answered ${res.status}.`);
|
|
3690
|
+
return await res.json();
|
|
3691
|
+
};
|
|
3692
|
+
var stringField = (obj, key, where) => {
|
|
3693
|
+
const value = obj[key];
|
|
3694
|
+
if (typeof value !== "string" || value === "") throw new OAuthLoginError(`${where} has no ${key}.`);
|
|
3695
|
+
return value;
|
|
3696
|
+
};
|
|
3697
|
+
var discoverOAuth = async (apiBaseUrl, fetchImpl) => {
|
|
3698
|
+
const origin = new URL(apiBaseUrl).origin;
|
|
3699
|
+
const prm = await getJson(fetchImpl, `${origin}/.well-known/oauth-protected-resource`, "protected resource metadata");
|
|
3700
|
+
const servers = prm.authorization_servers;
|
|
3701
|
+
const issuer = Array.isArray(servers) && typeof servers[0] === "string" ? servers[0] : void 0;
|
|
3702
|
+
if (!issuer) throw new OAuthLoginError(`${origin} names no authorization server.`);
|
|
3703
|
+
const asUrl = `${issuer.replace(/\/$/, "")}/.well-known/oauth-authorization-server`;
|
|
3704
|
+
const meta = await getJson(fetchImpl, asUrl, "authorization server metadata");
|
|
3705
|
+
const methods = meta.code_challenge_methods_supported;
|
|
3706
|
+
if (Array.isArray(methods) && !methods.includes("S256")) throw new OAuthLoginError(`${issuer} does not support PKCE S256.`);
|
|
3707
|
+
return {
|
|
3708
|
+
resource: typeof prm.resource === "string" && prm.resource !== "" ? prm.resource : origin,
|
|
3709
|
+
authorizationEndpoint: stringField(meta, "authorization_endpoint", asUrl),
|
|
3710
|
+
tokenEndpoint: stringField(meta, "token_endpoint", asUrl),
|
|
3711
|
+
registrationEndpoint: stringField(meta, "registration_endpoint", asUrl)
|
|
3712
|
+
};
|
|
3713
|
+
};
|
|
3714
|
+
var registerClient = async (registrationEndpoint, redirectUri, fetchImpl) => {
|
|
3715
|
+
const res = await fetchImpl(registrationEndpoint, {
|
|
3716
|
+
method: "POST",
|
|
3717
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
3718
|
+
body: JSON.stringify({
|
|
3719
|
+
client_name: "Exayard CLI",
|
|
3720
|
+
redirect_uris: [redirectUri],
|
|
3721
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
3722
|
+
response_types: ["code"],
|
|
3723
|
+
token_endpoint_auth_method: "none",
|
|
3724
|
+
scope: OAUTH_SCOPES.join(" ")
|
|
3725
|
+
})
|
|
3726
|
+
});
|
|
3727
|
+
const body = await res.json().catch(() => ({}));
|
|
3728
|
+
if (!res.ok) throw new OAuthLoginError(`Registering the CLI failed (${res.status}): ${oauthErrorText(body)}`);
|
|
3729
|
+
return stringField(body, "client_id", "The registration answer");
|
|
3730
|
+
};
|
|
3731
|
+
var oauthErrorText = (body) => [body.error, body.error_description].filter((v) => typeof v === "string" && v !== "").join(": ") || "no detail";
|
|
3732
|
+
var tokenRequest = async (tokenEndpoint, form, fetchImpl, now) => {
|
|
3733
|
+
const params = new URLSearchParams();
|
|
3734
|
+
for (const [k, v] of Object.entries(form)) if (v !== void 0) params.set(k, v);
|
|
3735
|
+
const res = await fetchImpl(tokenEndpoint, {
|
|
3736
|
+
method: "POST",
|
|
3737
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
3738
|
+
body: params.toString()
|
|
3739
|
+
});
|
|
3740
|
+
const body = await res.json().catch(() => ({}));
|
|
3741
|
+
if (!res.ok) throw new OAuthLoginError(`The token request failed (${res.status}): ${oauthErrorText(body)}`);
|
|
3742
|
+
const accessToken = stringField(body, "access_token", "The token answer");
|
|
3743
|
+
const expiresIn = typeof body.expires_in === "number" ? body.expires_in : Number(body.expires_in);
|
|
3744
|
+
return {
|
|
3745
|
+
accessToken,
|
|
3746
|
+
...typeof body.refresh_token === "string" && body.refresh_token !== "" ? { refreshToken: body.refresh_token } : {},
|
|
3747
|
+
...Number.isFinite(expiresIn) && expiresIn > 0 ? { expiresAt: now() + expiresIn * 1e3 } : {}
|
|
3748
|
+
};
|
|
3749
|
+
};
|
|
3750
|
+
var CALLBACK_PAGE = (title) => `<!doctype html><html><head><meta charset="utf-8"><title>Exayard CLI</title></head><body style="font-family:system-ui,sans-serif;padding:3rem"><h1>${title}</h1><p>You can close this tab and go back to the terminal.</p></body></html>`;
|
|
3751
|
+
var startLoopback = async (port, signal) => {
|
|
3752
|
+
let resolveCallback = () => void 0;
|
|
3753
|
+
let rejectCallback = () => void 0;
|
|
3754
|
+
const callback = new Promise((resolve, reject) => {
|
|
3755
|
+
resolveCallback = resolve;
|
|
3756
|
+
rejectCallback = reject;
|
|
3757
|
+
});
|
|
3758
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
3759
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
3760
|
+
if (url.pathname !== "/callback") {
|
|
3761
|
+
res.writeHead(404, { "Content-Type": "text/plain" }).end("Not found");
|
|
3762
|
+
return;
|
|
3763
|
+
}
|
|
3764
|
+
const failed = url.searchParams.has("error") || !url.searchParams.has("code");
|
|
3765
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(CALLBACK_PAGE(failed ? "Sign-in did not finish" : "You are signed in"));
|
|
3766
|
+
resolveCallback(url.searchParams);
|
|
3767
|
+
});
|
|
3768
|
+
await new Promise((resolve, reject) => {
|
|
3769
|
+
server.once("error", reject);
|
|
3770
|
+
server.listen(port, "127.0.0.1", () => resolve());
|
|
3771
|
+
});
|
|
3772
|
+
const close = () => {
|
|
3773
|
+
server.closeAllConnections();
|
|
3774
|
+
server.close();
|
|
3775
|
+
};
|
|
3776
|
+
const onAbort = () => rejectCallback(new OAuthLoginError("Sign-in cancelled."));
|
|
3777
|
+
if (signal?.aborted) onAbort();
|
|
3778
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3779
|
+
const { port: bound } = server.address();
|
|
3780
|
+
return { redirectUri: `http://127.0.0.1:${bound}/callback`, callback, close };
|
|
3781
|
+
};
|
|
3782
|
+
var openInBrowser = (url) => {
|
|
3783
|
+
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", '""', url.replace(/&/g, "^&")]] : ["xdg-open", [url]];
|
|
3784
|
+
try {
|
|
3785
|
+
const child = (0, import_node_child_process.spawn)(cmd, args, { stdio: "ignore", detached: true });
|
|
3786
|
+
child.on("error", () => void 0);
|
|
3787
|
+
child.unref();
|
|
3788
|
+
} catch {
|
|
3789
|
+
}
|
|
3790
|
+
};
|
|
3791
|
+
var browserLogin = async (opts) => {
|
|
3792
|
+
const fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
3793
|
+
const now = opts.now ?? Date.now;
|
|
3794
|
+
const discovery = await discoverOAuth(opts.apiBaseUrl, fetchImpl);
|
|
3795
|
+
const loopback = await startLoopback(opts.port ?? 0, opts.signal);
|
|
3796
|
+
try {
|
|
3797
|
+
const clientId = await registerClient(discovery.registrationEndpoint, loopback.redirectUri, fetchImpl);
|
|
3798
|
+
const { verifier, challenge } = pkcePair();
|
|
3799
|
+
const state = base64url((0, import_node_crypto.randomBytes)(16));
|
|
3800
|
+
const authorize = new URL(discovery.authorizationEndpoint);
|
|
3801
|
+
authorize.search = new URLSearchParams({
|
|
3802
|
+
response_type: "code",
|
|
3803
|
+
client_id: clientId,
|
|
3804
|
+
redirect_uri: loopback.redirectUri,
|
|
3805
|
+
scope: OAUTH_SCOPES.join(" "),
|
|
3806
|
+
state,
|
|
3807
|
+
code_challenge: challenge,
|
|
3808
|
+
code_challenge_method: "S256",
|
|
3809
|
+
resource: discovery.resource
|
|
3810
|
+
}).toString();
|
|
3811
|
+
opts.writeErr("Opening your browser to sign in. If it does not open, visit:");
|
|
3812
|
+
opts.writeErr(authorize.toString());
|
|
3813
|
+
await Promise.resolve(opts.openBrowser ? opts.openBrowser(authorize.toString()) : openInBrowser(authorize.toString())).catch(() => void 0);
|
|
3814
|
+
const query = await loopback.callback;
|
|
3815
|
+
if (query.get("error")) {
|
|
3816
|
+
throw new OAuthLoginError(`Sign-in refused: ${[query.get("error"), query.get("error_description")].filter(Boolean).join(": ")}`);
|
|
3817
|
+
}
|
|
3818
|
+
if (query.get("state") !== state) throw new OAuthLoginError("Sign-in answer did not match this login (state). Run `exayard login` again.");
|
|
3819
|
+
const code = query.get("code");
|
|
3820
|
+
if (!code) throw new OAuthLoginError("Sign-in answer carried no code.");
|
|
3821
|
+
const tokens = await tokenRequest(
|
|
3822
|
+
discovery.tokenEndpoint,
|
|
3823
|
+
{
|
|
3824
|
+
grant_type: "authorization_code",
|
|
3825
|
+
code,
|
|
3826
|
+
redirect_uri: loopback.redirectUri,
|
|
3827
|
+
client_id: clientId,
|
|
3828
|
+
code_verifier: verifier,
|
|
3829
|
+
resource: discovery.resource
|
|
3830
|
+
},
|
|
3831
|
+
fetchImpl,
|
|
3832
|
+
now
|
|
3833
|
+
);
|
|
3834
|
+
return { ...tokens, clientId, tokenEndpoint: discovery.tokenEndpoint, redirectUri: loopback.redirectUri, resource: discovery.resource };
|
|
3835
|
+
} finally {
|
|
3836
|
+
loopback.close();
|
|
3837
|
+
}
|
|
3838
|
+
};
|
|
3839
|
+
var refreshSession = async (session, fetchImpl, now = Date.now) => {
|
|
3840
|
+
if (!session.refreshToken) throw new OAuthLoginError("Your sign-in ended. Run `exayard login` again.");
|
|
3841
|
+
let tokens;
|
|
3842
|
+
try {
|
|
3843
|
+
tokens = await tokenRequest(
|
|
3844
|
+
session.tokenEndpoint,
|
|
3845
|
+
{ grant_type: "refresh_token", refresh_token: session.refreshToken, client_id: session.clientId, resource: session.resource },
|
|
3846
|
+
fetchImpl,
|
|
3847
|
+
now
|
|
3848
|
+
);
|
|
3849
|
+
} catch (err) {
|
|
3850
|
+
throw new OAuthLoginError(`Your sign-in could not be renewed (${err.message}). Run \`exayard login\` again.`);
|
|
3851
|
+
}
|
|
3852
|
+
return { ...session, ...tokens, refreshToken: tokens.refreshToken ?? session.refreshToken };
|
|
3853
|
+
};
|
|
3854
|
+
var needsRefresh = (session, now) => session.expiresAt !== void 0 && session.expiresAt - now < REFRESH_MARGIN_MS;
|
|
3855
|
+
var sessionFetch = (initial, save, fetchImpl = (input, init) => fetch(input, init), now = Date.now) => {
|
|
3856
|
+
let session = initial;
|
|
3857
|
+
let refreshing = null;
|
|
3858
|
+
const current = async () => {
|
|
3859
|
+
if (!needsRefresh(session, now())) return session;
|
|
3860
|
+
refreshing ??= refreshSession(session, fetchImpl, now).then((next) => {
|
|
3861
|
+
session = next;
|
|
3862
|
+
save(next);
|
|
3863
|
+
return next;
|
|
3864
|
+
}).finally(() => {
|
|
3865
|
+
refreshing = null;
|
|
3866
|
+
});
|
|
3867
|
+
return refreshing;
|
|
3868
|
+
};
|
|
3869
|
+
return async (input, init) => {
|
|
3870
|
+
const headers = new Headers(init?.headers);
|
|
3871
|
+
if (!headers.has("Authorization")) return fetchImpl(input, init);
|
|
3872
|
+
let accessToken;
|
|
3873
|
+
try {
|
|
3874
|
+
;
|
|
3875
|
+
({ accessToken } = await current());
|
|
3876
|
+
} catch (err) {
|
|
3877
|
+
return new Response(
|
|
3878
|
+
JSON.stringify({ type: "about:blank", title: "Sign-in ended", status: 401, code: "unauthenticated", detail: err.message }),
|
|
3879
|
+
{ status: 401, headers: { "Content-Type": "application/problem+json" } }
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
3883
|
+
return fetchImpl(input, { ...init, headers });
|
|
3884
|
+
};
|
|
3885
|
+
};
|
|
3886
|
+
|
|
3887
|
+
// src/operations.ts
|
|
3888
|
+
var import_sdk2 = __toESM(require_dist());
|
|
3889
|
+
var kebab = (name) => name.replace(/_/g, "-");
|
|
3890
|
+
var BY_COMMAND = new Map(
|
|
3891
|
+
Object.keys(import_sdk2.OPERATIONS).map((name) => [kebab(name), name])
|
|
3892
|
+
);
|
|
3893
|
+
|
|
433
3894
|
// src/commands.ts
|
|
434
3895
|
var makeClient = (ctx) => {
|
|
435
3896
|
const cfg = readConfig();
|
|
436
3897
|
const apiKey = (typeof ctx.flags["api-key"] === "string" ? ctx.flags["api-key"] : void 0) ?? cfg.apiKey;
|
|
437
|
-
if (
|
|
438
|
-
|
|
3898
|
+
if (apiKey) return new import_sdk3.Exayard({ apiKey, baseUrl: cfg.baseUrl });
|
|
3899
|
+
if (cfg.oauth) {
|
|
3900
|
+
return new import_sdk3.Exayard({ bearerToken: cfg.oauth.accessToken, baseUrl: cfg.baseUrl, fetch: sessionFetch(cfg.oauth, saveOAuthSession) });
|
|
439
3901
|
}
|
|
440
|
-
|
|
3902
|
+
throw new Error("Not logged in. Run `exayard login` or set EXAYARD_API_KEY.");
|
|
441
3903
|
};
|
|
442
3904
|
var renderProject = (p) => {
|
|
443
3905
|
const id = String(p._id ?? p.id ?? "???");
|
|
@@ -446,29 +3908,60 @@ var renderProject = (p) => {
|
|
|
446
3908
|
return `${id} ${status.padEnd(10)} ${name}`;
|
|
447
3909
|
};
|
|
448
3910
|
var loginCommand = async (ctx) => {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
ctx.writeErr("Paste an API key from https://exayard.com/settings/api-keys:");
|
|
452
|
-
ctx.writeErr(" exayard login --api-key sk_live_...");
|
|
3911
|
+
if (ctx.flags["api-key"] === true) {
|
|
3912
|
+
ctx.writeErr("Usage: exayard login --api-key exa_live_... (or just `exayard login` to sign in in the browser)");
|
|
453
3913
|
return 2;
|
|
454
3914
|
}
|
|
3915
|
+
const pasted = typeof ctx.flags["api-key"] === "string" ? ctx.flags["api-key"] : void 0;
|
|
455
3916
|
const orgId = typeof ctx.flags.org === "string" ? ctx.flags.org : void 0;
|
|
456
3917
|
const baseUrl = (typeof ctx.flags["base-url"] === "string" ? ctx.flags["base-url"] : void 0) ?? readConfig().baseUrl;
|
|
3918
|
+
if (!pasted) return await browserLoginCommand(ctx, baseUrl, orgId);
|
|
457
3919
|
writeConfig({ apiKey: pasted, organizationId: orgId, baseUrl });
|
|
458
|
-
const exa = new
|
|
3920
|
+
const exa = new import_sdk3.Exayard({ apiKey: pasted, baseUrl });
|
|
459
3921
|
await exa.me.get();
|
|
460
3922
|
ctx.write(ctx.json ? JSON.stringify({ ok: true }) : "Logged in. Token saved to ~/.exayard/config.json.");
|
|
461
3923
|
return 0;
|
|
462
3924
|
};
|
|
3925
|
+
var DEFAULT_BASE_URL = "https://api.exayard.com/v1";
|
|
3926
|
+
var browserLoginCommand = async (ctx, baseUrl, orgId) => {
|
|
3927
|
+
const ac = new AbortController();
|
|
3928
|
+
const onSigint = () => ac.abort();
|
|
3929
|
+
process.once("SIGINT", onSigint);
|
|
3930
|
+
let session;
|
|
3931
|
+
try {
|
|
3932
|
+
session = await browserLogin({ apiBaseUrl: baseUrl ?? DEFAULT_BASE_URL, writeErr: ctx.writeErr, openBrowser: ctx.openBrowser, signal: ac.signal });
|
|
3933
|
+
} catch (err) {
|
|
3934
|
+
if (!(err instanceof OAuthLoginError)) throw err;
|
|
3935
|
+
ctx.writeErr(err.message);
|
|
3936
|
+
return 1;
|
|
3937
|
+
} finally {
|
|
3938
|
+
process.off("SIGINT", onSigint);
|
|
3939
|
+
}
|
|
3940
|
+
const organizationId = orgId ?? readConfigFile().organizationId;
|
|
3941
|
+
writeConfig({ ...baseUrl ? { baseUrl } : {}, ...organizationId ? { organizationId } : {}, oauth: session });
|
|
3942
|
+
const exa = new import_sdk3.Exayard({ bearerToken: session.accessToken, baseUrl, fetch: sessionFetch(session, saveOAuthSession) });
|
|
3943
|
+
const me = await exa.me.get();
|
|
3944
|
+
const companies = me.memberships.map((m) => m.orgId);
|
|
3945
|
+
let saved = organizationId;
|
|
3946
|
+
if (!saved && companies.length === 1) {
|
|
3947
|
+
saved = companies[0];
|
|
3948
|
+
writeConfig({ ...readConfigFile(), organizationId: saved });
|
|
3949
|
+
}
|
|
3950
|
+
if (ctx.json) {
|
|
3951
|
+
ctx.write(JSON.stringify({ ok: true, organizationId: saved ?? null, memberships: me.memberships }));
|
|
3952
|
+
} else {
|
|
3953
|
+
ctx.write(`Logged in${saved ? ` (company ${saved})` : ""}. Saved to ~/.exayard/config.json.`);
|
|
3954
|
+
if (!saved && companies.length > 1) {
|
|
3955
|
+
ctx.writeErr("You belong to several companies. Pass --org <id> on calls, or save one with `exayard login --org <id>`:");
|
|
3956
|
+
for (const m of me.memberships) ctx.writeErr(` ${m.orgId}${m.role ? ` ${m.role}` : ""}`);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
if (process.env.EXAYARD_API_KEY) ctx.writeErr("EXAYARD_API_KEY is set; calls use that key, not this sign-in, while it is.");
|
|
3960
|
+
return 0;
|
|
3961
|
+
};
|
|
463
3962
|
var projectsListCommand = async (ctx) => {
|
|
464
3963
|
const exa = makeClient(ctx);
|
|
465
|
-
const
|
|
466
|
-
const organizationId = (typeof ctx.flags.org === "string" ? ctx.flags.org : void 0) ?? cfg.organizationId;
|
|
467
|
-
if (!organizationId) {
|
|
468
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
469
|
-
return 2;
|
|
470
|
-
}
|
|
471
|
-
const res = await exa.projects.list({ organizationId });
|
|
3964
|
+
const res = await exa.projects.list({ organizationId: resolveOrgId(ctx) });
|
|
472
3965
|
if (ctx.json) {
|
|
473
3966
|
ctx.write(JSON.stringify(res, null, 2));
|
|
474
3967
|
} else {
|
|
@@ -493,15 +3986,11 @@ var assessmentsRunCommand = async (ctx) => {
|
|
|
493
3986
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
494
3987
|
if (!projectId) {
|
|
495
3988
|
ctx.writeErr(
|
|
496
|
-
`Usage: exayard assessments run <project_id> --
|
|
3989
|
+
`Usage: exayard assessments run <project_id> --pages <id1,id2> --elements '[{"id","name","category","hexColor"}]' [--org <org_id>]`
|
|
497
3990
|
);
|
|
498
3991
|
return 2;
|
|
499
3992
|
}
|
|
500
3993
|
const organizationId = resolveOrgId(ctx);
|
|
501
|
-
if (!organizationId) {
|
|
502
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
503
|
-
return 2;
|
|
504
|
-
}
|
|
505
3994
|
const pageIds = typeof ctx.flags.pages === "string" ? ctx.flags.pages.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
506
3995
|
if (pageIds.length === 0) {
|
|
507
3996
|
ctx.writeErr("--pages <pageId1,pageId2> required.");
|
|
@@ -521,12 +4010,7 @@ var filesUploadCommand = async (ctx) => {
|
|
|
521
4010
|
const path = typeof ctx.flags.file === "string" ? ctx.flags.file : ctx.positional[0];
|
|
522
4011
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : void 0;
|
|
523
4012
|
if (!path || !projectId) {
|
|
524
|
-
ctx.writeErr("Usage: exayard files upload <path> --project <projectId>
|
|
525
|
-
return 2;
|
|
526
|
-
}
|
|
527
|
-
const organizationId = resolveOrgId(ctx);
|
|
528
|
-
if (!organizationId) {
|
|
529
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
4013
|
+
ctx.writeErr("Usage: exayard files upload <path> --project <projectId>");
|
|
530
4014
|
return 2;
|
|
531
4015
|
}
|
|
532
4016
|
const { readFileSync: readFileSync2 } = await import("fs");
|
|
@@ -536,7 +4020,6 @@ var filesUploadCommand = async (ctx) => {
|
|
|
536
4020
|
const mimeType = ext === ".pdf" ? "application/pdf" : ext === ".png" ? "image/png" : ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : "application/octet-stream";
|
|
537
4021
|
const exa = makeClient(ctx);
|
|
538
4022
|
const res = await exa.files.upload({
|
|
539
|
-
organizationId,
|
|
540
4023
|
projectId,
|
|
541
4024
|
filename: basename(path),
|
|
542
4025
|
mimeType,
|
|
@@ -550,14 +4033,10 @@ var filesUploadCommand = async (ctx) => {
|
|
|
550
4033
|
var pagesListCommand = async (ctx) => {
|
|
551
4034
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
552
4035
|
if (!projectId) {
|
|
553
|
-
ctx.writeErr("Usage: exayard pages list <project_id> [--org
|
|
4036
|
+
ctx.writeErr("Usage: exayard pages list <project_id> [--org <org_id>]");
|
|
554
4037
|
return 2;
|
|
555
4038
|
}
|
|
556
4039
|
const organizationId = resolveOrgId(ctx);
|
|
557
|
-
if (!organizationId) {
|
|
558
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
559
|
-
return 2;
|
|
560
|
-
}
|
|
561
4040
|
const exa = makeClient(ctx);
|
|
562
4041
|
const res = await exa.pages.list(projectId, { organizationId });
|
|
563
4042
|
ctx.write(JSON.stringify(res, null, 2));
|
|
@@ -566,14 +4045,10 @@ var pagesListCommand = async (ctx) => {
|
|
|
566
4045
|
var assessmentsProposeCommand = async (ctx) => {
|
|
567
4046
|
const projectId = typeof ctx.flags.project === "string" ? ctx.flags.project : ctx.positional[0];
|
|
568
4047
|
if (!projectId) {
|
|
569
|
-
ctx.writeErr('Usage: exayard assessments propose <project_id> --prompt "..." [--pages id1,id2] [--org
|
|
4048
|
+
ctx.writeErr('Usage: exayard assessments propose <project_id> --prompt "..." [--pages id1,id2] [--org <org_id>]');
|
|
570
4049
|
return 2;
|
|
571
4050
|
}
|
|
572
4051
|
const organizationId = resolveOrgId(ctx);
|
|
573
|
-
if (!organizationId) {
|
|
574
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
575
|
-
return 2;
|
|
576
|
-
}
|
|
577
4052
|
const prompt = typeof ctx.flags.prompt === "string" ? ctx.flags.prompt : ctx.positional[1];
|
|
578
4053
|
if (!prompt) {
|
|
579
4054
|
ctx.writeErr('--prompt "<what to measure>" required.');
|
|
@@ -619,37 +4094,30 @@ var takeoffSummaryCommand = async (ctx) => {
|
|
|
619
4094
|
return 0;
|
|
620
4095
|
};
|
|
621
4096
|
var webhooksListenCommand = async (ctx) => {
|
|
622
|
-
if (process.env.EXAYARD_EXPERIMENTAL !== "1") {
|
|
623
|
-
ctx.writeErr(
|
|
624
|
-
[
|
|
625
|
-
"exayard listen is not yet implemented.",
|
|
626
|
-
"",
|
|
627
|
-
"The tunnel implementation is still in progress. To preview the command",
|
|
628
|
-
"surface, set EXAYARD_EXPERIMENTAL=1 \u2014 but note the tunnel itself will not",
|
|
629
|
-
"forward events yet.",
|
|
630
|
-
"",
|
|
631
|
-
"For now, register a webhook endpoint pointing at a public tunnel",
|
|
632
|
-
"(ngrok, cloudflared) and verify signatures locally with:",
|
|
633
|
-
"",
|
|
634
|
-
' echo "$RAW_BODY" | exayard webhooks verify --signature "$EXAYARD_SIGNATURE" --secret whsec_...'
|
|
635
|
-
].join("\n")
|
|
636
|
-
);
|
|
637
|
-
return 2;
|
|
638
|
-
}
|
|
639
4097
|
const forwardTo = typeof ctx.flags["forward-to"] === "string" ? ctx.flags["forward-to"] : void 0;
|
|
640
|
-
if (
|
|
641
|
-
ctx.writeErr(
|
|
4098
|
+
if (ctx.flags["forward-to"] === true) {
|
|
4099
|
+
ctx.writeErr('Usage: exayard listen [--forward-to http://localhost:3000/webhooks] [--events "bid.won,project.created"]');
|
|
642
4100
|
return 2;
|
|
643
4101
|
}
|
|
644
|
-
ctx.
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
4102
|
+
const events = typeof ctx.flags.events === "string" ? ctx.flags.events.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
4103
|
+
const exa = makeClient(ctx);
|
|
4104
|
+
const ac = new AbortController();
|
|
4105
|
+
const onSigint = () => ac.abort();
|
|
4106
|
+
process.once("SIGINT", onSigint);
|
|
4107
|
+
try {
|
|
4108
|
+
return await runListen(exa, {
|
|
4109
|
+
forwardTo,
|
|
4110
|
+
events,
|
|
4111
|
+
organizationId: resolveOrgId(ctx),
|
|
4112
|
+
name: typeof ctx.flags.name === "string" ? ctx.flags.name : void 0,
|
|
4113
|
+
json: ctx.json,
|
|
4114
|
+
write: ctx.write,
|
|
4115
|
+
writeErr: ctx.writeErr,
|
|
4116
|
+
signal: ac.signal
|
|
4117
|
+
});
|
|
4118
|
+
} finally {
|
|
4119
|
+
process.off("SIGINT", onSigint);
|
|
4120
|
+
}
|
|
653
4121
|
};
|
|
654
4122
|
var webhooksVerifyCommand = async (ctx) => {
|
|
655
4123
|
const signature = typeof ctx.flags.signature === "string" ? ctx.flags.signature : void 0;
|
|
@@ -660,11 +4128,11 @@ var webhooksVerifyCommand = async (ctx) => {
|
|
|
660
4128
|
}
|
|
661
4129
|
const body = await readAllStdin();
|
|
662
4130
|
try {
|
|
663
|
-
const event = await (0,
|
|
4131
|
+
const event = await (0, import_sdk3.constructWebhookEvent)(body, signature, secret);
|
|
664
4132
|
ctx.write(ctx.json ? JSON.stringify(event, null, 2) : `verified: ${event.type} (${event.id})`);
|
|
665
4133
|
return 0;
|
|
666
4134
|
} catch (err) {
|
|
667
|
-
if (err instanceof
|
|
4135
|
+
if (err instanceof import_sdk3.WebhookSignatureError) {
|
|
668
4136
|
ctx.writeErr(`Signature invalid (${err.code}): ${err.message}`);
|
|
669
4137
|
} else {
|
|
670
4138
|
ctx.writeErr(`Verify failed: ${err.message}`);
|
|
@@ -699,35 +4167,27 @@ var helpSearchCommand = async (ctx) => {
|
|
|
699
4167
|
};
|
|
700
4168
|
var resolveOrgId = (ctx) => {
|
|
701
4169
|
const fromFlag = typeof ctx.flags.org === "string" ? ctx.flags.org : void 0;
|
|
702
|
-
return fromFlag ?? readConfig().organizationId ??
|
|
4170
|
+
return fromFlag ?? readConfig().organizationId ?? void 0;
|
|
703
4171
|
};
|
|
704
4172
|
var projectsCreateCommand = async (ctx) => {
|
|
705
4173
|
const name = typeof ctx.flags.name === "string" ? ctx.flags.name : ctx.positional[0];
|
|
706
4174
|
if (!name) {
|
|
707
|
-
ctx.writeErr('Usage: exayard projects create --name "<project name>" [--org
|
|
4175
|
+
ctx.writeErr('Usage: exayard projects create --name "<project name>" [--org <org_id>]');
|
|
708
4176
|
return 2;
|
|
709
4177
|
}
|
|
710
4178
|
const organizationId = resolveOrgId(ctx);
|
|
711
|
-
if (!organizationId) {
|
|
712
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
713
|
-
return 2;
|
|
714
|
-
}
|
|
715
4179
|
const exa = makeClient(ctx);
|
|
716
4180
|
const res = await exa.projects.create({ organizationId, name });
|
|
717
|
-
ctx.write(ctx.json ? JSON.stringify(res, null, 2) : `created ${res.
|
|
4181
|
+
ctx.write(ctx.json ? JSON.stringify(res, null, 2) : `created ${res.id} ${name}`);
|
|
718
4182
|
return 0;
|
|
719
4183
|
};
|
|
720
4184
|
var projectsArchiveCommand = async (ctx) => {
|
|
721
4185
|
const id = typeof ctx.flags.id === "string" ? ctx.flags.id : ctx.positional[0];
|
|
722
4186
|
if (!id) {
|
|
723
|
-
ctx.writeErr("Usage: exayard projects archive <project_id> [--org
|
|
4187
|
+
ctx.writeErr("Usage: exayard projects archive <project_id> [--org <org_id>]");
|
|
724
4188
|
return 2;
|
|
725
4189
|
}
|
|
726
4190
|
const organizationId = resolveOrgId(ctx);
|
|
727
|
-
if (!organizationId) {
|
|
728
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
729
|
-
return 2;
|
|
730
|
-
}
|
|
731
4191
|
const exa = makeClient(ctx);
|
|
732
4192
|
await exa.projects.archive(id, { organizationId });
|
|
733
4193
|
ctx.write(ctx.json ? JSON.stringify({ ok: true, archived: id }) : `archived ${id}`);
|
|
@@ -735,10 +4195,6 @@ var projectsArchiveCommand = async (ctx) => {
|
|
|
735
4195
|
};
|
|
736
4196
|
var webhooksListCommand = async (ctx) => {
|
|
737
4197
|
const organizationId = resolveOrgId(ctx);
|
|
738
|
-
if (!organizationId) {
|
|
739
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
740
|
-
return 2;
|
|
741
|
-
}
|
|
742
4198
|
const exa = makeClient(ctx);
|
|
743
4199
|
const res = await exa.webhooks.listEndpoints({ organizationId });
|
|
744
4200
|
if (ctx.json) {
|
|
@@ -762,10 +4218,6 @@ var webhooksCreateCommand = async (ctx) => {
|
|
|
762
4218
|
return 2;
|
|
763
4219
|
}
|
|
764
4220
|
const organizationId = resolveOrgId(ctx);
|
|
765
|
-
if (!organizationId) {
|
|
766
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
767
|
-
return 2;
|
|
768
|
-
}
|
|
769
4221
|
const events = eventsFlag.split(",").map((s) => s.trim()).filter(Boolean);
|
|
770
4222
|
const exa = makeClient(ctx);
|
|
771
4223
|
const res = await exa.webhooks.createEndpoint({ organizationId, url, events, description });
|
|
@@ -780,14 +4232,10 @@ var webhooksCreateCommand = async (ctx) => {
|
|
|
780
4232
|
var webhooksDeleteCommand = async (ctx) => {
|
|
781
4233
|
const id = typeof ctx.flags.id === "string" ? ctx.flags.id : ctx.positional[0];
|
|
782
4234
|
if (!id) {
|
|
783
|
-
ctx.writeErr("Usage: exayard webhooks delete <endpoint_id> [--org
|
|
4235
|
+
ctx.writeErr("Usage: exayard webhooks delete <endpoint_id> [--org <org_id>]");
|
|
784
4236
|
return 2;
|
|
785
4237
|
}
|
|
786
4238
|
const organizationId = resolveOrgId(ctx);
|
|
787
|
-
if (!organizationId) {
|
|
788
|
-
ctx.writeErr("--org <organizationId> required (or set EXAYARD_ORG_ID).");
|
|
789
|
-
return 2;
|
|
790
|
-
}
|
|
791
4239
|
const exa = makeClient(ctx);
|
|
792
4240
|
await exa.webhooks.deleteEndpoint(id, { organizationId });
|
|
793
4241
|
ctx.write(ctx.json ? JSON.stringify({ ok: true, deleted: id }) : `deleted ${id}`);
|