@fourier-labs/harbour-contracts 0.88.0 → 0.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/contract-release.json +24 -24
  2. package/dist/gen-ts/app-integrations-declaration.d.ts +8 -0
  3. package/dist/gen-ts/contract-release.d.ts +6 -6
  4. package/dist/gen-ts/contract-release.js +6 -6
  5. package/dist/gen-ts/data-grant-v2.d.ts +16 -2
  6. package/dist/gen-ts/integration-execute-input-rules.d.ts +230 -0
  7. package/dist/gen-ts/integration-execute-input-rules.js +70 -0
  8. package/dist/gen-ts/integration-execute-request.d.ts +19 -2
  9. package/dist/gen-ts/integration-execute-result.d.ts +49 -0
  10. package/dist/gen-ts/integration-operations.d.ts +56 -0
  11. package/dist/gen-ts/integration-operations.js +72 -0
  12. package/dist/gen-ts/resolved-app-serving.d.ts +4 -0
  13. package/dist/gen-ts/saas-connection.d.ts +34 -3
  14. package/gen-ts/app-integrations-declaration.ts +8 -0
  15. package/gen-ts/contract-release.ts +6 -6
  16. package/gen-ts/data-grant-v2.ts +113 -1
  17. package/gen-ts/integration-execute-input-rules.ts +70 -0
  18. package/gen-ts/integration-execute-request.ts +27 -2
  19. package/gen-ts/integration-execute-result.ts +49 -0
  20. package/gen-ts/integration-operations.ts +72 -0
  21. package/gen-ts/resolved-app-serving.ts +4 -0
  22. package/gen-ts/saas-connection.ts +222 -2
  23. package/package.json +1 -1
  24. package/schema/app-integrations-declaration.schema.json +121 -9
  25. package/schema/data-grant-v2.schema.json +17 -1
  26. package/schema/integration-execute-request.schema.json +458 -7
  27. package/schema/integration-execute-result.schema.json +234 -2
  28. package/schema/resolved-app-serving.schema.json +1 -0
  29. package/schema/saas-connection.schema.json +732 -126
@@ -735,5 +735,77 @@ export const IntegrationOperations = {
735
735
  "readOnly": false,
736
736
  "declaredMode": false,
737
737
  "idempotencyKey": false
738
+ },
739
+ "zohobooks.contact.list": {
740
+ "kind": "saas",
741
+ "identities": [
742
+ "user"
743
+ ],
744
+ "readOnly": true,
745
+ "declaredMode": false,
746
+ "idempotencyKey": false
747
+ },
748
+ "zohobooks.purchaseorder.list": {
749
+ "kind": "saas",
750
+ "identities": [
751
+ "user"
752
+ ],
753
+ "readOnly": true,
754
+ "declaredMode": false,
755
+ "idempotencyKey": false
756
+ },
757
+ "zohobooks.purchaseorder.read": {
758
+ "kind": "saas",
759
+ "identities": [
760
+ "user"
761
+ ],
762
+ "readOnly": true,
763
+ "declaredMode": false,
764
+ "idempotencyKey": false
765
+ },
766
+ "zohobooks.bill.create": {
767
+ "kind": "saas",
768
+ "identities": [
769
+ "user"
770
+ ],
771
+ "readOnly": false,
772
+ "declaredMode": false,
773
+ "idempotencyKey": true
774
+ },
775
+ "zohobooks.bill.read": {
776
+ "kind": "saas",
777
+ "identities": [
778
+ "user"
779
+ ],
780
+ "readOnly": true,
781
+ "declaredMode": false,
782
+ "idempotencyKey": false
783
+ },
784
+ "zohobooks.invoice.create": {
785
+ "kind": "saas",
786
+ "identities": [
787
+ "user"
788
+ ],
789
+ "readOnly": false,
790
+ "declaredMode": false,
791
+ "idempotencyKey": true
792
+ },
793
+ "zohobooks.invoice.read": {
794
+ "kind": "saas",
795
+ "identities": [
796
+ "user"
797
+ ],
798
+ "readOnly": true,
799
+ "declaredMode": false,
800
+ "idempotencyKey": false
801
+ },
802
+ "zohobooks.invoice.pdf": {
803
+ "kind": "saas",
804
+ "identities": [
805
+ "user"
806
+ ],
807
+ "readOnly": true,
808
+ "declaredMode": false,
809
+ "idempotencyKey": false
738
810
  }
739
811
  } as const;
@@ -22,6 +22,10 @@ export interface ResolvedAppServing {
22
22
  * The platform cell (LLD §3.7) whose routing store and template distribution serve this app.
23
23
  */
24
24
  cellId?: string;
25
+ /**
26
+ * How the tenant's cells run the app's server (ADR 0035): `kubernetes`, the cluster cell, or `serverless`, the gateway service with Lambda Web Functions, which serves kit apps only. One choice per tenant, the same in every environment; governance copies it from the tenant's deployment destination, and build and Provision branch on it. Absent means kubernetes.
27
+ */
28
+ hosting?: 'kubernetes' | 'serverless';
25
29
  /**
26
30
  * The cell's CloudFront KeyValueStore ARN; equals edge.routingStoreArn unless the cell record names its own.
27
31
  */
@@ -17,7 +17,8 @@ export type SaasProvider =
17
17
  | 'outlook'
18
18
  | 'onedrive'
19
19
  | 'excel'
20
- | 'teams';
20
+ | 'teams'
21
+ | 'zohobooks';
21
22
  /**
22
23
  * DISABLED stops every app call immediately (details.code CONNECTION_DISABLED).
23
24
  */
@@ -72,7 +73,7 @@ export interface SaasConnection {
72
73
  * Operations IT allows apps to request on this connection: the provider's own SaaS operations (Slack: slack.channel.history, slack.message.post, slack.message.update, slack.message.delete, slack.dm.send, slack.user.lookup; Gmail: gmail.thread.list, gmail.thread.read, gmail.message.read, gmail.message.send, gmail.attachment.read; Basecamp (0.76.0): basecamp.project.list, basecamp.todolist.list, basecamp.todo.list, basecamp.todo.create, basecamp.todo.complete, basecamp.message.list, basecamp.message.post, basecamp.comment.list, basecamp.comment.post, basecamp.campfire.post, basecamp.people.list; SharePoint (0.78.0): sharepoint.list.list, sharepoint.item.list, sharepoint.item.create, sharepoint.item.update, sharepoint.drive.list, sharepoint.file.list, sharepoint.file.search, sharepoint.file.read, sharepoint.file.upload; Google Workspace and Microsoft 365 (0.86.0): the calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.*, excel.* and teams.* operations of the connection's provider). Never warehouse.view.read. Phone (0.82.0): phone.sms.list; 0.83.0 adds phone.number.get. Seventy-eight SaaS operations in all since 0.86.0.
73
74
  *
74
75
  * @minItems 1
75
- * @maxItems 78
76
+ * @maxItems 86
76
77
  */
77
78
  operations: (
78
79
  | 'slack.channel.history'
@@ -156,7 +157,117 @@ export interface SaasConnection {
156
157
  | 'warehouse.view.read'
157
158
  | 'warehouse.table.append'
158
159
  | 'service.request'
160
+ | 'zohobooks.contact.list'
161
+ | 'zohobooks.purchaseorder.list'
162
+ | 'zohobooks.purchaseorder.read'
163
+ | 'zohobooks.bill.create'
164
+ | 'zohobooks.bill.read'
165
+ | 'zohobooks.invoice.create'
166
+ | 'zohobooks.invoice.read'
167
+ | 'zohobooks.invoice.pdf'
159
168
  )[];
169
+ /**
170
+ * Explicit user-mode operations eligible for automatic app access, subject to the connection operation ceiling and personal consent. Empty means review all; absent inherits company defaults or the existing userModeApproval migration rule. Governance validates provider and mode compatibility. Background execution still requires personal background consent, not a separate admin grant.
171
+ *
172
+ * @maxItems 86
173
+ */
174
+ automaticUserOperations?: (
175
+ | 'slack.channel.history'
176
+ | 'slack.message.post'
177
+ | 'slack.message.update'
178
+ | 'slack.message.delete'
179
+ | 'slack.dm.send'
180
+ | 'slack.user.lookup'
181
+ | 'gmail.thread.list'
182
+ | 'gmail.thread.read'
183
+ | 'gmail.message.read'
184
+ | 'gmail.message.send'
185
+ | 'gmail.attachment.read'
186
+ | 'basecamp.project.list'
187
+ | 'basecamp.todolist.list'
188
+ | 'basecamp.todo.list'
189
+ | 'basecamp.todo.create'
190
+ | 'basecamp.todo.complete'
191
+ | 'basecamp.message.list'
192
+ | 'basecamp.message.post'
193
+ | 'basecamp.comment.list'
194
+ | 'basecamp.comment.post'
195
+ | 'basecamp.campfire.post'
196
+ | 'basecamp.people.list'
197
+ | 'sharepoint.list.list'
198
+ | 'sharepoint.item.list'
199
+ | 'sharepoint.item.create'
200
+ | 'sharepoint.item.update'
201
+ | 'sharepoint.drive.list'
202
+ | 'sharepoint.file.list'
203
+ | 'sharepoint.file.search'
204
+ | 'sharepoint.file.read'
205
+ | 'sharepoint.file.upload'
206
+ | 'phone.sms.list'
207
+ | 'phone.number.get'
208
+ | 'calendar.calendar.list'
209
+ | 'calendar.event.list'
210
+ | 'calendar.event.read'
211
+ | 'calendar.event.create'
212
+ | 'calendar.event.update'
213
+ | 'calendar.event.cancel'
214
+ | 'drive.file.list'
215
+ | 'drive.file.search'
216
+ | 'drive.file.read'
217
+ | 'drive.file.upload'
218
+ | 'drive.folder.create'
219
+ | 'docs.document.read'
220
+ | 'docs.document.create'
221
+ | 'docs.document.append'
222
+ | 'docs.text.replace'
223
+ | 'sheets.spreadsheet.read'
224
+ | 'sheets.values.read'
225
+ | 'sheets.values.append'
226
+ | 'sheets.values.update'
227
+ | 'sheets.values.clear'
228
+ | 'sheets.spreadsheet.create'
229
+ | 'outlook.message.list'
230
+ | 'outlook.message.read'
231
+ | 'outlook.attachment.read'
232
+ | 'outlook.message.send'
233
+ | 'outlook.message.reply'
234
+ | 'outlook.event.list'
235
+ | 'outlook.event.read'
236
+ | 'outlook.event.create'
237
+ | 'outlook.event.update'
238
+ | 'outlook.event.cancel'
239
+ | 'onedrive.file.list'
240
+ | 'onedrive.file.search'
241
+ | 'onedrive.file.read'
242
+ | 'onedrive.file.upload'
243
+ | 'onedrive.folder.create'
244
+ | 'excel.worksheet.list'
245
+ | 'excel.range.read'
246
+ | 'excel.range.update'
247
+ | 'excel.table.list'
248
+ | 'excel.row.add'
249
+ | 'excel.worksheet.add'
250
+ | 'teams.message.list'
251
+ | 'teams.message.post'
252
+ | 'teams.reply.post'
253
+ | 'warehouse.view.read'
254
+ | 'warehouse.table.append'
255
+ | 'service.request'
256
+ | 'zohobooks.contact.list'
257
+ | 'zohobooks.purchaseorder.list'
258
+ | 'zohobooks.purchaseorder.read'
259
+ | 'zohobooks.bill.create'
260
+ | 'zohobooks.bill.read'
261
+ | 'zohobooks.invoice.create'
262
+ | 'zohobooks.invoice.read'
263
+ | 'zohobooks.invoice.pdf'
264
+ )[];
265
+ /**
266
+ * Reusable permissions for admitted builders on explicit shared SaaS resources. Governance checks registered resources, provider/mode compatibility and the connection operation ceiling. Connection version and audit record changes; no per-entry approval metadata.
267
+ *
268
+ * @maxItems 128
269
+ */
270
+ reusableAccess?: ReusableSaasAccess[];
160
271
  developmentPreapproval: DevelopmentPreapproval;
161
272
  oauthClient?: SaasOauthClient;
162
273
  userModeApproval?: UserModeApproval;
@@ -203,6 +314,10 @@ export interface SaasConnection {
203
314
  installations?: {
204
315
  [k: string]: LegacySaasInstallation;
205
316
  };
317
+ /**
318
+ * Zoho data center. Chosen by the connection administrator, never by an app call.
319
+ */
320
+ region?: 'us' | 'in' | 'eu' | 'au' | 'jp' | 'ca' | 'sa' | 'cn' | 'ae';
206
321
  }
207
322
  /**
208
323
  * The one bot installation of the platform-owned provider app in this tenant's workspace.
@@ -240,6 +355,111 @@ export interface BasecampAccount {
240
355
  */
241
356
  name: string;
242
357
  }
358
+ export interface ReusableSaasAccess {
359
+ /**
360
+ * Tenant-wide logical alias of a connection (governance sourceId) or of a resource under it.
361
+ */
362
+ resource: string;
363
+ /**
364
+ * Whose authority an operation runs under. Fixed per operation, except the declared-mode Slack operations (slack.message.post; since 0.71.0 also slack.channel.history, slack.message.update, slack.message.delete): declared per app for the access request, and named per call among the app's approved modes (execute request `mode`, 0.55.0).
365
+ */
366
+ mode: 'app' | 'user';
367
+ /**
368
+ * @minItems 1
369
+ * @maxItems 86
370
+ */
371
+ operations: (
372
+ | 'slack.channel.history'
373
+ | 'slack.message.post'
374
+ | 'slack.message.update'
375
+ | 'slack.message.delete'
376
+ | 'slack.dm.send'
377
+ | 'slack.user.lookup'
378
+ | 'gmail.thread.list'
379
+ | 'gmail.thread.read'
380
+ | 'gmail.message.read'
381
+ | 'gmail.message.send'
382
+ | 'gmail.attachment.read'
383
+ | 'basecamp.project.list'
384
+ | 'basecamp.todolist.list'
385
+ | 'basecamp.todo.list'
386
+ | 'basecamp.todo.create'
387
+ | 'basecamp.todo.complete'
388
+ | 'basecamp.message.list'
389
+ | 'basecamp.message.post'
390
+ | 'basecamp.comment.list'
391
+ | 'basecamp.comment.post'
392
+ | 'basecamp.campfire.post'
393
+ | 'basecamp.people.list'
394
+ | 'sharepoint.list.list'
395
+ | 'sharepoint.item.list'
396
+ | 'sharepoint.item.create'
397
+ | 'sharepoint.item.update'
398
+ | 'sharepoint.drive.list'
399
+ | 'sharepoint.file.list'
400
+ | 'sharepoint.file.search'
401
+ | 'sharepoint.file.read'
402
+ | 'sharepoint.file.upload'
403
+ | 'phone.sms.list'
404
+ | 'phone.number.get'
405
+ | 'calendar.calendar.list'
406
+ | 'calendar.event.list'
407
+ | 'calendar.event.read'
408
+ | 'calendar.event.create'
409
+ | 'calendar.event.update'
410
+ | 'calendar.event.cancel'
411
+ | 'drive.file.list'
412
+ | 'drive.file.search'
413
+ | 'drive.file.read'
414
+ | 'drive.file.upload'
415
+ | 'drive.folder.create'
416
+ | 'docs.document.read'
417
+ | 'docs.document.create'
418
+ | 'docs.document.append'
419
+ | 'docs.text.replace'
420
+ | 'sheets.spreadsheet.read'
421
+ | 'sheets.values.read'
422
+ | 'sheets.values.append'
423
+ | 'sheets.values.update'
424
+ | 'sheets.values.clear'
425
+ | 'sheets.spreadsheet.create'
426
+ | 'outlook.message.list'
427
+ | 'outlook.message.read'
428
+ | 'outlook.attachment.read'
429
+ | 'outlook.message.send'
430
+ | 'outlook.message.reply'
431
+ | 'outlook.event.list'
432
+ | 'outlook.event.read'
433
+ | 'outlook.event.create'
434
+ | 'outlook.event.update'
435
+ | 'outlook.event.cancel'
436
+ | 'onedrive.file.list'
437
+ | 'onedrive.file.search'
438
+ | 'onedrive.file.read'
439
+ | 'onedrive.file.upload'
440
+ | 'onedrive.folder.create'
441
+ | 'excel.worksheet.list'
442
+ | 'excel.range.read'
443
+ | 'excel.range.update'
444
+ | 'excel.table.list'
445
+ | 'excel.row.add'
446
+ | 'excel.worksheet.add'
447
+ | 'teams.message.list'
448
+ | 'teams.message.post'
449
+ | 'teams.reply.post'
450
+ | 'warehouse.view.read'
451
+ | 'warehouse.table.append'
452
+ | 'service.request'
453
+ | 'zohobooks.contact.list'
454
+ | 'zohobooks.purchaseorder.list'
455
+ | 'zohobooks.purchaseorder.read'
456
+ | 'zohobooks.bill.create'
457
+ | 'zohobooks.bill.read'
458
+ | 'zohobooks.invoice.create'
459
+ | 'zohobooks.invoice.read'
460
+ | 'zohobooks.invoice.pdf'
461
+ )[];
462
+ }
243
463
  export interface DevelopmentPreapproval {
244
464
  /**
245
465
  * Auto-approve development requests within the allowlist.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fourier-labs/harbour-contracts",
3
- "version": "0.88.0",
3
+ "version": "0.91.0",
4
4
  "description": "Single source of truth for every data shape that crosses a boundary between Isomorph components.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://schemas.harbour.dev/app-integrations-declaration.schema.json",
4
4
  "title": "AppIntegrationsDeclaration",
5
- "description": "Committed `.isomorph/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (eighty-one operations since 0.86.0, ADR 0018, ADR 0022, ADR 0024, ADR 0028, ADR 0029 and ADR 0032). Each operation carries a fixed identity mode \u2014 slack.dm.send=app, slack.user.lookup=app, every gmail.* operation=user, every basecamp.* operation=user, every sharepoint.* operation=user, every calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.*, excel.* and teams.* operation=user (0.86.0), warehouse.view.read=app, service.request=app \u2014 except the declared-mode Slack operations (slack.message.post, and since 0.71.0 slack.channel.history, slack.message.update and slack.message.delete), whose mode the app DECLARES: `app` acts as the company's Slack app (the one audited bot identity; a post goes out under the IT-approved per-app display name when the connection declares `presentation`), `user` acts as the signed-in person after their own consent (reads what they can read, edits what they wrote). The declared mode is the one the app requests access for; an app may hold approved grants for both modes after two requests, and each declared-mode call names which approved mode it runs under (`mode` on the execute request, 0.55.0) \u2014 a mode IT did not approve is refused, never swapped for the other. A `saas` connection may declare only the Slack, Gmail, Basecamp, SharePoint, phone, Google Workspace (calendar, drive, docs, sheets) and Microsoft 365 (outlook, onedrive, excel, teams) operations and a `database` connection only warehouse.view.read; `presentation` is a `saas` connection's optional per-app display name and icon for app-mode Slack posts, shown to IT at approval time and applied only from the approved grant. Gmail is user-mode only (reads, one send); its single resource is the fixed `inbox` alias. Basecamp (0.76.0, ADR 0022) is user-mode only too: basecamp.project.list runs on the fixed `account` alias (the company's pinned Basecamp account) and every other basecamp.* operation on a project alias IT mapped in the connection's `channels` map, so Basecamp itself refuses an id outside that project. SharePoint (0.78.0, ADR 0024) is user-mode only as well: every sharepoint.* operation runs on a site alias IT mapped in the connection's `channels` map (a Microsoft Graph site id), so Graph itself refuses a list, item or file outside that site. Google Workspace and Microsoft 365 (0.86.0, ADR 0032) are user-mode only too: every calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.* and excel.* operation runs on the provider's one fixed alias for the signed-in person's own data (governance resolves it, the way Gmail's is `inbox`; the contract does not pin the literal), and every teams.* operation on a channel alias IT mapped in the connection's `channels` map (a Teams team and channel id), so Graph itself refuses a message outside that channel. slack.dm.send and slack.user.lookup address people by email rather than a channel, so their single resource is the fixed `workspace` alias (the connection's installed workspace), the way Gmail's is `inbox`. This file also owns the shared IntegrationOperation / IntegrationIdentityMode / IntegrationEnvironment / IntegrationConnectionKind / IntegrationPresentation definitions referenced by the other integration schemas.",
5
+ "description": "Committed `.isomorph/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (eighty-one operations since 0.86.0, ADR 0018, ADR 0022, ADR 0024, ADR 0028, ADR 0029 and ADR 0032). Each operation carries a fixed identity mode — slack.dm.send=app, slack.user.lookup=app, every gmail.* operation=user, every basecamp.* operation=user, every sharepoint.* operation=user, every calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.*, excel.* and teams.* operation=user (0.86.0), warehouse.view.read=app, service.request=app — except the declared-mode Slack operations (slack.message.post, and since 0.71.0 slack.channel.history, slack.message.update and slack.message.delete), whose mode the app DECLARES: `app` acts as the company's Slack app (the one audited bot identity; a post goes out under the IT-approved per-app display name when the connection declares `presentation`), `user` acts as the signed-in person after their own consent (reads what they can read, edits what they wrote). The declared mode is the one the app requests access for; an app may hold approved grants for both modes after two requests, and each declared-mode call names which approved mode it runs under (`mode` on the execute request, 0.55.0) — a mode IT did not approve is refused, never swapped for the other. A `saas` connection may declare only the Slack, Gmail, Basecamp, SharePoint, phone, Google Workspace (calendar, drive, docs, sheets) and Microsoft 365 (outlook, onedrive, excel, teams) operations and a `database` connection only warehouse.view.read; `presentation` is a `saas` connection's optional per-app display name and icon for app-mode Slack posts, shown to IT at approval time and applied only from the approved grant. Gmail is user-mode only (reads, one send); its single resource is the fixed `inbox` alias. Basecamp (0.76.0, ADR 0022) is user-mode only too: basecamp.project.list runs on the fixed `account` alias (the company's pinned Basecamp account) and every other basecamp.* operation on a project alias IT mapped in the connection's `channels` map, so Basecamp itself refuses an id outside that project. SharePoint (0.78.0, ADR 0024) is user-mode only as well: every sharepoint.* operation runs on a site alias IT mapped in the connection's `channels` map (a Microsoft Graph site id), so Graph itself refuses a list, item or file outside that site. Google Workspace and Microsoft 365 (0.86.0, ADR 0032) are user-mode only too: every calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.* and excel.* operation runs on the provider's one fixed alias for the signed-in person's own data (governance resolves it, the way Gmail's is `inbox`; the contract does not pin the literal), and every teams.* operation on a channel alias IT mapped in the connection's `channels` map (a Teams team and channel id), so Graph itself refuses a message outside that channel. slack.dm.send and slack.user.lookup address people by email rather than a channel, so their single resource is the fixed `workspace` alias (the connection's installed workspace), the way Gmail's is `inbox`. This file also owns the shared IntegrationOperation / IntegrationIdentityMode / IntegrationEnvironment / IntegrationConnectionKind / IntegrationPresentation definitions referenced by the other integration schemas.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -35,12 +35,12 @@
35
35
  "BasecampId": {
36
36
  "type": "string",
37
37
  "pattern": "^[0-9]{1,20}$",
38
- "description": "A Basecamp id (0.76.0): an account, project, to-do list, to-do, recording or person, as a string of 1..20 digits \u2014 how every basecamp.* result names one and every basecamp.* input takes one back."
38
+ "description": "A Basecamp id (0.76.0): an account, project, to-do list, to-do, recording or person, as a string of 1..20 digits — how every basecamp.* result names one and every basecamp.* input takes one back."
39
39
  },
40
40
  "GraphId": {
41
41
  "type": "string",
42
42
  "pattern": "^[A-Za-z0-9!._,=-]{1,256}$",
43
- "description": "A Microsoft Graph id (0.78.0): a SharePoint list (a GUID), list item (digits), drive (`b!...`) or drive item, as Graph names it \u2014 opaque, 1..256 characters of letters, digits and `!._,=-`. How every sharepoint.* result names one and every sharepoint.* input takes one back."
43
+ "description": "A Microsoft Graph id (0.78.0): a SharePoint list (a GUID), list item (digits), drive (`b!...`) or drive item, as Graph names it — opaque, 1..256 characters of letters, digits and `!._,=-`. How every sharepoint.* result names one and every sharepoint.* input takes one back."
44
44
  },
45
45
  "IntegrationConnectionKind": {
46
46
  "type": "string",
@@ -133,9 +133,17 @@
133
133
  "teams.reply.post",
134
134
  "warehouse.view.read",
135
135
  "warehouse.table.append",
136
- "service.request"
136
+ "service.request",
137
+ "zohobooks.contact.list",
138
+ "zohobooks.purchaseorder.list",
139
+ "zohobooks.purchaseorder.read",
140
+ "zohobooks.bill.create",
141
+ "zohobooks.bill.read",
142
+ "zohobooks.invoice.create",
143
+ "zohobooks.invoice.read",
144
+ "zohobooks.invoice.pdf"
137
145
  ],
138
- "description": "Closed operation set. Extending it is a contracts change. 0.86.0 (ADR 0032) adds the forty-five Google Workspace and Microsoft 365 operations, every one user mode only: on the provider's fixed alias for the signed-in person's own data, calendar.calendar.list, calendar.event.list, calendar.event.read, calendar.event.create (a receipted write: explicit action, idempotencyKey), calendar.event.update and calendar.event.cancel (idempotent at Google, no key); drive.file.list, drive.file.search, drive.file.read, drive.file.upload and drive.folder.create (receipted writes); docs.document.read, docs.document.create (receipted), docs.document.append and docs.text.replace (no key); sheets.spreadsheet.read, sheets.values.read, sheets.values.append (receipted), sheets.values.update and sheets.values.clear (no key), sheets.spreadsheet.create (receipted); outlook.message.list, outlook.message.read, outlook.attachment.read, outlook.message.send and outlook.message.reply (receipted sends, as gmail.message.send), outlook.event.list, outlook.event.read, outlook.event.create (receipted), outlook.event.update and outlook.event.cancel (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read, onedrive.file.upload and onedrive.folder.create (receipted); excel.worksheet.list, excel.range.read, excel.range.update (no key), excel.table.list, excel.row.add and excel.worksheet.add (receipted); and on a Teams channel alias (a key of the connection's `channels` map) teams.message.list, teams.message.post and teams.reply.post (receipted). 0.78.0 (ADR 0024) adds the nine SharePoint operations, every one user mode only on a site alias (a key of the connection's `channels` map, holding a Microsoft Graph site id): sharepoint.list.list, sharepoint.item.list, sharepoint.item.create (a receipted write: explicit action, idempotencyKey), sharepoint.item.update (a PATCH, idempotent at Graph, no key), sharepoint.drive.list, sharepoint.file.list, sharepoint.file.search, sharepoint.file.read and sharepoint.file.upload (a receipted write). 0.76.0 (ADR 0022) adds the eleven Basecamp operations, every one user mode only like gmail.*: basecamp.project.list (the projects of the company's pinned Basecamp account; its resource is the fixed `account` alias), and on one mapped project (the resource is a project alias, a key of the connection's `channels` map) basecamp.todolist.list, basecamp.todo.list, basecamp.todo.create (a receipted write: explicit action, idempotencyKey), basecamp.todo.complete (idempotent at Basecamp, no key), basecamp.message.list, basecamp.message.post, basecamp.comment.list, basecamp.comment.post, basecamp.campfire.post (the three posts are receipted writes) and basecamp.people.list. gmail.message.send (0.50.0) sends one email as the signed-in person: a send like slack.message.post (explicit Send, idempotencyKey, write receipt) under user identity; since 0.71.0 it may carry an HTML body and attachments. slack.message.post (0.53.0) runs in the mode the app declares: app (the company's Slack app, under the IT-approved per-app name) or user (the signed-in person, after their consent); since 0.55.0 each call names which of the approved modes it runs under. 0.71.0 (ADR 0018) adds slack.message.update and slack.message.delete (declared mode, like post), slack.dm.send (app mode: a direct message to 1..8 workspace members named by email), slack.user.lookup (app mode: email to Slack user id), gmail.thread.read (every message of one thread) and gmail.attachment.read (one attachment's bytes), and lets slack.channel.history run in app mode as well as user mode. 0.82.0 (ADR 0028) adds phone.sms.list (app mode only, a number alias from a phone connection's `channels` map: the SMS received on that number since a time) and warehouse.table.append (app mode only on a database connection, a receipted write with idempotencyKey: rows appended to a view alias whose mapping says `access: append`). 0.83.0 (ADR 0029) adds phone.number.get (app mode only, read-only, no input, on a number alias from a phone connection's `channels` map: the E.164 number that alias maps to, so the app \u2014 or a computer-use agent it drives \u2014 can type it into a portal that signs in by mobile number). 0.85.0 (ADR 0031) raises warehouse.table.append's `rows` from 1..500 to 1..5000 records (the byte cap stays governance's, now 8 MiB), so a downloaded report of a few thousand rows lands in one call."
146
+ "description": "Closed operation set. Extending it is a contracts change. 0.86.0 (ADR 0032) adds the forty-five Google Workspace and Microsoft 365 operations, every one user mode only: on the provider's fixed alias for the signed-in person's own data, calendar.calendar.list, calendar.event.list, calendar.event.read, calendar.event.create (a receipted write: explicit action, idempotencyKey), calendar.event.update and calendar.event.cancel (idempotent at Google, no key); drive.file.list, drive.file.search, drive.file.read, drive.file.upload and drive.folder.create (receipted writes); docs.document.read, docs.document.create (receipted), docs.document.append and docs.text.replace (no key); sheets.spreadsheet.read, sheets.values.read, sheets.values.append (receipted), sheets.values.update and sheets.values.clear (no key), sheets.spreadsheet.create (receipted); outlook.message.list, outlook.message.read, outlook.attachment.read, outlook.message.send and outlook.message.reply (receipted sends, as gmail.message.send), outlook.event.list, outlook.event.read, outlook.event.create (receipted), outlook.event.update and outlook.event.cancel (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read, onedrive.file.upload and onedrive.folder.create (receipted); excel.worksheet.list, excel.range.read, excel.range.update (no key), excel.table.list, excel.row.add and excel.worksheet.add (receipted); and on a Teams channel alias (a key of the connection's `channels` map) teams.message.list, teams.message.post and teams.reply.post (receipted). 0.78.0 (ADR 0024) adds the nine SharePoint operations, every one user mode only on a site alias (a key of the connection's `channels` map, holding a Microsoft Graph site id): sharepoint.list.list, sharepoint.item.list, sharepoint.item.create (a receipted write: explicit action, idempotencyKey), sharepoint.item.update (a PATCH, idempotent at Graph, no key), sharepoint.drive.list, sharepoint.file.list, sharepoint.file.search, sharepoint.file.read and sharepoint.file.upload (a receipted write). 0.76.0 (ADR 0022) adds the eleven Basecamp operations, every one user mode only like gmail.*: basecamp.project.list (the projects of the company's pinned Basecamp account; its resource is the fixed `account` alias), and on one mapped project (the resource is a project alias, a key of the connection's `channels` map) basecamp.todolist.list, basecamp.todo.list, basecamp.todo.create (a receipted write: explicit action, idempotencyKey), basecamp.todo.complete (idempotent at Basecamp, no key), basecamp.message.list, basecamp.message.post, basecamp.comment.list, basecamp.comment.post, basecamp.campfire.post (the three posts are receipted writes) and basecamp.people.list. gmail.message.send (0.50.0) sends one email as the signed-in person: a send like slack.message.post (explicit Send, idempotencyKey, write receipt) under user identity; since 0.71.0 it may carry an HTML body and attachments. slack.message.post (0.53.0) runs in the mode the app declares: app (the company's Slack app, under the IT-approved per-app name) or user (the signed-in person, after their consent); since 0.55.0 each call names which of the approved modes it runs under. 0.71.0 (ADR 0018) adds slack.message.update and slack.message.delete (declared mode, like post), slack.dm.send (app mode: a direct message to 1..8 workspace members named by email), slack.user.lookup (app mode: email to Slack user id), gmail.thread.read (every message of one thread) and gmail.attachment.read (one attachment's bytes), and lets slack.channel.history run in app mode as well as user mode. 0.82.0 (ADR 0028) adds phone.sms.list (app mode only, a number alias from a phone connection's `channels` map: the SMS received on that number since a time) and warehouse.table.append (app mode only on a database connection, a receipted write with idempotencyKey: rows appended to a view alias whose mapping says `access: append`). 0.83.0 (ADR 0029) adds phone.number.get (app mode only, read-only, no input, on a number alias from a phone connection's `channels` map: the E.164 number that alias maps to, so the app — or a computer-use agent it drives — can type it into a portal that signs in by mobile number). 0.85.0 (ADR 0031) raises warehouse.table.append's `rows` from 1..500 to 1..5000 records (the byte cap stays governance's, now 8 MiB), so a downloaded report of a few thousand rows lands in one call."
139
147
  },
140
148
  "IntegrationIdentityMode": {
141
149
  "type": "string",
@@ -160,7 +168,7 @@
160
168
  "required": [
161
169
  "displayName"
162
170
  ],
163
- "description": "How an app's app-mode Slack posts are presented (0.53.0): the display name and optional icon the platform bot posts under, so each app's posts are distinguishable while the bot stays the one audited identity. Declared by the app, shown to IT on the access request (\"posts as\"), copied onto the approved grant and applied only from there \u2014 a call can never name itself. Needs the Slack app scope chat:write.customize on the tenant's installation; without it the post goes out under the bot's own name and the receipt says so.",
171
+ "description": "How an app's app-mode Slack posts are presented (0.53.0): the display name and optional icon the platform bot posts under, so each app's posts are distinguishable while the bot stays the one audited identity. Declared by the app, shown to IT on the access request (\"posts as\"), copied onto the approved grant and applied only from there — a call can never name itself. Needs the Slack app scope chat:write.customize on the tenant's installation; without it the post goes out under the bot's own name and the receipt says so.",
164
172
  "properties": {
165
173
  "displayName": {
166
174
  "type": "string",
@@ -234,7 +242,7 @@
234
242
  "identity",
235
243
  "resources"
236
244
  ],
237
- "description": "Every gmail.* operation (resource: the fixed `inbox` alias), since 0.76.0 every basecamp.* operation (resource: a project alias from the connection's `channels` map, or the fixed `account` alias for basecamp.project.list), since 0.78.0 every sharepoint.* operation (resource: a site alias from the connection's `channels` map) and since 0.86.0 every calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.*, excel.* and teams.* operation (resource: the provider's fixed alias for the person's own data, or for teams.* a channel alias from the connection's `channels` map): user mode only \u2014 the app acts as the signed-in person after their own consent.",
245
+ "description": "Every gmail.* operation (resource: the fixed `inbox` alias), since 0.76.0 every basecamp.* operation (resource: a project alias from the connection's `channels` map, or the fixed `account` alias for basecamp.project.list), since 0.78.0 every sharepoint.* operation (resource: a site alias from the connection's `channels` map) and since 0.86.0 every calendar.*, drive.*, docs.*, sheets.*, outlook.*, onedrive.*, excel.* and teams.* operation (resource: the provider's fixed alias for the person's own data, or for teams.* a channel alias from the connection's `channels` map): user mode only — the app acts as the signed-in person after their own consent.",
238
246
  "properties": {
239
247
  "identity": {
240
248
  "type": "string",
@@ -252,7 +260,7 @@
252
260
  "identity",
253
261
  "resources"
254
262
  ],
255
- "description": "slack.message.post, slack.channel.history, slack.message.update, slack.message.delete: the app declares the mode \u2014 app (acts as the company's Slack app) or user (acts as the signed-in person).",
263
+ "description": "slack.message.post, slack.channel.history, slack.message.update, slack.message.delete: the app declares the mode — app (acts as the company's Slack app) or user (acts as the signed-in person).",
256
264
  "properties": {
257
265
  "identity": {
258
266
  "$ref": "#/definitions/IntegrationIdentityMode"
@@ -269,7 +277,7 @@
269
277
  "identity",
270
278
  "resources"
271
279
  ],
272
- "description": "slack.dm.send, slack.user.lookup (0.71.0): app mode only \u2014 the company's Slack app messages people or resolves them by email; the resource is the fixed `workspace` alias.",
280
+ "description": "slack.dm.send, slack.user.lookup (0.71.0): app mode only — the company's Slack app messages people or resolves them by email; the resource is the fixed `workspace` alias.",
273
281
  "properties": {
274
282
  "identity": {
275
283
  "type": "string",
@@ -567,6 +575,30 @@
567
575
  },
568
576
  "service.request": {
569
577
  "$ref": "#/definitions/ServiceOperation"
578
+ },
579
+ "zohobooks.contact.list": {
580
+ "$ref": "#/definitions/UserModeChannelOperation"
581
+ },
582
+ "zohobooks.purchaseorder.list": {
583
+ "$ref": "#/definitions/UserModeChannelOperation"
584
+ },
585
+ "zohobooks.purchaseorder.read": {
586
+ "$ref": "#/definitions/UserModeChannelOperation"
587
+ },
588
+ "zohobooks.bill.create": {
589
+ "$ref": "#/definitions/UserModeChannelOperation"
590
+ },
591
+ "zohobooks.bill.read": {
592
+ "$ref": "#/definitions/UserModeChannelOperation"
593
+ },
594
+ "zohobooks.invoice.create": {
595
+ "$ref": "#/definitions/UserModeChannelOperation"
596
+ },
597
+ "zohobooks.invoice.read": {
598
+ "$ref": "#/definitions/UserModeChannelOperation"
599
+ },
600
+ "zohobooks.invoice.pdf": {
601
+ "$ref": "#/definitions/UserModeChannelOperation"
570
602
  }
571
603
  }
572
604
  }
@@ -1013,6 +1045,46 @@
1013
1045
  "required": [
1014
1046
  "service.request"
1015
1047
  ]
1048
+ },
1049
+ {
1050
+ "required": [
1051
+ "zohobooks.contact.list"
1052
+ ]
1053
+ },
1054
+ {
1055
+ "required": [
1056
+ "zohobooks.purchaseorder.list"
1057
+ ]
1058
+ },
1059
+ {
1060
+ "required": [
1061
+ "zohobooks.purchaseorder.read"
1062
+ ]
1063
+ },
1064
+ {
1065
+ "required": [
1066
+ "zohobooks.bill.create"
1067
+ ]
1068
+ },
1069
+ {
1070
+ "required": [
1071
+ "zohobooks.bill.read"
1072
+ ]
1073
+ },
1074
+ {
1075
+ "required": [
1076
+ "zohobooks.invoice.create"
1077
+ ]
1078
+ },
1079
+ {
1080
+ "required": [
1081
+ "zohobooks.invoice.read"
1082
+ ]
1083
+ },
1084
+ {
1085
+ "required": [
1086
+ "zohobooks.invoice.pdf"
1087
+ ]
1016
1088
  }
1017
1089
  ]
1018
1090
  }
@@ -1437,6 +1509,46 @@
1437
1509
  "required": [
1438
1510
  "warehouse.table.append"
1439
1511
  ]
1512
+ },
1513
+ {
1514
+ "required": [
1515
+ "zohobooks.contact.list"
1516
+ ]
1517
+ },
1518
+ {
1519
+ "required": [
1520
+ "zohobooks.purchaseorder.list"
1521
+ ]
1522
+ },
1523
+ {
1524
+ "required": [
1525
+ "zohobooks.purchaseorder.read"
1526
+ ]
1527
+ },
1528
+ {
1529
+ "required": [
1530
+ "zohobooks.bill.create"
1531
+ ]
1532
+ },
1533
+ {
1534
+ "required": [
1535
+ "zohobooks.bill.read"
1536
+ ]
1537
+ },
1538
+ {
1539
+ "required": [
1540
+ "zohobooks.invoice.create"
1541
+ ]
1542
+ },
1543
+ {
1544
+ "required": [
1545
+ "zohobooks.invoice.read"
1546
+ ]
1547
+ },
1548
+ {
1549
+ "required": [
1550
+ "zohobooks.invoice.pdf"
1551
+ ]
1440
1552
  }
1441
1553
  ]
1442
1554
  }
@@ -17,7 +17,7 @@
17
17
  "status": { "$ref": "#/definitions/DataGrantV2Status" },
18
18
  "operations": {
19
19
  "type": "array",
20
- "maxItems": 78,
20
+ "maxItems": 86,
21
21
  "uniqueItems": true,
22
22
  "items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationOperation" },
23
23
  "description": "Approved operations; each must match this grant's mode. Empty until the first approval. At most the seventy-eight SaaS operations (0.86.0: eleven Slack and Gmail, eleven Basecamp, nine SharePoint, two phone, forty-five Google Workspace and Microsoft 365); a database grant holds up to two (warehouse.view.read, warehouse.table.append)."
@@ -29,6 +29,7 @@
29
29
  "items": { "$ref": "#/definitions/IntegrationResource" },
30
30
  "description": "Approved logical resources. Never inherited by wildcard. Empty until the first approval."
31
31
  },
32
+ "reviewedScope": { "$ref": "#/definitions/ReviewedIntegrationScope" },
32
33
  "expiresAt": { "type": "string", "format": "date-time", "description": "Absent only for explicitly approved ongoing production access." },
33
34
  "expiredAt": { "type": "string", "format": "date-time", "description": "Set by the recovery sweep once expiry has passed; enforcement itself is the UTC check on every call." },
34
35
  "approvedBy": { "type": "string", "minLength": 1, "maxLength": 255 },
@@ -58,6 +59,21 @@
58
59
  }
59
60
  },
60
61
  "definitions": {
62
+ "ReviewedIntegrationScope": {
63
+ "type": "object", "additionalProperties": false,
64
+ "required": ["operations", "resources"],
65
+ "description": "Only for SaaS grants mixing reviewed and automatic access: the previously human-reviewed scope. The remaining scope must satisfy current automatic rules. Homogeneous grants continue using approvedBy alone. Governance validates this is a subset of the grant; database behaviour is unchanged.",
66
+ "properties": {
67
+ "operations": {
68
+ "type": "array", "maxItems": 86, "uniqueItems": true,
69
+ "items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationOperation" }
70
+ },
71
+ "resources": {
72
+ "type": "array", "maxItems": 64, "uniqueItems": true,
73
+ "items": { "$ref": "#/definitions/IntegrationResource" }
74
+ }
75
+ }
76
+ },
61
77
  "IntegrationResource": {
62
78
  "type": "object",
63
79
  "additionalProperties": false,