@fourier-labs/harbour-contracts 0.96.0 → 0.97.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 (32) hide show
  1. package/contract-release.json +26 -26
  2. package/dist/gen-ts/app-integrations-declaration.d.ts +5 -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 +4 -4
  6. package/dist/gen-ts/deployment-app-registration.d.ts +7 -3
  7. package/dist/gen-ts/file-arrival.d.ts +1 -1
  8. package/dist/gen-ts/integration-execute-input-rules.d.ts +130 -0
  9. package/dist/gen-ts/integration-execute-input-rules.js +42 -0
  10. package/dist/gen-ts/integration-execute-request.d.ts +5 -3
  11. package/dist/gen-ts/integration-execute-result.d.ts +36 -0
  12. package/dist/gen-ts/integration-operations.d.ts +35 -0
  13. package/dist/gen-ts/integration-operations.js +45 -0
  14. package/dist/gen-ts/saas-connection.d.ts +6 -6
  15. package/gen-ts/app-integrations-declaration.ts +5 -0
  16. package/gen-ts/contract-release.ts +6 -6
  17. package/gen-ts/data-grant-v2.ts +12 -2
  18. package/gen-ts/deployment-app-registration.ts +12 -3
  19. package/gen-ts/file-arrival.ts +5 -1
  20. package/gen-ts/integration-execute-input-rules.ts +42 -0
  21. package/gen-ts/integration-execute-request.ts +10 -3
  22. package/gen-ts/integration-execute-result.ts +36 -0
  23. package/gen-ts/integration-operations.ts +45 -0
  24. package/gen-ts/saas-connection.ts +18 -3
  25. package/package.json +1 -1
  26. package/schema/app-integrations-declaration.schema.json +71 -1
  27. package/schema/data-grant-v2.schema.json +2 -2
  28. package/schema/deployment-app-registration.schema.json +21 -4
  29. package/schema/file-arrival.schema.json +3 -1
  30. package/schema/integration-execute-request.schema.json +358 -10
  31. package/schema/integration-execute-result.schema.json +156 -1
  32. package/schema/saas-connection.schema.json +3 -3
@@ -301,8 +301,7 @@
301
301
  "required": [
302
302
  "type",
303
303
  "connection",
304
- "resource",
305
- "folderId"
304
+ "resource"
306
305
  ],
307
306
  "properties": {
308
307
  "type": {
@@ -328,15 +327,33 @@
328
327
  "mimeTypes": {
329
328
  "type": "array",
330
329
  "minItems": 1,
331
- "maxItems": 2,
330
+ "maxItems": 4,
332
331
  "uniqueItems": true,
333
332
  "items": {
334
333
  "enum": [
335
334
  "application/pdf",
336
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
335
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
336
+ "image/png",
337
+ "image/jpeg"
337
338
  ]
338
339
  }
340
+ },
341
+ "configurableFolder": {
342
+ "const": true,
343
+ "description": "The app owner may select a folder at runtime within the declared connection and resource."
344
+ }
345
+ },
346
+ "if": {
347
+ "not": {
348
+ "required": [
349
+ "configurableFolder"
350
+ ]
339
351
  }
352
+ },
353
+ "then": {
354
+ "required": [
355
+ "folderId"
356
+ ]
340
357
  }
341
358
  }
342
359
  ]
@@ -50,7 +50,9 @@
50
50
  "mimeType": {
51
51
  "enum": [
52
52
  "application/pdf",
53
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
53
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
54
+ "image/png",
55
+ "image/jpeg"
54
56
  ]
55
57
  },
56
58
  "size": {
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://schemas.harbour.dev/integration-execute-request.schema.json",
4
4
  "title": "IntegrationExecuteRequest",
5
- "description": "Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a \"Posted by <app> on Isomorph\" footer) or as the signed-in person (no name, no footer) \u2014 the same input either way \u2014 and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to 1..50 addresses (to+cc+bcc <= 50 in total), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5 of <= 2 MiB each as base64, replyToMessageId?} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules \u2014 exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved \u2014 are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id \u2014 GraphId, 1..256 characters \u2014 every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.",
5
+ "description": "Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a \"Posted by <app> on Isomorph\" footer) or as the signed-in person (no name, no footer) — the same input either way — and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to 1..50 addresses (to+cc+bcc <= 50 in total), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5 of <= 2 MiB each as base64, replyToMessageId?} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules — exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved — are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id — GraphId, 1..256 characters — every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -153,7 +153,7 @@
153
153
  },
154
154
  "recordingId": {
155
155
  "$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId",
156
- "description": "basecamp.comment.list / basecamp.comment.post (0.76.0): the Basecamp recording commented on \u2014 a message, a to-do or any other id a basecamp.* result named."
156
+ "description": "basecamp.comment.list / basecamp.comment.post (0.76.0): the Basecamp recording commented on — a message, a to-do or any other id a basecamp.* result named."
157
157
  },
158
158
  "content": {
159
159
  "type": "string",
@@ -267,7 +267,7 @@
267
267
  "type": "string",
268
268
  "minLength": 1,
269
269
  "maxLength": 2796203,
270
- "description": "sharepoint.file.upload (0.78.0): the file bytes, standard base64, at most 2 MiB of bytes (2,796,203 base64 characters) \u2014 the Gmail attachment bound. drive.file.upload / onedrive.file.upload (0.86.0): the same bound."
270
+ "description": "sharepoint.file.upload (0.78.0): the file bytes, standard base64, at most 2 MiB of bytes (2,796,203 base64 characters) — the Gmail attachment bound. drive.file.upload / onedrive.file.upload (0.86.0): the same bound."
271
271
  },
272
272
  "columns": {
273
273
  "type": "array",
@@ -385,7 +385,7 @@
385
385
  }
386
386
  ]
387
387
  },
388
- "description": "One row keyed by column name, 1..64 columns; each value a string of at most 4000 characters, a number, a boolean or null. Which columns the alias approves, and whether a value fits its column type (the catalog's columnTypes, which only governance holds), is governance's check \u2014 the way warehouse.view.read's cross-field rules are."
388
+ "description": "One row keyed by column name, 1..64 columns; each value a string of at most 4000 characters, a number, a boolean or null. Which columns the alias approves, and whether a value fits its column type (the catalog's columnTypes, which only governance holds), is governance's check — the way warehouse.view.read's cross-field rules are."
389
389
  },
390
390
  "description": "warehouse.table.append (0.82.0; 1..5000 since 0.85.0): the rows to append, 1..5000 of them, every one a record of the approved columns. Governance refuses an unapproved column, a value outside its column type and a request above its byte cap (8 MiB) with ROW_VALUE_INVALID; nothing is coerced."
391
391
  },
@@ -524,7 +524,7 @@
524
524
  "items": {
525
525
  "$ref": "#/definitions/DocsReplacement"
526
526
  },
527
- "description": "docs.text.replace (0.86.0): the find-and-replace pairs applied over the whole document, 1..64, in order (a mail merge: `{{customer}}` \u2192 the name)."
527
+ "description": "docs.text.replace (0.86.0): the find-and-replace pairs applied over the whole document, 1..64, in order (a mail merge: `{{customer}}` → the name)."
528
528
  },
529
529
  "worksheet": {
530
530
  "type": "string",
@@ -610,9 +610,7 @@
610
610
  "type": "object",
611
611
  "additionalProperties": false,
612
612
  "required": [
613
- "itemId",
614
- "quantity",
615
- "rate"
613
+ "quantity"
616
614
  ],
617
615
  "properties": {
618
616
  "itemId": {
@@ -643,6 +641,16 @@
643
641
  "type": "string",
644
642
  "pattern": "^[0-9]{1,32}$",
645
643
  "maxLength": 32
644
+ },
645
+ "lineItemId": {
646
+ "type": "string",
647
+ "pattern": "^[0-9]{1,32}$",
648
+ "maxLength": 32
649
+ },
650
+ "salesOrderItemId": {
651
+ "type": "string",
652
+ "pattern": "^[0-9]{1,32}$",
653
+ "maxLength": 32
646
654
  }
647
655
  }
648
656
  }
@@ -656,7 +664,7 @@
656
664
  },
657
665
  "mode": {
658
666
  "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationIdentityMode",
659
- "description": "The declared-mode Slack operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0): which of the app's IT-approved modes this call runs under \u2014 app (the company's Slack app; a post goes out under the grant's per-app name and the footer) or user (the signed-in person, after their consent; no name, no footer). Required when the app holds approved grants for both modes (else MODE_REQUIRED); may be omitted when it holds one, which is then used. A named mode without an approved grant is refused with MODE_NOT_GRANTED; governance never falls back to the other mode. Every other operation's mode is fixed, so the field is refused there."
667
+ "description": "The declared-mode Slack operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0): which of the app's IT-approved modes this call runs under — app (the company's Slack app; a post goes out under the grant's per-app name and the footer) or user (the signed-in person, after their consent; no name, no footer). Required when the app holds approved grants for both modes (else MODE_REQUIRED); may be omitted when it holds one, which is then used. A named mode without an approved grant is refused with MODE_NOT_GRANTED; governance never falls back to the other mode. Every other operation's mode is fixed, so the field is refused there."
660
668
  },
661
669
  "sampleRows": {
662
670
  "type": "array",
@@ -3587,7 +3595,54 @@
3587
3595
  }
3588
3596
  }
3589
3597
  }
3590
- ]
3598
+ ],
3599
+ "properties": {
3600
+ "lineItems": {
3601
+ "type": "array",
3602
+ "minItems": 1,
3603
+ "maxItems": 100,
3604
+ "items": {
3605
+ "type": "object",
3606
+ "additionalProperties": false,
3607
+ "required": [
3608
+ "itemId",
3609
+ "quantity",
3610
+ "rate"
3611
+ ],
3612
+ "properties": {
3613
+ "itemId": {
3614
+ "type": "string",
3615
+ "pattern": "^[0-9]{1,32}$",
3616
+ "maxLength": 32
3617
+ },
3618
+ "quantity": {
3619
+ "type": "string",
3620
+ "pattern": "^[0-9]{1,10}(\\.[0-9]{1,6})?$",
3621
+ "maxLength": 17
3622
+ },
3623
+ "rate": {
3624
+ "type": "string",
3625
+ "pattern": "^[0-9]{1,10}(\\.[0-9]{1,6})?$",
3626
+ "maxLength": 17
3627
+ },
3628
+ "taxId": {
3629
+ "type": "string",
3630
+ "pattern": "^[0-9]{1,32}$",
3631
+ "maxLength": 32
3632
+ },
3633
+ "description": {
3634
+ "type": "string",
3635
+ "maxLength": 2000
3636
+ },
3637
+ "purchaseOrderItemId": {
3638
+ "type": "string",
3639
+ "pattern": "^[0-9]{1,32}$",
3640
+ "maxLength": 32
3641
+ }
3642
+ }
3643
+ }
3644
+ }
3645
+ }
3591
3646
  }
3592
3647
  },
3593
3648
  "required": [
@@ -3697,6 +3752,11 @@
3697
3752
  "description": {
3698
3753
  "type": "string",
3699
3754
  "maxLength": 2000
3755
+ },
3756
+ "salesOrderItemId": {
3757
+ "type": "string",
3758
+ "pattern": "^[0-9]{1,32}$",
3759
+ "maxLength": 32
3700
3760
  }
3701
3761
  }
3702
3762
  }
@@ -3782,6 +3842,239 @@
3782
3842
  ]
3783
3843
  }
3784
3844
  }
3845
+ },
3846
+ {
3847
+ "if": {
3848
+ "properties": {
3849
+ "operation": {
3850
+ "const": "zohobooks.salesorder.list"
3851
+ }
3852
+ }
3853
+ },
3854
+ "then": {
3855
+ "x-read-only": true,
3856
+ "x-primary-identity": "user",
3857
+ "properties": {
3858
+ "mode": false,
3859
+ "input": {
3860
+ "propertyNames": {
3861
+ "enum": [
3862
+ "customerId",
3863
+ "pageToken"
3864
+ ]
3865
+ },
3866
+ "required": [
3867
+ "customerId"
3868
+ ]
3869
+ }
3870
+ },
3871
+ "not": {
3872
+ "required": [
3873
+ "idempotencyKey"
3874
+ ]
3875
+ }
3876
+ }
3877
+ },
3878
+ {
3879
+ "if": {
3880
+ "properties": {
3881
+ "operation": {
3882
+ "const": "zohobooks.salesorder.read"
3883
+ }
3884
+ }
3885
+ },
3886
+ "then": {
3887
+ "x-read-only": true,
3888
+ "x-primary-identity": "user",
3889
+ "properties": {
3890
+ "mode": false,
3891
+ "input": {
3892
+ "propertyNames": {
3893
+ "enum": [
3894
+ "documentId"
3895
+ ]
3896
+ },
3897
+ "required": [
3898
+ "documentId"
3899
+ ],
3900
+ "properties": {
3901
+ "documentId": {
3902
+ "pattern": "^[0-9]{1,32}$",
3903
+ "maxLength": 32
3904
+ }
3905
+ }
3906
+ }
3907
+ },
3908
+ "not": {
3909
+ "required": [
3910
+ "idempotencyKey"
3911
+ ]
3912
+ }
3913
+ }
3914
+ },
3915
+ {
3916
+ "if": {
3917
+ "properties": {
3918
+ "operation": {
3919
+ "const": "zohobooks.invoice.reviseDraft"
3920
+ }
3921
+ }
3922
+ },
3923
+ "then": {
3924
+ "x-read-only": false,
3925
+ "x-primary-identity": "user",
3926
+ "properties": {
3927
+ "mode": false,
3928
+ "input": {
3929
+ "propertyNames": {
3930
+ "enum": [
3931
+ "documentId",
3932
+ "lineItems"
3933
+ ]
3934
+ },
3935
+ "required": [
3936
+ "documentId",
3937
+ "lineItems"
3938
+ ],
3939
+ "properties": {
3940
+ "lineItems": {
3941
+ "type": "array",
3942
+ "minItems": 1,
3943
+ "maxItems": 100,
3944
+ "items": {
3945
+ "type": "object",
3946
+ "additionalProperties": false,
3947
+ "required": [
3948
+ "lineItemId",
3949
+ "quantity"
3950
+ ],
3951
+ "properties": {
3952
+ "lineItemId": {
3953
+ "type": "string",
3954
+ "pattern": "^[0-9]{1,32}$",
3955
+ "maxLength": 32
3956
+ },
3957
+ "quantity": {
3958
+ "type": "string",
3959
+ "pattern": "^[0-9]{1,10}(\\.[0-9]{1,6})?$",
3960
+ "maxLength": 17
3961
+ }
3962
+ }
3963
+ }
3964
+ },
3965
+ "documentId": {
3966
+ "pattern": "^[0-9]{1,32}$",
3967
+ "maxLength": 32
3968
+ }
3969
+ }
3970
+ }
3971
+ },
3972
+ "required": [
3973
+ "idempotencyKey"
3974
+ ]
3975
+ }
3976
+ },
3977
+ {
3978
+ "if": {
3979
+ "properties": {
3980
+ "operation": {
3981
+ "const": "zohobooks.creditnote.create"
3982
+ }
3983
+ }
3984
+ },
3985
+ "then": {
3986
+ "x-read-only": false,
3987
+ "x-primary-identity": "user",
3988
+ "properties": {
3989
+ "mode": false,
3990
+ "input": {
3991
+ "propertyNames": {
3992
+ "enum": [
3993
+ "documentId",
3994
+ "documentDate",
3995
+ "referenceNumber",
3996
+ "lineItems"
3997
+ ]
3998
+ },
3999
+ "required": [
4000
+ "documentId",
4001
+ "documentDate",
4002
+ "lineItems"
4003
+ ],
4004
+ "properties": {
4005
+ "lineItems": {
4006
+ "type": "array",
4007
+ "minItems": 1,
4008
+ "maxItems": 100,
4009
+ "items": {
4010
+ "type": "object",
4011
+ "additionalProperties": false,
4012
+ "required": [
4013
+ "lineItemId",
4014
+ "quantity"
4015
+ ],
4016
+ "properties": {
4017
+ "lineItemId": {
4018
+ "type": "string",
4019
+ "pattern": "^[0-9]{1,32}$",
4020
+ "maxLength": 32
4021
+ },
4022
+ "quantity": {
4023
+ "type": "string",
4024
+ "pattern": "^[0-9]{1,10}(\\.[0-9]{1,6})?$",
4025
+ "maxLength": 17
4026
+ }
4027
+ }
4028
+ }
4029
+ },
4030
+ "documentId": {
4031
+ "pattern": "^[0-9]{1,32}$",
4032
+ "maxLength": 32
4033
+ }
4034
+ }
4035
+ }
4036
+ },
4037
+ "required": [
4038
+ "idempotencyKey"
4039
+ ]
4040
+ }
4041
+ },
4042
+ {
4043
+ "if": {
4044
+ "properties": {
4045
+ "operation": {
4046
+ "const": "zohobooks.creditnote.read"
4047
+ }
4048
+ }
4049
+ },
4050
+ "then": {
4051
+ "x-read-only": true,
4052
+ "x-primary-identity": "user",
4053
+ "properties": {
4054
+ "mode": false,
4055
+ "input": {
4056
+ "propertyNames": {
4057
+ "enum": [
4058
+ "documentId"
4059
+ ]
4060
+ },
4061
+ "required": [
4062
+ "documentId"
4063
+ ],
4064
+ "properties": {
4065
+ "documentId": {
4066
+ "pattern": "^[0-9]{1,32}$",
4067
+ "maxLength": 32
4068
+ }
4069
+ }
4070
+ }
4071
+ },
4072
+ "not": {
4073
+ "required": [
4074
+ "idempotencyKey"
4075
+ ]
4076
+ }
4077
+ }
3785
4078
  }
3786
4079
  ],
3787
4080
  "definitions": {
@@ -5135,6 +5428,61 @@
5135
5428
  "input": {
5136
5429
  "documentId": "456"
5137
5430
  }
5431
+ },
5432
+ {
5433
+ "connection": "company-zohobooks",
5434
+ "resource": "organization",
5435
+ "operation": "zohobooks.salesorder.list",
5436
+ "input": {
5437
+ "customerId": "123"
5438
+ }
5439
+ },
5440
+ {
5441
+ "connection": "company-zohobooks",
5442
+ "resource": "organization",
5443
+ "operation": "zohobooks.salesorder.read",
5444
+ "input": {
5445
+ "documentId": "123"
5446
+ }
5447
+ },
5448
+ {
5449
+ "connection": "company-zohobooks",
5450
+ "resource": "organization",
5451
+ "operation": "zohobooks.invoice.reviseDraft",
5452
+ "input": {
5453
+ "documentId": "123",
5454
+ "lineItems": [
5455
+ {
5456
+ "lineItemId": "456",
5457
+ "quantity": "90"
5458
+ }
5459
+ ]
5460
+ },
5461
+ "idempotencyKey": "940e4390-ce19-4668-97f8-c5e963bb8662"
5462
+ },
5463
+ {
5464
+ "connection": "company-zohobooks",
5465
+ "resource": "organization",
5466
+ "operation": "zohobooks.creditnote.create",
5467
+ "input": {
5468
+ "documentId": "123",
5469
+ "documentDate": "2026-10-24",
5470
+ "lineItems": [
5471
+ {
5472
+ "lineItemId": "456",
5473
+ "quantity": "10"
5474
+ }
5475
+ ]
5476
+ },
5477
+ "idempotencyKey": "940e4390-ce19-4668-97f8-c5e963bb8662"
5478
+ },
5479
+ {
5480
+ "connection": "company-zohobooks",
5481
+ "resource": "organization",
5482
+ "operation": "zohobooks.creditnote.read",
5483
+ "input": {
5484
+ "documentId": "123"
5485
+ }
5138
5486
  }
5139
5487
  ]
5140
5488
  }