@fourier-labs/harbour-contracts 0.100.0 → 0.102.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,9 +38,9 @@ exist here first.
38
38
  | `binding-classification-subject.schema.json` | Complete value-free binding-name classification before version allocation | AI governance §5.7 / SEC-UP-01 |
39
39
  | `sanitized-binding-evidence.schema.json` | Exact-version sanitized runtime-binding allocation receipt | AI governance §5.7 / SEC-UP-02 |
40
40
  | `ai-pricing-revision`, `ai-metering-policy`, `ai-budget-ledger-event`, `ai-trace-event-v2` | Additive metering v2 records, including media dimensions and P5 trace attribution; v1 records remain readable | AI governance §9 |
41
- | `app-integrations-declaration.schema.json` | Committed `.isomorph/integrations.json` (`isomorph.app-integrations/2.0`): closed eighty-one-operation set (Slack read/post/update/delete/DM/lookup, Gmail list/thread/read/send/attachment — ADR 0018; Basecamp project/to-do list/to-do/message/comment/people lists, to-do create and complete, message, comment and Campfire posts — ADR 0022; SharePoint list/item/drive/file lists, file search and read, item create and update, file upload — ADR 0024; phone SMS list — ADR 0028; phone number read — ADR 0029; Google Calendar, Drive, Docs and Sheets and Outlook, OneDrive, Excel and Teams reads and writes as the signed-in person — ADR 0032; warehouse view read and table append, the view mapping saying `access: read|append` — ADR 0028; service request) with fixed or declared identity modes; owns the shared operation/identity/environment enums | Dev kit §4 |
41
+ | `app-integrations-declaration.schema.json` | Committed `.isomorph/integrations.json` (`isomorph.app-integrations/2.0`): closed eighty-one-operation set (Slack read/post/update/delete/DM/lookup, Gmail list/thread/read/send/attachment — ADR 0018; Basecamp project/to-do list/to-do/message/comment/people lists, to-do create and complete, message, comment and Campfire posts — ADR 0022; SharePoint list/item/drive/file lists, file search and read, item create and update, file upload — ADR 0024; phone SMS list — ADR 0028; phone number read — ADR 0029; Google Calendar, Drive, Docs and Sheets and Outlook, OneDrive, Excel and Teams reads and writes as the signed-in person — ADR 0032; warehouse view read and table append, the view mapping saying `access: read|append` — ADR 0028; service request) with fixed or declared identity modes (`gmail.message.send` declared since 0.102.0 — ADR 0045); owns the shared operation/identity/environment enums | Dev kit §4 |
42
42
  | `integration-execute-request`, `integration-execute-result`, `integration-connect-result`, `integration-error-details` | App SDK integrations wire contract on `/_harbour/integrations/*`: per-operation input bounds (a warehouse read filters, groups, aggregates and sorts in the warehouse and says when it was `truncated` — ADR 0017; Slack and Gmail reads page, posts thread, mail carries HTML and attachments — ADR 0018; a development warehouse read may carry the app's `sampleRows` and the result says `sample` — ADR 0021; Basecamp lists page by `pageToken`, ids are digit strings, writes answer `{id, appUrl}` — ADR 0022; SharePoint lists page by a Graph `$skiptoken`, ids are Graph ids, `fields` is a bounded record, writes answer `{id, webUrl}` — ADR 0024; a warehouse append takes `rows`, a bounded list of records, and a phone SMS list takes `since`/`from` — ADR 0028; a phone number read takes no input — ADR 0029; calendar events take RFC 3339 `start`/`end`, Sheets and Excel take `values`, a bounded matrix of cells, Docs take `replacements`, and the event read, document read, values and worksheet results have their own shapes — ADR 0032), the per-operation result shapes, consent states and the closed `details.code` reason set | Dev kit §4, §8 |
43
- | `saas-connection.schema.json`, `data-grant-v2.schema.json` | Tenant SaaS connection record (one installation and channel map per tenant and provider; IT's optional `oauthClient`, `userModeApproval`, `directMessages`, `externalRecipients` — ADR 0018; a Basecamp connection's pinned `account` and project ids in `channels` — ADR 0022; a SharePoint connection's pinned Microsoft 365 tenant in `account` and site ids in `channels` — ADR 0024; up to 20 `approvers` who may approve access requests beside the tenant admins — ADR 0026; a phone connection's E.164 numbers in `channels` — ADR 0028, which an app reads back by alias — ADR 0029; eight Google Workspace and Microsoft 365 providers, user-consent only, a Teams connection's pinned tenant and `<teamId>/<channelId>` channel map — ADR 0032; optional `startedBy` when a builder's first request created the connection — ADR 0034) and the additive `harbour.data-grant/2.0` grant record with structured resources and provisioning lease/cleanup (status machine generated; legacy `data-grant` untouched) | Dev kit §5 |
43
+ | `saas-connection.schema.json`, `data-grant-v2.schema.json` | Tenant SaaS connection record (one installation and channel map per tenant and provider; IT's optional `oauthClient`, `userModeApproval`, `directMessages`, `externalRecipients` — ADR 0018; a Basecamp connection's pinned `account` and project ids in `channels` — ADR 0022; a SharePoint connection's pinned Microsoft 365 tenant in `account` and site ids in `channels` — ADR 0024; up to 20 `approvers` who may approve access requests beside the tenant admins — ADR 0026; a phone connection's E.164 numbers in `channels` — ADR 0028, which an app reads back by alias — ADR 0029; eight Google Workspace and Microsoft 365 providers, user-consent only, a Teams connection's pinned tenant and `<teamId>/<channelId>` channel map — ADR 0032; optional `startedBy` when a builder's first request created the connection — ADR 0034; a Gmail connection's `installation` is the company mailbox IT connected, `botName` its address — ADR 0045) and the additive `harbour.data-grant/2.0` grant record with structured resources and provisioning lease/cleanup (status machine generated; legacy `data-grant` untouched) | Dev kit §5 |
44
44
  | `kit-bundle-manifest.schema.json`, `kit-lock.schema.json` | Published kit bundle identity (SDK tarball digest plus optional `sdk.version` and https `sdk.url` download location) and the committed `.isomorph/kit.lock.json` the CodeBuild SDK-vendoring gate compares against | Dev kit §3 |
45
45
  | `deploy-response.schema.json` | 202 Accepted body for `POST /v1/tenants/{tenant}/apps/{app}/deployments`, returned only after the authoritative QUEUED version exists and its SQS message is accepted | — |
46
46
  | `deployment-source.schema.json` | Credential-free source in the `DeployRequest` body: `provider` is `github` or `gitlab`, `repositoryUrl` is shape-only and the deployment control plane checks the host against the tenant's source connection (ADR 0023); credentials are resolved internally by MaterializeSource | — |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schema": "harbour.contract-release/1.0",
3
- "repositoryVersion": "0.100.0.0",
4
- "publishVersion": "0.100.0",
5
- "publishTag": "v0.100.0",
6
- "schemasDigest": "sha256:5a49b987a272e49117b6f47b8fb90854b8eb128516aa5409ac8f016c97fb3c1b",
3
+ "repositoryVersion": "0.102.0.0",
4
+ "publishVersion": "0.102.0",
5
+ "publishTag": "v0.102.0",
6
+ "schemasDigest": "sha256:4f2533c3551745294cc524c8c02534da922da4379d0ca1499a86a51530a91624",
7
7
  "aiFixturesDigest": "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6",
8
- "generatedGoDigest": "sha256:7052659c5f2394396b097b4102a5d4b0369c11ddc7aa46a6e839825fee65ad8a",
9
- "generatedTypeScriptDigest": "sha256:5c9d13b601b1deb027b2518e4471316d72b079a647e99c3e26f0443f57861812",
8
+ "generatedGoDigest": "sha256:1436432fefcb98fb76d198d86a7b8aa67b394ddae74e72340dcc63a4772e50f1",
9
+ "generatedTypeScriptDigest": "sha256:3d68633fe45afe06a3953efd60361e595a059bc6c8c4250dcabe4a4a6107c72b",
10
10
  "generatedExclusions": {
11
11
  "gen-go/contract_release.go": "contains the generated aggregate digests (self-reference)",
12
12
  "gen-ts/contract-release.ts": "contains the generated aggregate digests (self-reference)"
@@ -31,7 +31,7 @@
31
31
  "schema/ai-transform-result.schema.json": "sha256:244e380a36957ab2dcb526ea22aaecb56b53664ce1fda6934d0021c084c99133",
32
32
  "schema/ai-verification-evidence.schema.json": "sha256:bdbc762df256d2a8dd0bdda60d4d13915ce40034cd756af9515871d145f10ad9",
33
33
  "schema/api-error-response.schema.json": "sha256:3d43d5bdf0b59c944e20509d832e6776c3ecdba4d6c245a9118bbe52062a8304",
34
- "schema/app-integrations-declaration.schema.json": "sha256:e524977ec920abd106130e5a5bfce05ad131b276bac7d7f5f4f28fd4a45fad40",
34
+ "schema/app-integrations-declaration.schema.json": "sha256:2fab400674c79e53279d68cfa97a0bf8dc70d3379d4a0197ca945b4bb446b85b",
35
35
  "schema/app-sign-in.schema.json": "sha256:2093740131b84a69757c2e474d16e32ff5d0bf0edd6f6f8de62ede7f82988f29",
36
36
  "schema/artifact-contract.schema.json": "sha256:8047b461a1c6d07bbe867a259c57bdf5cc0fdf282cf842ec7b683a9e93713b10",
37
37
  "schema/binding-classification-subject.schema.json": "sha256:94028f6914ee7b657824accba27330cf7f5e4e94fd8ed1c2b95521037f49f377",
@@ -42,7 +42,7 @@
42
42
  "schema/data-source.schema.json": "sha256:f32b2b4312bc06e023f4e1da60eed52ddfade59b74c1a1cc8e4b740a6f9c930b",
43
43
  "schema/deploy-request.schema.json": "sha256:c978406c273fa430db262888f0a8646b14e3057f64b0633780fcd6cfa646bc67",
44
44
  "schema/deploy-response.schema.json": "sha256:09064948abdbba7022738285d802ed44648a16ddc2a5b8c2eeb170ae19a66aeb",
45
- "schema/deployment-app-registration.schema.json": "sha256:21fff04264d29d30febe638b05bb75e1317478afbc622614864c722acd32973e",
45
+ "schema/deployment-app-registration.schema.json": "sha256:a70887007087508209da8682f1afc9eb85d916cdc239d62c090940ffb3b56c3f",
46
46
  "schema/deployment-continuation.schema.json": "sha256:55b6945439f2c91cd851dfe4bc9484e67a64091b7763b2d8267354b4a8bfae8c",
47
47
  "schema/deployment-environment.schema.json": "sha256:35b26cae07da9ee375a842b5e87c4570c8eca06775e9fd6dd46eb9eb8aa0df6c",
48
48
  "schema/deployment-preview-retirement.schema.json": "sha256:ccf6b971b7853ca98530272f280fe62cd3aab5425b10b81ae6b717cc949e014f",
@@ -58,7 +58,7 @@
58
58
  "schema/identity-context.schema.json": "sha256:a0be3d3499b70f1641887ec66ab5b21ca5ab71063e9209290905738943a36e59",
59
59
  "schema/integration-connect-result.schema.json": "sha256:3f254b67ef739dc1fa395bdc8c4dc93359b16d481dba2a2d777aa5b6f34b7af8",
60
60
  "schema/integration-error-details.schema.json": "sha256:dd997865b6061c37e298a0ad8e7bcee6186422e04e3ccf63d24d64ef860036d3",
61
- "schema/integration-execute-request.schema.json": "sha256:c65521c33f86a1f32f9b4ba4145a42f3e6b847a25915b36c774c8e89737142ad",
61
+ "schema/integration-execute-request.schema.json": "sha256:f147e3e5b36d37b7c02442109ae31dda3e15f624c0628844c65e6ab2f55e618b",
62
62
  "schema/integration-execute-result.schema.json": "sha256:057b787c28b5f7339a95184fafa5deb7ce2c6441254ad8423ce3238c7dd6996c",
63
63
  "schema/internal-configuration-request.schema.json": "sha256:229d44a3458f68dceddfba802e5c1adcd95523a45af29b93b972706b88461a7d",
64
64
  "schema/kit-bundle-manifest.schema.json": "sha256:d8fc6ca9c30d791e785b021687e084e9171c0ea1a3a176d0f1582fcccf2c0e19",
@@ -68,7 +68,7 @@
68
68
  "schema/promotion-request.schema.json": "sha256:00e96dfd8ddbdba88ea222941d43f3609ff6c815a79e34f6705cab458101c969",
69
69
  "schema/promotion-response.schema.json": "sha256:4129712d2e29d6ef21d93d4bd0bacfd60ada3f2ee0ade852824e64346b817634",
70
70
  "schema/resolved-app-serving.schema.json": "sha256:3b49268e189429310c2a5dcfd45e9d2187c80b6dbb9d329a0177e96e065c31c0",
71
- "schema/saas-connection.schema.json": "sha256:c7767b6b96f45e914e612912a423ee5bc613bfa308087ebd481f3f2c7d799da7",
71
+ "schema/saas-connection.schema.json": "sha256:8482db96f53d1738b89f58035ac1e92bba400c8510a9694ece775936794132aa",
72
72
  "schema/sanitized-binding-evidence.schema.json": "sha256:8aca62c4955771aa379574d3e7f78267c9c4f8018f637ed3f1feb2e47d1bf31b",
73
73
  "schema/secret-reference.schema.json": "sha256:db4e3c2c6a2dbd72d7ea0f9c91cb3b2343a7e67b601996c6818b2ef08e440895",
74
74
  "schema/security-scan-input-v2.schema.json": "sha256:0d418342078c9b5046ea6f4bef3c2c57539285dfee99cc0850d4f0d03739ee13",
@@ -114,11 +114,11 @@
114
114
  "gen-go/contracts_test.go": "sha256:56376287cb1548be1b35ab4c65d6f0b558c5d34885f7a47cb3d9e8d786607bf5",
115
115
  "gen-go/doc.go": "sha256:316d610ce568597a2ce6f38e9782e34fa5920f6f51ab0866e6d061b8a41cd822",
116
116
  "gen-go/error_category.go": "sha256:b2ddea22dcfa030f1a45e721e635485b0bd816cf1f38e7654628a653f5ed4583",
117
- "gen-go/integration_execute_input_rules.go": "sha256:7423e4cf47c1fc6958a833627fe3e1eb939f48ab37dbce449d12a1c049cf9e9c",
117
+ "gen-go/integration_execute_input_rules.go": "sha256:7c516b0566367db217d1d3dab7a01bfd1188cda963ac2e9e094ec2a895039a1e",
118
118
  "gen-go/integration_execute_input_rules_test.go": "sha256:0b04eb79d07764796e72fc9f3407f5852d377e636af96f6cc5ee34e2691113ff",
119
- "gen-go/integration_operations.go": "sha256:391c625030f5e8fbe9365af1273674d989cd67c830c0894fbec44be905eb95ab",
119
+ "gen-go/integration_operations.go": "sha256:a60ac73a7860b1b330b022ddde0c82e7ef146cc40ad22821013c8bdb73f9db28",
120
120
  "gen-go/schema_documents.go": "sha256:c19c0d540f602ee997a84b90e20702d9bc63ed7d27aec267e76c04655fc182e0",
121
- "gen-go/types.go": "sha256:e22cd866bb0ec547eb7324db7ee12994905cf99f63773dce868d6982be07243c",
121
+ "gen-go/types.go": "sha256:40aaa89e666a6c44eb7766e1d7a025da9951814376c2e13d7e37284c3380b1ae",
122
122
  "gen-go/version_state.go": "sha256:4ef8abb126414c0025cf2c59bc9c79a74ef4a2ff89e8f316cbf05acc872371c1"
123
123
  },
124
124
  "generatedTypeScript": {
@@ -143,7 +143,7 @@
143
143
  "gen-ts/ai-transform-result.ts": "sha256:208c66b2aeb425c0cbcc18b7191f6e8989443f2b2c1690648e0e06cc1448ce25",
144
144
  "gen-ts/ai-verification-evidence.ts": "sha256:979439d77f8b47631d1eb3b6c3c54ea6fec41089d2a51dc815fd370b1c4917fc",
145
145
  "gen-ts/api-error-response.ts": "sha256:0fd73be282e7a5d72d6c343a4e64d09c232556b16161167f0cffd6278ed7e3d3",
146
- "gen-ts/app-integrations-declaration.ts": "sha256:5266682c20f5a3f295e44525fe70f04649b00c61a578491eee4536dfb2b59f73",
146
+ "gen-ts/app-integrations-declaration.ts": "sha256:bafdfff8b00d7a244530a354525edb7f0e80bd2ff612b93ca3b9d6fff5a8913a",
147
147
  "gen-ts/app-sign-in.ts": "sha256:bf88671c00e9d9badb5ffc961a7e803cf92cee5a98c2f6c9e3172bbf5aaf0424",
148
148
  "gen-ts/artifact-contract.ts": "sha256:4a6dc6d2caaeed4bb2066cc8a333e6e09e5a0d4745e0b1038293d54ab9403aeb",
149
149
  "gen-ts/binding-classification-digest-helper.ts": "sha256:40fc7d85b4c3f3ffc5caf7d7950c62d013c83ee42058c7453b2100d95817f167",
@@ -155,7 +155,7 @@
155
155
  "gen-ts/data-source.ts": "sha256:2566f0d5c72e44278869f55211cf3d433aeb3552e57ae0ace0ff0b85e769dde6",
156
156
  "gen-ts/deploy-request.ts": "sha256:9581295f0955caf443bb94bdbdd79f168856ad1fd61009002a729bc3483b113f",
157
157
  "gen-ts/deploy-response.ts": "sha256:364d9f1acf32e491498237cdd3f402d5f8e102f208db7748034f4ec68c6a6137",
158
- "gen-ts/deployment-app-registration.ts": "sha256:553583145f210d8ea20e5381054e428f61683a8bde3210f92c75ff16a0cddfb4",
158
+ "gen-ts/deployment-app-registration.ts": "sha256:bc6f5d8410b8dc9595f2feb2a32898ef0ef9f15d2a54da94cd362dc05e1bbd5f",
159
159
  "gen-ts/deployment-continuation.ts": "sha256:cc9f63af4d0da5086a79103789e4f043783b75b4ed11c6512f044439a551a79c",
160
160
  "gen-ts/deployment-environment.ts": "sha256:fc08cabe2e52ebb93cfe84d2019994260f71734c33d0e50e7c63e40ef5b17cef",
161
161
  "gen-ts/deployment-preview-retirement.ts": "sha256:309421d08daf0ade163b6457ed0e5410ec6573c40a59423a6cce14780d9c38db",
@@ -173,10 +173,10 @@
173
173
  "gen-ts/index.ts": "sha256:5e903bb40fbf6599dc0d65171eaef7bc62b52d4cea090124a0c51ef876ad2909",
174
174
  "gen-ts/integration-connect-result.ts": "sha256:4b10f169b6e1af364e9b418de21caa9fadbfb77b43cbff77d741cfae0b52180c",
175
175
  "gen-ts/integration-error-details.ts": "sha256:ec082f2d302c1ef6c0bf768134c2bde4efa5fd5acf9cc0d3ccc4676e857ae43c",
176
- "gen-ts/integration-execute-input-rules.ts": "sha256:ea64438a14f2665d05a9da315f77a075d4d57ff46d93ed910dd44ff6186940f4",
177
- "gen-ts/integration-execute-request.ts": "sha256:9796ad64fe891a973c669776e050a07dbf173f1fcb8580b7626dfe2cdb229c62",
176
+ "gen-ts/integration-execute-input-rules.ts": "sha256:30623d79e0db8e239adffd061c0f9c21af7986b542230620c211eb5870110e1a",
177
+ "gen-ts/integration-execute-request.ts": "sha256:c946d525774ff18125368f67f990fcb637749248fc810f88fed440177d31708f",
178
178
  "gen-ts/integration-execute-result.ts": "sha256:e4772c27615de03284f7686aa49f92ff83b79a12587292129783443cf3ca0f80",
179
- "gen-ts/integration-operations.ts": "sha256:07abf1ad13798132a840aa0b03f36ded5602887a5a205f27e859631b66a93a75",
179
+ "gen-ts/integration-operations.ts": "sha256:fc468da58ab6177e755bb21b0907d7c0a510d6dd291476f0abc1c832c7820734",
180
180
  "gen-ts/internal-configuration-request.ts": "sha256:7032437797473fa1c581d134adef2f3d4b4f4132f482cd17743e9d47097886bc",
181
181
  "gen-ts/kit-bundle-manifest.ts": "sha256:73f5271d2eb3b5ce91e28933c5ab6883615c72a3808aa386f860f58f6df8edf6",
182
182
  "gen-ts/kit-lock.ts": "sha256:06bddeaf1e2c449c9cc0dbbc696210355a9005349c604d4050eb22385fb4c22e",
@@ -185,7 +185,7 @@
185
185
  "gen-ts/promotion-request.ts": "sha256:ac87da1d4b43cc4418215fd26e154830a78b58dfa9a83ae0e7e073e50ccc871e",
186
186
  "gen-ts/promotion-response.ts": "sha256:f1c9cafac96faeb02a443a31786f67714e818f38d90108e7af41958396524d6f",
187
187
  "gen-ts/resolved-app-serving.ts": "sha256:3264e85827be47bf391781cb13a1e58318d356f7bee8c2ff3e5b00a062253b23",
188
- "gen-ts/saas-connection.ts": "sha256:c6590d685f23b292de6b29a142f94050145c7a88cf165f554e6fe9ac28142ec4",
188
+ "gen-ts/saas-connection.ts": "sha256:d811dc7a0cd7d1a83e134928fc41e122262add238a8e424ef0b538bde532380d",
189
189
  "gen-ts/sanitized-binding-digest-helper.ts": "sha256:c47d7fdc20500e2e72c23fe39ecdd610fc397c47fcba51335ebec3c380fe6872",
190
190
  "gen-ts/sanitized-binding-evidence.ts": "sha256:fb3c753ebba1caf71bc85afee13db27172c17208b89258880e46edc7169b2744",
191
191
  "gen-ts/secret-reference.ts": "sha256:731fbcde7315cb4128d5b01bdbe6e6f248f13c7ad8dea04a3a4e927ac4890198",
@@ -11,7 +11,7 @@ export type DeclaredConnection = {
11
11
  'gmail.thread.list'?: UserModeChannelOperation;
12
12
  'gmail.thread.read'?: UserModeChannelOperation;
13
13
  'gmail.message.read'?: UserModeChannelOperation;
14
- 'gmail.message.send'?: UserModeChannelOperation;
14
+ 'gmail.message.send'?: DeclaredModeChannelOperation;
15
15
  'gmail.attachment.read'?: UserModeChannelOperation;
16
16
  'basecamp.project.list'?: UserModeChannelOperation;
17
17
  'basecamp.todolist.list'?: UserModeChannelOperation;
@@ -124,7 +124,7 @@ export type ColumnName = string;
124
124
  */
125
125
  export type McpToolName = string;
126
126
  /**
127
- * 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. Remote MCP connections add one generic user-only mcp.tool.call on server with exact accepted descriptors; an operation alone never authorizes every provider tool.
127
+ * 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 (except gmail.message.send, declared-mode since 0.102.0), 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 operations (slack.message.post, since 0.71.0 slack.channel.history, slack.message.update and slack.message.delete, and since 0.102.0 gmail.message.send), 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. Remote MCP connections add one generic user-only mcp.tool.call on server with exact accepted descriptors; an operation alone never authorizes every provider tool.
128
128
  */
129
129
  export interface AppIntegrationsDeclaration {
130
130
  schema: 'isomorph.app-integrations/2.0';
@@ -146,7 +146,7 @@ export interface IntegrationPresentation {
146
146
  iconEmoji?: string;
147
147
  }
148
148
  /**
149
- * 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).
149
+ * 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). Since 0.102.0 (ADR 0045) gmail.message.send too, on the fixed `inbox` alias: user sends from the signed-in person's own mailbox after their consent; app sends from the company mailbox IT connected (the `installation` on the Gmail connection).
150
150
  */
151
151
  export interface DeclaredModeChannelOperation {
152
152
  identity: IntegrationIdentityMode;
@@ -160,7 +160,7 @@ export interface AppModeChannelOperation {
160
160
  resources: ResourceNames;
161
161
  }
162
162
  /**
163
- * 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.
163
+ * Every gmail.* operation except gmail.message.send, declared-mode since 0.102.0 (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.
164
164
  */
165
165
  export interface UserModeChannelOperation {
166
166
  identity: 'user';
@@ -1,7 +1,7 @@
1
- export declare const ContractRepositoryVersion: "0.100.0.0";
2
- export declare const ContractPublishVersion: "0.100.0";
3
- export declare const ContractPublishTag: "v0.100.0";
4
- export declare const ContractSchemasDigest: "sha256:5a49b987a272e49117b6f47b8fb90854b8eb128516aa5409ac8f016c97fb3c1b";
1
+ export declare const ContractRepositoryVersion: "0.102.0.0";
2
+ export declare const ContractPublishVersion: "0.102.0";
3
+ export declare const ContractPublishTag: "v0.102.0";
4
+ export declare const ContractSchemasDigest: "sha256:4f2533c3551745294cc524c8c02534da922da4379d0ca1499a86a51530a91624";
5
5
  export declare const ContractAiFixturesDigest: "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
6
- export declare const ContractGeneratedGoDigest: "sha256:7052659c5f2394396b097b4102a5d4b0369c11ddc7aa46a6e839825fee65ad8a";
7
- export declare const ContractGeneratedTypeScriptDigest: "sha256:5c9d13b601b1deb027b2518e4471316d72b079a647e99c3e26f0443f57861812";
6
+ export declare const ContractGeneratedGoDigest: "sha256:1436432fefcb98fb76d198d86a7b8aa67b394ddae74e72340dcc63a4772e50f1";
7
+ export declare const ContractGeneratedTypeScriptDigest: "sha256:3d68633fe45afe06a3953efd60361e595a059bc6c8c4250dcabe4a4a6107c72b";
@@ -1,9 +1,9 @@
1
1
  /* eslint-disable */
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
- export const ContractRepositoryVersion = "0.100.0.0";
4
- export const ContractPublishVersion = "0.100.0";
5
- export const ContractPublishTag = "v0.100.0";
6
- export const ContractSchemasDigest = "sha256:5a49b987a272e49117b6f47b8fb90854b8eb128516aa5409ac8f016c97fb3c1b";
3
+ export const ContractRepositoryVersion = "0.102.0.0";
4
+ export const ContractPublishVersion = "0.102.0";
5
+ export const ContractPublishTag = "v0.102.0";
6
+ export const ContractSchemasDigest = "sha256:4f2533c3551745294cc524c8c02534da922da4379d0ca1499a86a51530a91624";
7
7
  export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
8
- export const ContractGeneratedGoDigest = "sha256:7052659c5f2394396b097b4102a5d4b0369c11ddc7aa46a6e839825fee65ad8a";
9
- export const ContractGeneratedTypeScriptDigest = "sha256:5c9d13b601b1deb027b2518e4471316d72b079a647e99c3e26f0443f57861812";
8
+ export const ContractGeneratedGoDigest = "sha256:1436432fefcb98fb76d198d86a7b8aa67b394ddae74e72340dcc63a4772e50f1";
9
+ export const ContractGeneratedTypeScriptDigest = "sha256:3d68633fe45afe06a3953efd60361e595a059bc6c8c4250dcabe4a4a6107c72b";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration.
2
+ * IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration. Since 0.102.0 (ADR 0045) a company app may open `externalPaths` (path prefixes served to people outside the company after one OTP method) and `public` paths (served with no sign-in at all); `externalPaths` is valid only with `signIn: company`, since an external app already serves everyone through its directory.
3
3
  */
4
4
  export type DeploymentAppRegistration = {
5
5
  tenantId: string;
@@ -21,6 +21,23 @@ export type DeploymentAppRegistration = {
21
21
  * @maxItems 1
22
22
  */
23
23
  signInMethods?: ('phone' | 'email')[];
24
+ /**
25
+ * Path prefixes of a company app that people outside the company reach after signing in with exactly one OTP method (phone or email) against the tenant's external directory; everything else stays company sign-in. Valid only with `signIn: company`.
26
+ */
27
+ externalPaths?: {
28
+ paths: PathPrefixes;
29
+ /**
30
+ * @minItems 1
31
+ * @maxItems 1
32
+ */
33
+ methods: ('phone' | 'email')[];
34
+ };
35
+ /**
36
+ * Path prefixes served with no sign-in at all (a careers page, a status page); everything else keeps the app's sign-in.
37
+ */
38
+ public?: {
39
+ paths: PathPrefixes;
40
+ };
24
41
  /**
25
42
  * Temporary rolling-release input for email targets; writers use eventTarget. Remove after old deployments are migrated.
26
43
  */
@@ -43,6 +60,13 @@ export type DeploymentAppRegistration = {
43
60
  gatewayUrl: string;
44
61
  };
45
62
  };
63
+ /**
64
+ * Absolute path prefixes (`/apply`, `/jobs/`), each at least one character past the leading slash: `/` alone is never a prefix, so a whole app is never opened by accident; no query or fragment.
65
+ *
66
+ * @minItems 1
67
+ * @maxItems 32
68
+ */
69
+ export type PathPrefixes = string[];
46
70
  /**
47
71
  * @minItems 1
48
72
  * @maxItems 100
@@ -2437,6 +2437,11 @@ export declare const IntegrationExecuteInputRules: {
2437
2437
  readonly minLength: 1;
2438
2438
  readonly maxLength: 2048;
2439
2439
  };
2440
+ readonly orderStatus: {
2441
+ readonly kind: "enum";
2442
+ readonly required: false;
2443
+ readonly values: readonly ["all"];
2444
+ };
2440
2445
  };
2441
2446
  };
2442
2447
  readonly "zohobooks.salesorder.read": {
@@ -750,6 +750,7 @@ export const IntegrationExecuteInputRules = {
750
750
  fields: {
751
751
  customerId: { kind: "string", required: true, minLength: 0, maxLength: 32, pattern: "^[0-9]{1,32}$" },
752
752
  pageToken: { kind: "string", required: false, minLength: 1, maxLength: 2048 },
753
+ orderStatus: { kind: "enum", required: false, values: ["all"] },
753
754
  },
754
755
  },
755
756
  "zohobooks.salesorder.read": {
@@ -7,7 +7,7 @@ export type IntegrationExecuteRequest = {
7
7
  */
8
8
  connection: string;
9
9
  /**
10
- * 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.
10
+ * Closed operation set. Extending it is a contracts change. 0.102.0 (ADR 0045) makes gmail.message.send declared-mode: user sends from the signed-in person's mailbox, app sends from the company mailbox IT connected (the installation on the Gmail connection); every other gmail.* operation stays user mode. 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.
11
11
  */
12
12
  operation: 'slack.channel.history' | 'slack.message.post' | 'slack.message.update' | 'slack.message.delete' | 'slack.dm.send' | 'slack.user.lookup' | 'gmail.thread.list' | 'gmail.thread.read' | 'gmail.message.read' | 'gmail.message.send' | 'gmail.attachment.read' | '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.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' | 'phone.sms.list' | 'phone.number.get' | 'calendar.calendar.list' | 'calendar.event.list' | 'calendar.event.read' | 'calendar.event.create' | 'calendar.event.update' | 'calendar.event.cancel' | 'drive.file.list' | 'drive.file.search' | 'drive.file.read' | 'drive.file.upload' | 'drive.folder.create' | 'docs.document.read' | 'docs.document.create' | 'docs.document.append' | 'docs.text.replace' | 'sheets.spreadsheet.read' | 'sheets.values.read' | 'sheets.values.append' | 'sheets.values.update' | 'sheets.values.clear' | 'sheets.spreadsheet.create' | 'outlook.message.list' | 'outlook.message.read' | 'outlook.attachment.read' | 'outlook.message.send' | 'outlook.message.reply' | 'outlook.event.list' | 'outlook.event.read' | 'outlook.event.create' | 'outlook.event.update' | 'outlook.event.cancel' | 'onedrive.file.list' | 'onedrive.file.search' | 'onedrive.file.read' | 'onedrive.file.upload' | 'onedrive.folder.create' | 'excel.worksheet.list' | 'excel.range.read' | 'excel.range.update' | 'excel.table.list' | 'excel.row.add' | 'excel.worksheet.add' | 'teams.message.list' | 'teams.message.post' | 'teams.reply.post' | 'warehouse.view.read' | 'warehouse.table.append' | 'service.request' | 'zohobooks.contact.list' | 'zohobooks.purchaseorder.list' | 'zohobooks.purchaseorder.read' | 'zohobooks.bill.create' | 'zohobooks.bill.read' | 'zohobooks.invoice.create' | 'zohobooks.invoice.read' | 'zohobooks.invoice.pdf' | 'zohobooks.salesorder.list' | 'zohobooks.salesorder.read' | 'zohobooks.invoice.reviseDraft' | 'zohobooks.invoice.markSent' | 'zohobooks.creditnote.create' | 'zohobooks.creditnote.read' | 'mcp.tool.call';
13
13
  /**
@@ -389,13 +389,17 @@ export type IntegrationExecuteRequest = {
389
389
  arguments?: {
390
390
  [k: string]: unknown;
391
391
  };
392
+ /**
393
+ * Zoho salesorder.list: include closed and previously invoiced orders for read-only historical reconciliation; omitted keeps open orders only.
394
+ */
395
+ orderStatus?: 'all';
392
396
  };
393
397
  /**
394
398
  * UUID of one explicit Send action; required for the sends (slack.message.post, slack.dm.send, gmail.message.send), the receipted Basecamp writes (basecamp.todo.create, basecamp.message.post, basecamp.comment.post, basecamp.campfire.post; 0.76.0) the receipted SharePoint writes (sharepoint.item.create, sharepoint.file.upload; 0.78.0) the receipted warehouse append (warehouse.table.append; 0.82.0, where it is also the BigQuery load job id) and the receipted Google Workspace and Microsoft 365 writes (0.86.0: calendar.event.create, drive.file.upload, drive.folder.create, docs.document.create, sheets.values.append, sheets.spreadsheet.create, outlook.message.send, outlook.message.reply, outlook.event.create, onedrive.file.upload, onedrive.folder.create, excel.row.add, excel.worksheet.add, teams.message.post, teams.reply.post), refused everywhere else. The same key with the same payload and subject returns the recorded outcome for seven days. MCP permits omission only for an accepted readOnlyHint=true descriptor; runtime requires the UUID for unknown/write tools.
395
399
  */
396
400
  idempotencyKey?: string;
397
401
  /**
398
- * 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. MCP accepts explicit user only; shared app mode is deferred.
402
+ * The declared-mode operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0; gmail.message.send since 0.102.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; for gmail.message.send, the company mailbox IT connected) or user (the signed-in person, after their consent; no name, no footer; for gmail.message.send, their own mailbox). 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. MCP accepts explicit user only; shared app mode is deferred.
399
403
  */
400
404
  mode?: 'app' | 'user';
401
405
  /**
@@ -65,9 +65,9 @@ export declare const IntegrationOperations: {
65
65
  };
66
66
  readonly "gmail.message.send": {
67
67
  readonly kind: "saas";
68
- readonly identities: readonly ["user"];
68
+ readonly identities: readonly ["user", "app"];
69
69
  readonly readOnly: false;
70
- readonly declaredMode: false;
70
+ readonly declaredMode: true;
71
71
  readonly idempotencyKey: true;
72
72
  };
73
73
  readonly "gmail.attachment.read": {
@@ -90,10 +90,11 @@ export const IntegrationOperations = {
90
90
  "gmail.message.send": {
91
91
  "kind": "saas",
92
92
  "identities": [
93
- "user"
93
+ "user",
94
+ "app"
94
95
  ],
95
96
  "readOnly": false,
96
- "declaredMode": false,
97
+ "declaredMode": true,
97
98
  "idempotencyKey": true
98
99
  },
99
100
  "gmail.attachment.read": {
@@ -122,7 +122,7 @@ export interface SaasConnection {
122
122
  region?: 'us' | 'in' | 'eu' | 'au' | 'jp' | 'ca' | 'sa' | 'cn' | 'ae';
123
123
  }
124
124
  /**
125
- * The one bot installation of the platform-owned provider app in this tenant's workspace.
125
+ * The one bot installation of the platform-owned provider app in this tenant's workspace. Since 0.102.0 (ADR 0045) a Gmail connection may carry one too: the company mailbox IT connected, which an app-mode gmail.message.send sends from. For Gmail, `botName` holds the mailbox address, `workspaceId` the Google Workspace domain the mailbox belongs to, `botUserId` the mailbox's Google account id (empty when unknown) and `scopes` the OAuth scopes granted; no new field.
126
126
  */
127
127
  export interface SaasInstallation {
128
128
  tokenRecordId: string;
@@ -140,7 +140,7 @@ export interface SaasInstallation {
140
140
  */
141
141
  scopes?: string[];
142
142
  /**
143
- * The installed bot user's Slack username as auth.test reported it at installation (0.54.0), e.g. `isomorph_ai` — what a person types after /invite. Absent on installations recorded before it.
143
+ * The installed bot user's Slack username as auth.test reported it at installation (0.54.0), e.g. `isomorph_ai` — what a person types after /invite. Absent on installations recorded before it. On a Gmail connection (0.102.0) the address of the company mailbox IT connected, e.g. `careers@example.com` — the From address of an app-mode gmail.message.send.
144
144
  */
145
145
  botName?: string;
146
146
  }
@@ -14,7 +14,7 @@ export type DeclaredConnection = {
14
14
  'gmail.thread.list'?: UserModeChannelOperation;
15
15
  'gmail.thread.read'?: UserModeChannelOperation;
16
16
  'gmail.message.read'?: UserModeChannelOperation;
17
- 'gmail.message.send'?: UserModeChannelOperation;
17
+ 'gmail.message.send'?: DeclaredModeChannelOperation;
18
18
  'gmail.attachment.read'?: UserModeChannelOperation;
19
19
  'basecamp.project.list'?: UserModeChannelOperation;
20
20
  'basecamp.todolist.list'?: UserModeChannelOperation;
@@ -128,7 +128,7 @@ export type ColumnName = string;
128
128
  export type McpToolName = string;
129
129
 
130
130
  /**
131
- * 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. Remote MCP connections add one generic user-only mcp.tool.call on server with exact accepted descriptors; an operation alone never authorizes every provider tool.
131
+ * 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 (except gmail.message.send, declared-mode since 0.102.0), 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 operations (slack.message.post, since 0.71.0 slack.channel.history, slack.message.update and slack.message.delete, and since 0.102.0 gmail.message.send), 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. Remote MCP connections add one generic user-only mcp.tool.call on server with exact accepted descriptors; an operation alone never authorizes every provider tool.
132
132
  */
133
133
  export interface AppIntegrationsDeclaration {
134
134
  schema: 'isomorph.app-integrations/2.0';
@@ -150,7 +150,7 @@ export interface IntegrationPresentation {
150
150
  iconEmoji?: string;
151
151
  }
152
152
  /**
153
- * 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).
153
+ * 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). Since 0.102.0 (ADR 0045) gmail.message.send too, on the fixed `inbox` alias: user sends from the signed-in person's own mailbox after their consent; app sends from the company mailbox IT connected (the `installation` on the Gmail connection).
154
154
  */
155
155
  export interface DeclaredModeChannelOperation {
156
156
  identity: IntegrationIdentityMode;
@@ -164,7 +164,7 @@ export interface AppModeChannelOperation {
164
164
  resources: ResourceNames;
165
165
  }
166
166
  /**
167
- * 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.
167
+ * Every gmail.* operation except gmail.message.send, declared-mode since 0.102.0 (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.
168
168
  */
169
169
  export interface UserModeChannelOperation {
170
170
  identity: 'user';
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable */
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
3
 
4
- export const ContractRepositoryVersion = "0.100.0.0" as const;
5
- export const ContractPublishVersion = "0.100.0" as const;
6
- export const ContractPublishTag = "v0.100.0" as const;
7
- export const ContractSchemasDigest = "sha256:5a49b987a272e49117b6f47b8fb90854b8eb128516aa5409ac8f016c97fb3c1b" as const;
4
+ export const ContractRepositoryVersion = "0.102.0.0" as const;
5
+ export const ContractPublishVersion = "0.102.0" as const;
6
+ export const ContractPublishTag = "v0.102.0" as const;
7
+ export const ContractSchemasDigest = "sha256:4f2533c3551745294cc524c8c02534da922da4379d0ca1499a86a51530a91624" as const;
8
8
  export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6" as const;
9
- export const ContractGeneratedGoDigest = "sha256:7052659c5f2394396b097b4102a5d4b0369c11ddc7aa46a6e839825fee65ad8a" as const;
10
- export const ContractGeneratedTypeScriptDigest = "sha256:5c9d13b601b1deb027b2518e4471316d72b079a647e99c3e26f0443f57861812" as const;
9
+ export const ContractGeneratedGoDigest = "sha256:1436432fefcb98fb76d198d86a7b8aa67b394ddae74e72340dcc63a4772e50f1" as const;
10
+ export const ContractGeneratedTypeScriptDigest = "sha256:3d68633fe45afe06a3953efd60361e595a059bc6c8c4250dcabe4a4a6107c72b" as const;
@@ -2,7 +2,7 @@
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
3
 
4
4
  /**
5
- * IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration.
5
+ * IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration. Since 0.102.0 (ADR 0045) a company app may open `externalPaths` (path prefixes served to people outside the company after one OTP method) and `public` paths (served with no sign-in at all); `externalPaths` is valid only with `signIn: company`, since an external app already serves everyone through its directory.
6
6
  */
7
7
  export type DeploymentAppRegistration = {
8
8
  tenantId: string;
@@ -24,6 +24,23 @@ export type DeploymentAppRegistration = {
24
24
  * @maxItems 1
25
25
  */
26
26
  signInMethods?: ('phone' | 'email')[];
27
+ /**
28
+ * Path prefixes of a company app that people outside the company reach after signing in with exactly one OTP method (phone or email) against the tenant's external directory; everything else stays company sign-in. Valid only with `signIn: company`.
29
+ */
30
+ externalPaths?: {
31
+ paths: PathPrefixes;
32
+ /**
33
+ * @minItems 1
34
+ * @maxItems 1
35
+ */
36
+ methods: ('phone' | 'email')[];
37
+ };
38
+ /**
39
+ * Path prefixes served with no sign-in at all (a careers page, a status page); everything else keeps the app's sign-in.
40
+ */
41
+ public?: {
42
+ paths: PathPrefixes;
43
+ };
27
44
  /**
28
45
  * Temporary rolling-release input for email targets; writers use eventTarget. Remove after old deployments are migrated.
29
46
  */
@@ -50,6 +67,13 @@ export type DeploymentAppRegistration = {
50
67
  gatewayUrl: string;
51
68
  };
52
69
  };
70
+ /**
71
+ * Absolute path prefixes (`/apply`, `/jobs/`), each at least one character past the leading slash: `/` alone is never a prefix, so a whole app is never opened by accident; no query or fragment.
72
+ *
73
+ * @minItems 1
74
+ * @maxItems 32
75
+ */
76
+ export type PathPrefixes = string[];
53
77
  /**
54
78
  * @minItems 1
55
79
  * @maxItems 100
@@ -779,6 +779,7 @@ export const IntegrationExecuteInputRules = {
779
779
  fields: {
780
780
  customerId: { kind: "string", required: true, minLength: 0, maxLength: 32, pattern: "^[0-9]{1,32}$" },
781
781
  pageToken: { kind: "string", required: false, minLength: 1, maxLength: 2048 },
782
+ orderStatus: { kind: "enum", required: false, values: ["all"] },
782
783
  },
783
784
  },
784
785
  "zohobooks.salesorder.read": {
@@ -10,7 +10,7 @@ export type IntegrationExecuteRequest = {
10
10
  */
11
11
  connection: string;
12
12
  /**
13
- * 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.
13
+ * Closed operation set. Extending it is a contracts change. 0.102.0 (ADR 0045) makes gmail.message.send declared-mode: user sends from the signed-in person's mailbox, app sends from the company mailbox IT connected (the installation on the Gmail connection); every other gmail.* operation stays user mode. 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.
14
14
  */
15
15
  operation:
16
16
  | 'slack.channel.history'
@@ -488,13 +488,17 @@ export type IntegrationExecuteRequest = {
488
488
  arguments?: {
489
489
  [k: string]: unknown;
490
490
  };
491
+ /**
492
+ * Zoho salesorder.list: include closed and previously invoiced orders for read-only historical reconciliation; omitted keeps open orders only.
493
+ */
494
+ orderStatus?: 'all';
491
495
  };
492
496
  /**
493
497
  * UUID of one explicit Send action; required for the sends (slack.message.post, slack.dm.send, gmail.message.send), the receipted Basecamp writes (basecamp.todo.create, basecamp.message.post, basecamp.comment.post, basecamp.campfire.post; 0.76.0) the receipted SharePoint writes (sharepoint.item.create, sharepoint.file.upload; 0.78.0) the receipted warehouse append (warehouse.table.append; 0.82.0, where it is also the BigQuery load job id) and the receipted Google Workspace and Microsoft 365 writes (0.86.0: calendar.event.create, drive.file.upload, drive.folder.create, docs.document.create, sheets.values.append, sheets.spreadsheet.create, outlook.message.send, outlook.message.reply, outlook.event.create, onedrive.file.upload, onedrive.folder.create, excel.row.add, excel.worksheet.add, teams.message.post, teams.reply.post), refused everywhere else. The same key with the same payload and subject returns the recorded outcome for seven days. MCP permits omission only for an accepted readOnlyHint=true descriptor; runtime requires the UUID for unknown/write tools.
494
498
  */
495
499
  idempotencyKey?: string;
496
500
  /**
497
- * 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. MCP accepts explicit user only; shared app mode is deferred.
501
+ * The declared-mode operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0; gmail.message.send since 0.102.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; for gmail.message.send, the company mailbox IT connected) or user (the signed-in person, after their consent; no name, no footer; for gmail.message.send, their own mailbox). 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. MCP accepts explicit user only; shared app mode is deferred.
498
502
  */
499
503
  mode?: 'app' | 'user';
500
504
  /**
@@ -91,10 +91,11 @@ export const IntegrationOperations = {
91
91
  "gmail.message.send": {
92
92
  "kind": "saas",
93
93
  "identities": [
94
- "user"
94
+ "user",
95
+ "app"
95
96
  ],
96
97
  "readOnly": false,
97
- "declaredMode": false,
98
+ "declaredMode": true,
98
99
  "idempotencyKey": true
99
100
  },
100
101
  "gmail.attachment.read": {
@@ -334,7 +334,7 @@ export interface SaasConnection {
334
334
  region?: 'us' | 'in' | 'eu' | 'au' | 'jp' | 'ca' | 'sa' | 'cn' | 'ae';
335
335
  }
336
336
  /**
337
- * The one bot installation of the platform-owned provider app in this tenant's workspace.
337
+ * The one bot installation of the platform-owned provider app in this tenant's workspace. Since 0.102.0 (ADR 0045) a Gmail connection may carry one too: the company mailbox IT connected, which an app-mode gmail.message.send sends from. For Gmail, `botName` holds the mailbox address, `workspaceId` the Google Workspace domain the mailbox belongs to, `botUserId` the mailbox's Google account id (empty when unknown) and `scopes` the OAuth scopes granted; no new field.
338
338
  */
339
339
  export interface SaasInstallation {
340
340
  tokenRecordId: string;
@@ -352,7 +352,7 @@ export interface SaasInstallation {
352
352
  */
353
353
  scopes?: string[];
354
354
  /**
355
- * The installed bot user's Slack username as auth.test reported it at installation (0.54.0), e.g. `isomorph_ai` — what a person types after /invite. Absent on installations recorded before it.
355
+ * The installed bot user's Slack username as auth.test reported it at installation (0.54.0), e.g. `isomorph_ai` — what a person types after /invite. Absent on installations recorded before it. On a Gmail connection (0.102.0) the address of the company mailbox IT connected, e.g. `careers@example.com` — the From address of an app-mode gmail.message.send.
356
356
  */
357
357
  botName?: string;
358
358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fourier-labs/harbour-contracts",
3
- "version": "0.100.0",
3
+ "version": "0.102.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 — 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. Remote MCP connections add one generic user-only mcp.tool.call on server with exact accepted descriptors; an operation alone never authorizes every provider tool.",
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 (except gmail.message.send, declared-mode since 0.102.0), 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 operations (slack.message.post, since 0.71.0 slack.channel.history, slack.message.update and slack.message.delete, and since 0.102.0 gmail.message.send), 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. Remote MCP connections add one generic user-only mcp.tool.call on server with exact accepted descriptors; an operation alone never authorizes every provider tool.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -151,7 +151,7 @@
151
151
  "zohobooks.creditnote.read",
152
152
  "mcp.tool.call"
153
153
  ],
154
- "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."
154
+ "description": "Closed operation set. Extending it is a contracts change. 0.102.0 (ADR 0045) makes gmail.message.send declared-mode: user sends from the signed-in person's mailbox, app sends from the company mailbox IT connected (the installation on the Gmail connection); every other gmail.* operation stays user mode. 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."
155
155
  },
156
156
  "IntegrationIdentityMode": {
157
157
  "type": "string",
@@ -250,7 +250,7 @@
250
250
  "identity",
251
251
  "resources"
252
252
  ],
253
- "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.",
253
+ "description": "Every gmail.* operation except gmail.message.send, declared-mode since 0.102.0 (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.",
254
254
  "properties": {
255
255
  "identity": {
256
256
  "type": "string",
@@ -268,7 +268,7 @@
268
268
  "identity",
269
269
  "resources"
270
270
  ],
271
- "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).",
271
+ "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). Since 0.102.0 (ADR 0045) gmail.message.send too, on the fixed `inbox` alias: user sends from the signed-in person's own mailbox after their consent; app sends from the company mailbox IT connected (the `installation` on the Gmail connection).",
272
272
  "properties": {
273
273
  "identity": {
274
274
  "$ref": "#/definitions/IntegrationIdentityMode"
@@ -369,7 +369,7 @@
369
369
  "$ref": "#/definitions/UserModeChannelOperation"
370
370
  },
371
371
  "gmail.message.send": {
372
- "$ref": "#/definitions/UserModeChannelOperation"
372
+ "$ref": "#/definitions/DeclaredModeChannelOperation"
373
373
  },
374
374
  "gmail.attachment.read": {
375
375
  "$ref": "#/definitions/UserModeChannelOperation"
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://contracts.harbour.dev/deployment-app-registration.schema.json",
4
4
  "title": "DeploymentAppRegistration",
5
- "description": "IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration.",
5
+ "description": "IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration. Since 0.102.0 (ADR 0045) a company app may open `externalPaths` (path prefixes served to people outside the company after one OTP method) and `public` paths (served with no sign-in at all); `externalPaths` is valid only with `signIn: company`, since an external app already serves everyone through its directory.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -98,6 +98,45 @@
98
98
  "minItems": 1,
99
99
  "maxItems": 1
100
100
  },
101
+ "externalPaths": {
102
+ "type": "object",
103
+ "additionalProperties": false,
104
+ "required": [
105
+ "paths",
106
+ "methods"
107
+ ],
108
+ "description": "Path prefixes of a company app that people outside the company reach after signing in with exactly one OTP method (phone or email) against the tenant's external directory; everything else stays company sign-in. Valid only with `signIn: company`.",
109
+ "properties": {
110
+ "paths": {
111
+ "$ref": "#/definitions/pathPrefixes"
112
+ },
113
+ "methods": {
114
+ "type": "array",
115
+ "items": {
116
+ "type": "string",
117
+ "enum": [
118
+ "phone",
119
+ "email"
120
+ ]
121
+ },
122
+ "minItems": 1,
123
+ "maxItems": 1
124
+ }
125
+ }
126
+ },
127
+ "public": {
128
+ "type": "object",
129
+ "additionalProperties": false,
130
+ "required": [
131
+ "paths"
132
+ ],
133
+ "description": "Path prefixes served with no sign-in at all (a careers page, a status page); everything else keeps the app's sign-in.",
134
+ "properties": {
135
+ "paths": {
136
+ "$ref": "#/definitions/pathPrefixes"
137
+ }
138
+ }
139
+ },
101
140
  "mailArrivalTarget": {
102
141
  "oneOf": [
103
142
  {
@@ -216,6 +255,23 @@
216
255
  ]
217
256
  }
218
257
  },
258
+ {
259
+ "if": {
260
+ "required": [
261
+ "externalPaths"
262
+ ]
263
+ },
264
+ "then": {
265
+ "properties": {
266
+ "signIn": {
267
+ "const": "company"
268
+ }
269
+ },
270
+ "required": [
271
+ "signIn"
272
+ ]
273
+ }
274
+ },
219
275
  {
220
276
  "if": {
221
277
  "required": [
@@ -252,6 +308,18 @@
252
308
  }
253
309
  ],
254
310
  "definitions": {
311
+ "pathPrefixes": {
312
+ "type": "array",
313
+ "minItems": 1,
314
+ "maxItems": 32,
315
+ "uniqueItems": true,
316
+ "items": {
317
+ "type": "string",
318
+ "pattern": "^/[^?#]+$",
319
+ "maxLength": 256
320
+ },
321
+ "description": "Absolute path prefixes (`/apply`, `/jobs/`), each at least one character past the leading slash: `/` alone is never a prefix, so a whole app is never opened by accident; no query or fragment."
322
+ },
255
323
  "mailArrivalJobs": {
256
324
  "type": "array",
257
325
  "minItems": 1,
@@ -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) — 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.",
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.",
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 — 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 \u2014 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) — 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) \u2014 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 — 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 \u2014 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}}` → 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}}` \u2192 the name)."
528
528
  },
529
529
  "worksheet": {
530
530
  "type": "string",
@@ -662,6 +662,13 @@
662
662
  "type": "object",
663
663
  "additionalProperties": true,
664
664
  "description": "Validated against the accepted tool inputSchema at runtime; arbitrary nested provider arguments are permitted only here."
665
+ },
666
+ "orderStatus": {
667
+ "type": "string",
668
+ "enum": [
669
+ "all"
670
+ ],
671
+ "description": "Zoho salesorder.list: include closed and previously invoiced orders for read-only historical reconciliation; omitted keeps open orders only."
665
672
  }
666
673
  }
667
674
  },
@@ -672,7 +679,7 @@
672
679
  },
673
680
  "mode": {
674
681
  "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationIdentityMode",
675
- "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. MCP accepts explicit user only; shared app mode is deferred."
682
+ "description": "The declared-mode operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0; gmail.message.send since 0.102.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; for gmail.message.send, the company mailbox IT connected) or user (the signed-in person, after their consent; no name, no footer; for gmail.message.send, their own mailbox). 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. MCP accepts explicit user only; shared app mode is deferred."
676
683
  },
677
684
  "sampleRows": {
678
685
  "type": "array",
@@ -1381,11 +1388,11 @@
1381
1388
  },
1382
1389
  "then": {
1383
1390
  "x-read-only": false,
1391
+ "x-primary-identity": "user",
1384
1392
  "required": [
1385
1393
  "idempotencyKey"
1386
1394
  ],
1387
1395
  "properties": {
1388
- "mode": false,
1389
1396
  "input": {
1390
1397
  "propertyNames": {
1391
1398
  "enum": [
@@ -3868,7 +3875,8 @@
3868
3875
  "propertyNames": {
3869
3876
  "enum": [
3870
3877
  "customerId",
3871
- "pageToken"
3878
+ "pageToken",
3879
+ "orderStatus"
3872
3880
  ]
3873
3881
  },
3874
3882
  "required": [
@@ -537,7 +537,7 @@
537
537
  "status",
538
538
  "installedAt"
539
539
  ],
540
- "description": "The one bot installation of the platform-owned provider app in this tenant's workspace.",
540
+ "description": "The one bot installation of the platform-owned provider app in this tenant's workspace. Since 0.102.0 (ADR 0045) a Gmail connection may carry one too: the company mailbox IT connected, which an app-mode gmail.message.send sends from. For Gmail, `botName` holds the mailbox address, `workspaceId` the Google Workspace domain the mailbox belongs to, `botUserId` the mailbox's Google account id (empty when unknown) and `scopes` the OAuth scopes granted; no new field.",
541
541
  "properties": {
542
542
  "tokenRecordId": {
543
543
  "type": "string",
@@ -576,7 +576,7 @@
576
576
  "type": "string",
577
577
  "minLength": 1,
578
578
  "maxLength": 128,
579
- "description": "The installed bot user's Slack username as auth.test reported it at installation (0.54.0), e.g. `isomorph_ai` — what a person types after /invite. Absent on installations recorded before it."
579
+ "description": "The installed bot user's Slack username as auth.test reported it at installation (0.54.0), e.g. `isomorph_ai` — what a person types after /invite. Absent on installations recorded before it. On a Gmail connection (0.102.0) the address of the company mailbox IT connected, e.g. `careers@example.com` — the From address of an app-mode gmail.message.send."
580
580
  }
581
581
  }
582
582
  },