diff --git a/.agents/skills/v2-api-conventions/SKILL.md b/.agents/skills/v2-api-conventions/SKILL.md index a7174f61e30..0b77773666a 100644 --- a/.agents/skills/v2-api-conventions/SKILL.md +++ b/.agents/skills/v2-api-conventions/SKILL.md @@ -149,6 +149,16 @@ Order matters because each layer is checked against the one before it. 3. **Route** with `defineV2JsonRoute`, declaring `contract`, `auth: v2ApiKeyAuth`, `operation`, `rateLimit`, `errorPolicy`, `mapInput`, `useCase`, `present`. Auth and rate limiting run before parsing. 4. **OpenAPI description** in `lib/api/contracts/v2/openapi/.ts`, then `bun run generate:openapi`. A description that claims behaviour the route does not have is the same class of bug as a wrong schema. +### Public descriptions + +Use the [API description conventions](../../../apps/sim/lib/api/contracts/v2/openapi/README.md) when writing or auditing endpoint and field descriptions. Keep a short action-and-resource summary; use the description for behavior that changes the caller's choice, input, interpretation, or next action. Ordinary operations usually need one to three sentences, with no mandatory minimum. + +Keep archive versus permanent-delete behavior, replacement versus partial-update semantics, partial success, retry safety, redaction, and asynchronous completion explicit. Verify these claims against the implementation. Describe observable behavior without exposing storage formats, locking mechanisms, internal identifiers, deployment architecture, or implementation history unless that detail changes how the caller must use the API. + +Reuse wording across resource families when behavior matches: “Omitted fields remain unchanged,” “Archive,” and “permanently delete.” Prefer “during the request” or “asynchronously” to “settled inline.” Preserve real semantic differences; do not standardize them away. + +Put field-specific rules in the source schema and reuse shared authentication and pagination wording. Shared schema descriptions also feed CLI help, so refer to related operation names rather than HTTP paths. Regenerate OpenAPI, CLI metadata, and CLI docs after changing their source descriptions; never hand-edit generated output. + ## Rule 6 — a transient failure says when to come back A response the caller is *expected* to retry must say how long to wait. Two statuses qualify, and both are wired: @@ -186,11 +196,14 @@ Audited against the primary specs and against Stripe, GitHub, and Google's AIPs. ## Idempotency: at-most-once, not replay -`POST /workflows/{id}/execute` accepts `X-Run-Id`, a caller-supplied run identifier claimed through the `idempotency_key` table (`execution-id-claim.ts`). It is a **uniqueness claim, not an idempotency key**, and the distinction is deliberate and already published in the operation description: +`POST /workflows/{id}/execute` accepts `X-Run-Id` from API-key and OAuth callers; anonymous requests ignore it. It is a **uniqueness claim, not an idempotency key**: + +- An available ID is claimed before execution starts. +- An already claimed ID returns **409** with `error.details.code: "RUN_ID_CONFLICT"`, the run id in `error.details.runId`, and an `X-Run-Id` response header. It never replays the earlier run's result. Get Workflow Run can retrieve an existing run, but a claim does not guarantee a retrievable run. +- IDs of runs that started remain reserved after their execution logs are deleted. +- An ambiguous enqueue can retain the claim indefinitely without creating a retrievable run. A **409** followed by **404** is an unresolved outcome, not proof that execution never started or that the ID will become reusable. -- First use wins and runs. -- Any reuse returns **409** with `error.details.code: "RUN_ID_CONFLICT"`, the run id in `error.details.runId`, and an `X-Run-Id` response header. It never replays the earlier run's result — the client recovers it by polling the runs resource. -- Claims are durable tombstones, so deleting execution logs cannot make an id reusable. +For an uncertain execution outcome, reuse the same run ID if retrying and check Get Workflow Run. Do not promise polling will eventually find a run. If the outcome cannot be verified, do not automatically restart with a fresh ID or an omitted header: either can start another execution. Failures before a run starts can release the claim, so phrase the conflict rule as an ID that is already claimed. That makes the money path safe against double-execution **for callers that opt in**. What it is not: a Stripe-style `Idempotency-Key` that stores and replays the original status and body. Building that means a request fingerprint, a retention window, an in-flight-vs-completed distinction (the expired IETF draft would have these be 422 and 409 respectively), and somewhere to put a large synchronous execution body. It is a designed piece of work, not an increment — do not half-build it by aliasing the header name, which would invite clients written against Stripe semantics to treat our 409 as a hard failure. diff --git a/apps/docs/content/docs/cli/blocks.mdx b/apps/docs/content/docs/cli/blocks.mdx index 9d95f9fc6d5..9affcb3485d 100644 --- a/apps/docs/content/docs/cli/blocks.mdx +++ b/apps/docs/content/docs/cli/blocks.mdx @@ -38,7 +38,7 @@ sim blocks list [options] | `--search ` | No | Case-insensitive substring match against the block id, name, and description. | | `--category ` | No | Restrict to one toolbar category. Accepted values: `blocks`, `tools`, `triggers`. | | `--capability ` | No | Restrict to blocks that can start a workflow — the `triggers` category, blocks declaring `triggerAllowed`, and blocks with trigger-mode fields. Accepted values: `trigger`. | -| `--source ` | No | Restrict to shipped blocks or to this workspace’s deployed custom blocks. Accepted values: `builtin`, `custom`. | +| `--source ` | No | Restrict to built-in blocks or this workspace's deployed custom blocks. Accepted values: `builtin`, `custom`. | | `--sort-by ` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `id`, `name`, `category`. | | `--sort-order ` | No | Sort direction. Accepted values: `asc`, `desc`. | | `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | diff --git a/apps/docs/content/docs/cli/files.mdx b/apps/docs/content/docs/cli/files.mdx index 89d5db8aadc..cad15d5cf51 100644 --- a/apps/docs/content/docs/cli/files.mdx +++ b/apps/docs/content/docs/cli/files.mdx @@ -286,7 +286,7 @@ sim files list [options] | Option | Required | Description | | --- | --- | --- | | `--folder ` | No | Folder path as shown in the app; the leading / is optional. | -| `--recursive` | No | Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. | +| `--recursive` | No | Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter. | | `--no-recursive` | No | Send --recursive as false. | | `--scope ` | No | Which lifecycle set to list: `active` (default) for live files, `archived` for files a delete soft-deleted. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. | | `--search ` | No | Case-insensitive substring match against the file name. | diff --git a/apps/docs/content/docs/cli/knowledge.mdx b/apps/docs/content/docs/cli/knowledge.mdx index 8ffe1dd8613..257f6fa765e 100644 --- a/apps/docs/content/docs/cli/knowledge.mdx +++ b/apps/docs/content/docs/cli/knowledge.mdx @@ -1001,7 +1001,7 @@ sim knowledge list [options] | Option | Required | Description | | --- | --- | --- | -| `--scope ` | No | Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. | +| `--scope ` | No | Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches. Accepted values: `active`, `archived`. | | `--folder ` | No | Folder path as shown in the app; the leading / is optional. | | `--search ` | No | Case-insensitive substring match against the resource name. | | `--sort-by ` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. | diff --git a/apps/docs/content/docs/cli/logs.mdx b/apps/docs/content/docs/cli/logs.mdx index 186a39255de..1a330a5d077 100644 --- a/apps/docs/content/docs/cli/logs.mdx +++ b/apps/docs/content/docs/cli/logs.mdx @@ -53,7 +53,7 @@ sim logs stats [options] | `--level ` | No | Severity level to include. Accepted values: `info`, `error`. | | `--start-date ` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. | | `--end-date ` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. | -| `--segment-count ` | No | Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty. | +| `--segment-count ` | No | Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets. | diff --git a/apps/docs/content/docs/cli/mcp-servers.mdx b/apps/docs/content/docs/cli/mcp-servers.mdx index ca1607cc7f9..ee8ab322e07 100644 --- a/apps/docs/content/docs/cli/mcp-servers.mdx +++ b/apps/docs/content/docs/cli/mcp-servers.mdx @@ -23,13 +23,13 @@ sim mcp-servers create [options] | --- | --- | --- | | `--name ` | Yes | Server display name. | | `--description ` | No | Optional server description. | -| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--transport ` | No | Transport protocol. Defaults to `streamable-http` on creation. Accepted values: `streamable-http`. | | `--url ` | Yes | Absolute HTTP or HTTPS endpoint URL without `{{ENV_VAR}}` references. It determines server identity and is immutable: delete and recreate the server to change endpoints. | | `--auth-type ` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. | | `--headers ` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). | -| `--timeout ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | -| `--retries ` | No | Number of retries per request. Applied server-side as 3 when omitted on create. | -| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. | +| `--timeout ` | No | Per-request timeout in milliseconds. Defaults to 30000 on creation. | +| `--retries ` | No | Number of retries per request. Defaults to 3 on creation. | +| `--enabled` | No | Whether workflows can use the server's tools. Defaults to true on creation. | | `--no-enabled` | No | Send --enabled as false. | | `--oauth-client-id ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | | `--oauth-client-secret ` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. (--oauth-client-secret null sends the word, not JSON null). | @@ -121,7 +121,7 @@ List MCP Server Tools (OAuth login or personal API key required) | Option | Required | Description | | --- | --- | --- | -| `--refresh` | No | Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip. | +| `--refresh` | No | Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools. | | `--no-refresh` | No | Send --refresh as false. | @@ -150,13 +150,13 @@ sim mcp-servers update [options] | --- | --- | --- | | `--name ` | No | Server display name. | | `--description ` | No | Optional server description. | -| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--transport ` | No | Transport protocol. Defaults to `streamable-http` on creation. Accepted values: `streamable-http`. | | `--url ` | No | Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints. | | `--auth-type ` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. | | `--headers ` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). | -| `--timeout ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | -| `--retries ` | No | Number of retries per request. Applied server-side as 3 when omitted on create. | -| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. | +| `--timeout ` | No | Per-request timeout in milliseconds. Defaults to 30000 on creation. | +| `--retries ` | No | Number of retries per request. Defaults to 3 on creation. | +| `--enabled` | No | Whether workflows can use the server's tools. Defaults to true on creation. | | `--no-enabled` | No | Send --enabled as false. | | `--oauth-client-id ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | | `--oauth-client-secret ` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. (--oauth-client-secret null sends the word, not JSON null). | diff --git a/apps/docs/content/docs/cli/reference.mdx b/apps/docs/content/docs/cli/reference.mdx index 6b61ccc2022..7a9eba06685 100644 --- a/apps/docs/content/docs/cli/reference.mdx +++ b/apps/docs/content/docs/cli/reference.mdx @@ -311,7 +311,7 @@ sim blocks list [options] | `--search ` | No | Case-insensitive substring match against the block id, name, and description. | | `--category ` | No | Restrict to one toolbar category. Accepted values: `blocks`, `tools`, `triggers`. | | `--capability ` | No | Restrict to blocks that can start a workflow — the `triggers` category, blocks declaring `triggerAllowed`, and blocks with trigger-mode fields. Accepted values: `trigger`. | -| `--source ` | No | Restrict to shipped blocks or to this workspace’s deployed custom blocks. Accepted values: `builtin`, `custom`. | +| `--source ` | No | Restrict to built-in blocks or this workspace's deployed custom blocks. Accepted values: `builtin`, `custom`. | | `--sort-by ` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `id`, `name`, `category`. | | `--sort-order ` | No | Sort direction. Accepted values: `asc`, `desc`. | | `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | @@ -982,7 +982,7 @@ sim files list [options] | Option | Required | Description | | --- | --- | --- | | `--folder ` | No | Folder path as shown in the app; the leading / is optional. | -| `--recursive` | No | Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. | +| `--recursive` | No | Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter. | | `--no-recursive` | No | Send --recursive as false. | | `--scope ` | No | Which lifecycle set to list: `active` (default) for live files, `archived` for files a delete soft-deleted. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. | | `--search ` | No | Case-insensitive substring match against the file name. | @@ -2296,7 +2296,7 @@ sim knowledge list [options] | Option | Required | Description | | --- | --- | --- | -| `--scope ` | No | Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. | +| `--scope ` | No | Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches. Accepted values: `active`, `archived`. | | `--folder ` | No | Folder path as shown in the app; the leading / is optional. | | `--search ` | No | Case-insensitive substring match against the resource name. | | `--sort-by ` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. | @@ -2498,7 +2498,7 @@ sim logs stats [options] | `--level ` | No | Severity level to include. Accepted values: `info`, `error`. | | `--start-date ` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. | | `--end-date ` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. | -| `--segment-count ` | No | Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty. | +| `--segment-count ` | No | Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets. | @@ -2585,13 +2585,13 @@ sim mcp-servers create [options] | --- | --- | --- | | `--name ` | Yes | Server display name. | | `--description ` | No | Optional server description. | -| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--transport ` | No | Transport protocol. Defaults to `streamable-http` on creation. Accepted values: `streamable-http`. | | `--url ` | Yes | Absolute HTTP or HTTPS endpoint URL without `{{ENV_VAR}}` references. It determines server identity and is immutable: delete and recreate the server to change endpoints. | | `--auth-type ` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. | | `--headers ` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). | -| `--timeout ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | -| `--retries ` | No | Number of retries per request. Applied server-side as 3 when omitted on create. | -| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. | +| `--timeout ` | No | Per-request timeout in milliseconds. Defaults to 30000 on creation. | +| `--retries ` | No | Number of retries per request. Defaults to 3 on creation. | +| `--enabled` | No | Whether workflows can use the server's tools. Defaults to true on creation. | | `--no-enabled` | No | Send --enabled as false. | | `--oauth-client-id ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | | `--oauth-client-secret ` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. (--oauth-client-secret null sends the word, not JSON null). | @@ -2689,7 +2689,7 @@ sim mcp-servers tools list [options] | Option | Required | Description | | --- | --- | --- | -| `--refresh` | No | Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip. | +| `--refresh` | No | Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools. | | `--no-refresh` | No | Send --refresh as false. | @@ -2720,13 +2720,13 @@ sim mcp-servers update [options] | --- | --- | --- | | `--name ` | No | Server display name. | | `--description ` | No | Optional server description. | -| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--transport ` | No | Transport protocol. Defaults to `streamable-http` on creation. Accepted values: `streamable-http`. | | `--url ` | No | Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints. | | `--auth-type ` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. | | `--headers ` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). | -| `--timeout ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | -| `--retries ` | No | Number of retries per request. Applied server-side as 3 when omitted on create. | -| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. | +| `--timeout ` | No | Per-request timeout in milliseconds. Defaults to 30000 on creation. | +| `--retries ` | No | Number of retries per request. Defaults to 3 on creation. | +| `--enabled` | No | Whether workflows can use the server's tools. Defaults to true on creation. | | `--no-enabled` | No | Send --enabled as false. | | `--oauth-client-id ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | | `--oauth-client-secret ` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. (--oauth-client-secret null sends the word, not JSON null). | @@ -4853,7 +4853,7 @@ sim workflows activate create [options] ### sim workflows operations apply -Apply Workflow Operations +Apply Workflow Operations (OAuth login or personal API key required) ```bash sim workflows operations apply [options] @@ -5246,7 +5246,7 @@ sim workflows chat status ### sim workflows chat publish -Publish or replace a workflow’s chat deployment +Publish or replace a workflow’s chat deployment (OAuth login or personal API key required) ```bash sim workflows chat publish [options] @@ -5368,7 +5368,7 @@ sim workflows run [options] | --- | --- | --- | | `--input ` | No | Trigger input as JSON (JSON, or @path / @- to read a file or stdin). | | `--async` | No | Queue the run and return immediately. | -| `--execution-timeout-seconds ` | No | Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true. | +| `--execution-timeout-seconds ` | No | Maximum duration of an asynchronous run, in seconds, capped by the plan's execution timeout. Requires `async: true`; otherwise returns `400`. | | `--select-output ` | No | Return streamed outputs as blockName.path or childWorkflowId.blockName.path; selecting a child workflow applies to every invocation, requires --follow (space-separated, or @path / @- with one value per line; @@value for a literal leading @). | | `--include-file-base64` | No | Inline eligible output files as base64 content. Rejected when `async` is true. | | `--no-include-file-base64` | No | Send --include-file-base64 as false. | @@ -5487,7 +5487,7 @@ sim workflows state get ### sim workflows state replace -Replace Workflow State +Replace Workflow State (OAuth login or personal API key required) ```bash sim workflows state replace [options] diff --git a/apps/docs/content/docs/cli/workflows.mdx b/apps/docs/content/docs/cli/workflows.mdx index eb434be0dcb..9d44a7a7d8b 100644 --- a/apps/docs/content/docs/cli/workflows.mdx +++ b/apps/docs/content/docs/cli/workflows.mdx @@ -44,6 +44,8 @@ Activate Workflow Version (OAuth login or personal API key required) sim workflows operations apply [options] ``` +Apply Workflow Operations (OAuth login or personal API key required) + **Arguments** @@ -415,6 +417,8 @@ Show a workflow’s chat deployment (OAuth login or personal API key required) sim workflows chat publish [options] ``` +Publish or replace a workflow’s chat deployment (OAuth login or personal API key required) + **Arguments** @@ -527,7 +531,7 @@ sim workflows run [options] | --- | --- | --- | | `--input ` | No | Trigger input as JSON (JSON, or @path / @- to read a file or stdin). | | `--async` | No | Queue the run and return immediately. | -| `--execution-timeout-seconds ` | No | Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true. | +| `--execution-timeout-seconds ` | No | Maximum duration of an asynchronous run, in seconds, capped by the plan's execution timeout. Requires `async: true`; otherwise returns `400`. | | `--select-output ` | No | Return streamed outputs as blockName.path or childWorkflowId.blockName.path; selecting a child workflow applies to every invocation, requires --follow (space-separated, or @path / @- with one value per line; @@value for a literal leading @). | | `--include-file-base64` | No | Inline eligible output files as base64 content. Rejected when `async` is true. | | `--no-include-file-base64` | No | Send --include-file-base64 as false. | @@ -642,6 +646,8 @@ sim workflows state get sim workflows state replace [options] ``` +Replace Workflow State (OAuth login or personal API key required) + **Arguments** diff --git a/apps/docs/openapi-v2-billing.json b/apps/docs/openapi-v2-billing.json index 9934dac3b61..97d0ce13726 100644 --- a/apps/docs/openapi-v2-billing.json +++ b/apps/docs/openapi-v2-billing.json @@ -39,7 +39,7 @@ "get": { "operationId": "getBillingStatus", "summary": "Get Billing Status", - "description": "Return the current plan, billing standing, credit allowance, and storage quota. `credits` and `storage` report the payer's pooled allowances and are null unless the caller can manage that payer's billing; they are always null for a workspace API key. Billing history lives at `GET /api/v2/billing/logs`.\n\nOAuth scope: `api:read`.", + "description": "Get the current plan, billing standing, credit allowance, and storage quota. Pooled `credits` and `storage` are visible only to callers who can manage the payer's billing; workspace API keys receive null for both. Use List Billing Logs for credit history.\n\nOAuth scope: `api:read`.", "x-sim-operation": "billing.status.read", "x-oauth-scope": "api:read", "tags": ["Billing"], @@ -107,7 +107,7 @@ "get": { "operationId": "listBillingLogs", "summary": "List Billing Logs", - "description": "List the credit-denominated billing ledger with source filtering and opaque cursor pagination. `period` defaults to `30d`, so an unqualified request covers only the last 30 days: paginating to `nextCursor: null` exhausts that window, not the whole ledger. An inverted custom window is a 400 rather than an empty page.\n\nOAuth scope: `api:read`.", + "description": "List credit usage with source filtering and cursor pagination. The default `period` is `30d`; pagination covers only the selected time window. An inverted custom window returns `400`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "billing.logs.list", "x-oauth-scope": "api:read", "tags": ["Billing"], diff --git a/apps/docs/openapi-v2-files-audit.json b/apps/docs/openapi-v2-files-audit.json index 7469f66274a..956ffba38ed 100644 --- a/apps/docs/openapi-v2-files-audit.json +++ b/apps/docs/openapi-v2-files-audit.json @@ -43,7 +43,7 @@ "get": { "operationId": "listFiles", "summary": "List Files", - "description": "List workspace files with search, sorting, folder filtering, and opaque cursor pagination. Defaults to active files; pass `scope=archived` to page over soft-deleted ones. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List active workspace files with folder filtering, search, sorting, and cursor pagination. Use `scope=archived` to find files available for restoration. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.list", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -64,9 +64,9 @@ "name": "folderPath", "in": "query", "required": false, - "description": "Restrict results to files inside this folder — its direct children, or its whole subtree when `recursive` is true. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict files to this folder, including subfolders when `recursive` is true. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to files inside this folder — its direct children, or its whole subtree when `recursive` is true. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict files to this folder, including subfolders when `recursive` is true. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -74,9 +74,9 @@ "name": "recursive", "in": "query", "required": false, - "description": "Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.", + "description": "Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter.", "schema": { - "description": "Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.", + "description": "Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter.", "enum": [ "true", "1", @@ -361,7 +361,7 @@ "get": { "operationId": "getFileUpload", "summary": "Get File Upload", - "description": "Read an upload session's current state — whether it is still accepting bytes, has finalized into a file, or has failed. Use it to decide whether an interrupted transfer can be resumed or should be abandoned. Like every other upload control leg it requires the signed upload token, and is re-authorized against the workspace on each call.\n\nOAuth scope: `api:read`.", + "description": "Get an upload session's state to determine whether an interrupted transfer can resume. Requires the signed upload token and current workspace access.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.upload.read", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -449,7 +449,7 @@ "delete": { "operationId": "abortFileUpload", "summary": "Abort File Upload", - "description": "Abort an active upload session and release provider-side multipart state.\n\nOAuth scope: `api:write`.", + "description": "Abort an incomplete upload session and discard its uploaded data. Completed uploads cannot be aborted.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.upload.cancel", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -652,7 +652,7 @@ "post": { "operationId": "completeFileUpload", "summary": "Complete File Upload", - "description": "Finalize uploaded bytes, verify provider state, and begin atomic workspace-file registration.\n\nOAuth scope: `api:write`.", + "description": "Finalize an upload and register its workspace file. Repeating a completed upload returns the existing file.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.upload.complete", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -745,7 +745,7 @@ "get": { "operationId": "readFileText", "summary": "Read File Text", - "description": "Extract text from stored file bytes without modifying the file; use `POST /api/v2/files/{fileId}/unzip` to unpack archives. Unsupported types return `400` and point to raw-byte download; generated documents still compiling return `409`, and files above the extraction ceiling return `413`. `degraded: true` means extraction was incomplete or synthesized from raw bytes and is not authoritative; legacy `.doc` and `.ppt` extraction may return this best-effort result. `truncated` means a parser limit stopped extraction.\n\nOAuth scope: `api:read`.", + "description": "Extract text without changing the file. Use Unzip File to unpack archives or Download File for original bytes. Unsupported types return `400`, compiling documents return `409`, and oversized files return `413`. `degraded: true` indicates incomplete or synthesized text, including some legacy `.doc` and `.ppt` results; `truncated: true` indicates a parser limit.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.read_content", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -868,7 +868,7 @@ "get": { "operationId": "bulkDownloadFiles", "summary": "Bulk Download Files", - "description": "Stream files as a zip. Provide comma-separated file IDs and folder paths; folders expand recursively, and unmatched paths are rejected. Each parameter and the resolved selection allow at most 100 entries, with bytes bounded. Oversized selections return `400`; downloads record an audit event. `HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only. `HEAD` omits `Content-Length`; use file metadata to size downloads.\n\nOAuth scope: `api:read`.", + "description": "Stream selected files and recursive folder contents as a ZIP archive. Each selection parameter and the resolved set allow 100 entries; unmatched paths or excess entries return `400`. Total bytes are bounded. Downloads record an audit event. `HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success. `HEAD` omits `Content-Length`; use file metadata to size downloads.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.download", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -899,9 +899,9 @@ "name": "folderPaths", "in": "query", "required": false, - "description": "Folder paths to include with all their descendants, comma-separated. At most 100 entries, and the files they resolve to count against the same 100-file download ceiling. A path that matches no folder is rejected rather than ignored.", + "description": "Comma-separated folder paths whose contents are included recursively. Up to 100 paths; resolved files share the 100-file download limit. Unknown paths are rejected.", "schema": { - "description": "Folder paths to include with all their descendants, comma-separated. At most 100 entries, and the files they resolve to count against the same 100-file download ceiling. A path that matches no folder is rejected rather than ignored.", + "description": "Comma-separated folder paths whose contents are included recursively. Up to 100 paths; resolved files share the 100-file download limit. Unknown paths are rejected.", "type": "string" } } @@ -966,7 +966,7 @@ "post": { "operationId": "unzipFile", "summary": "Unzip File", - "description": "Unzip a `.zip` archive into a new sibling folder, creating workspace files and returning only counts and the destination path. Use `GET /api/v2/files/{fileId}/text` to read text; page `GET /api/v2/files?folderPath=...` to inspect unpacked files. Large archives can take minutes. Only one unzip per archive may run; concurrent attempts return `409`. Archives above the size ceiling or operations exceeding their time budget return `413`.\n\nOAuth scope: `api:write`.", + "description": "Extract a ZIP archive into a new sibling folder and return counts and the destination path. Use List Files to inspect its contents. Large archives can take minutes; concurrent extraction of the same archive returns `409`. Size or processing-time limits return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.extract_archive", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -1055,7 +1055,7 @@ "get": { "operationId": "downloadFile", "summary": "Download File", - "description": "Download current file bytes. Generated documents use compiled artifacts, returning `409` while compiling and `413` above the rendered-size ceiling. Downloading records an audit event. `HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only. `HEAD` omits `Content-Length`; use file metadata to size downloads.\n\nOAuth scope: `api:read`.", + "description": "Download current file bytes. Generated documents use compiled artifacts, returning `409` while compiling and `413` above the rendered-size ceiling. Downloading records an audit event. `HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success. `HEAD` omits `Content-Length`; use file metadata to size downloads.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.download", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -1150,7 +1150,7 @@ "delete": { "operationId": "deleteFile", "summary": "Delete File", - "description": "Archive a workspace file. This is a soft delete: the file stops appearing in the default listing and is no longer readable through the API, but its stored bytes are never removed. Archiving an already-archived file is a `404`, not a no-op. List archived files with `GET /files?scope=archived`, and reverse the delete with `POST /files/{fileId}/restore`.\n\nOAuth scope: `api:write`.", + "description": "Archive a workspace file, retaining its stored bytes and removing API read access. List Files with `scope=archived` finds it; Restore File recovers it. Archiving an already archived file returns `404`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.delete", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -1318,7 +1318,7 @@ "post": { "operationId": "restoreFile", "summary": "Restore File", - "description": "Reverse a soft delete and return the file to the workspace. Not a pure undo: the file comes back at the workspace root, and gains a `_restored` suffix when another file there already holds its name, so read `folderPath` and `name` off the response. Restoring an already-active file returns it unchanged, so a retry is safe. An archived workspace is a `400`, and a name the restore could not free is a `409`.\n\nOAuth scope: `api:write`.", + "description": "Restore an archived file to the workspace root. Name collisions add a `_restored` suffix; use the returned `name` and `folderPath`. An active file returns unchanged. An archived workspace returns `400`; an unresolved name collision returns `409`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.restore", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -1407,7 +1407,7 @@ "get": { "operationId": "getFile", "summary": "Get File Metadata", - "description": "Return file metadata together with the nullable current public-share state.\n\nOAuth scope: `api:read`.", + "description": "Get file metadata and its public-share configuration. The `share` field is null when the file has never been shared.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.read_metadata", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -1500,7 +1500,7 @@ "get": { "operationId": "listAuditLogs", "summary": "List Audit Logs", - "description": "List an organization audit trail with filters and opaque cursor pagination. Requires an Enterprise subscription and organization admin or owner access. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List an organization audit trail with filters and opaque cursor pagination. Requires an Enterprise subscription and organization admin or owner access. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "audit_logs.list", "x-oauth-scope": "api:read", "tags": ["Audit Logs"], @@ -1676,7 +1676,7 @@ "get": { "operationId": "getAuditLog", "summary": "Get Audit Log", - "description": "Return one organization audit-log entry. Requires an Enterprise subscription and organization admin or owner access. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Get one organization audit-log entry. Requires an Enterprise subscription and organization admin or owner access. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "audit_logs.read_detail", "x-oauth-scope": "api:read", "tags": ["Audit Logs"], @@ -1754,7 +1754,7 @@ "post": { "operationId": "moveFileItems", "summary": "Move Files", - "description": "Move up to 1,000 files to a canonical folder path or the workspace root.\n\nOAuth scope: `api:write`.", + "description": "Move up to 1,000 files to a folder path or the workspace root.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.move", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -1828,7 +1828,7 @@ "get": { "operationId": "getFileShare", "summary": "Get File Share", - "description": "Return the nullable current public-share configuration for a file. A file that has never been shared returns `data: null` rather than a 404; a share that was created and later disabled is still returned, with `isActive: false`.\n\nOAuth scope: `api:read`.", + "description": "Get a file's public-share configuration. An unshared file returns `data: null`; a disabled share returns its configuration with `isActive: false`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.share.read", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -1907,7 +1907,7 @@ "patch": { "operationId": "upsertFileShare", "summary": "Enable or Disable File Share", - "description": "Create or partially update a server-tokenized public share. Only `isActive` is required; each other field states what enabling a mode does to it. Enabling any mode other than `public` on a file that has never been shared must carry its credential in the same request. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create or update a file's public share. `isActive` is required; other fields describe their behavior when access modes change. Enabling a protected mode on a previously unshared file requires its credential in the same request. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.share.update", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -1993,7 +1993,7 @@ "patch": { "operationId": "editFileContent", "summary": "Edit File Content", - "description": "Modify part of a text file; `PUT` on this path replaces the whole file. `search_replace` requires one exact match unless `replaceAll` is true. The anchored modes match trimmed complete lines: replacement preserves both boundaries, insertion preserves its anchor, and deletion removes the start but preserves the end. Use `occurrence` for repeated anchors. Non-UTF-8 files return `400`. Concurrent writes return `409`; re-read before retrying.\n\nOAuth scope: `api:write`.", + "description": "Edit part of a UTF-8 file; use Replace File Content to replace it entirely. Search-and-replace requires one exact match unless `replaceAll` is true. Anchored modes match trimmed complete lines; their input descriptions specify boundary handling. Non-UTF-8 files return `400`. Concurrent writes return `409`; re-read before retrying.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.update_content", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -2169,7 +2169,7 @@ "get": { "operationId": "searchFileContent", "summary": "Search File Content", - "description": "Search indexed text in active workspace files and return matching lines with file IDs and line numbers. `folderPaths` limits both results and the coverage reported by `complete` and `indexStatus`. Because indexing is asynchronous, a missing term is unknown rather than absent when `complete` is false. `truncated` means additional matches exist beyond `maxResults`.\n\nOAuth scope: `api:read`.", + "description": "Search indexed text in active workspace files and return matching lines with file IDs and line numbers. `folderPaths` limits both results and reported coverage. Missing matches are inconclusive if `complete` is false or `indexStatus.skippedFiles` or `indexStatus.partialFiles` is nonzero. `truncated` means additional matches exist beyond `maxResults`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.search_content", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -2311,7 +2311,7 @@ "post": { "operationId": "bulkDeleteFiles", "summary": "Delete Files", - "description": "Delete up to 1,000 workspace files in one operation. This is the same soft delete as `DELETE /api/v2/files/{fileId}`: files are archived, not erased, and `POST /api/v2/files/{fileId}/restore` reverses each one.\n\nOAuth scope: `api:write`.", + "description": "Archive up to 1,000 workspace files while retaining their stored bytes. Use Restore File to recover each file.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.delete", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -2382,7 +2382,7 @@ "get": { "operationId": "listFilesFolders", "summary": "List Folders", - "description": "List workspace file folders with optional parent-path filtering and sorting. Pass `scope=archived` to list folders a recursive `DELETE` soft-deleted, which is how a caller finds a path to hand to `POST /api/v2/files/folders/restore`. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List workspace file folders with parent-path filtering and sorting. Use `scope=archived` to find paths accepted by Restore Folder. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "files.folders.list", "x-oauth-scope": "api:read", "tags": ["Files"], @@ -2403,9 +2403,9 @@ "name": "parentPath", "in": "query", "required": false, - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -2542,7 +2542,7 @@ "post": { "operationId": "createFilesFolder", "summary": "Create Folder", - "description": "Create a canonical folder path in a workspace.\n\nOAuth scope: `api:write`.", + "description": "Create a folder at the supplied workspace path.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.folders.create", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -2614,7 +2614,7 @@ "patch": { "operationId": "relocateFilesFolder", "summary": "Rename or Move Folder", - "description": "Rename or move a folder and atomically rewrite descendant canonical paths.\n\nOAuth scope: `api:write`.", + "description": "Rename or move a folder and atomically update all descendant paths.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.folders.update", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -2686,7 +2686,7 @@ "delete": { "operationId": "deleteFilesFolder", "summary": "Delete Folder", - "description": "Delete a folder, optionally including every nested file and folder.\n\nOAuth scope: `api:write`.", + "description": "Archive an empty folder, or set `recursive=true` to archive its files and subfolders. Use Restore Folder to recover the archived contents.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.folders.delete", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -2792,7 +2792,7 @@ "post": { "operationId": "restoreFilesFolder", "summary": "Restore Folder", - "description": "Restore a soft-deleted folder and everything archived with it. `DELETE /api/v2/files/folders` archives recursively, so this is what makes a recursive delete recoverable: without it the archived files stay visible through `GET /api/v2/files?scope=archived` but the folder structure cannot be rebuilt. Address the folder by the path reported by `GET /api/v2/files/folders?scope=archived`; a path that is not archived answers `404`.\n\nOAuth scope: `api:write`.", + "description": "Restore a folder and the files and subfolders archived with it. Use the path from List Folders with `scope=archived`. A path that is not archived returns `404`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "files.folders.restore", "x-oauth-scope": "api:write", "tags": ["Files"], @@ -4204,7 +4204,7 @@ ], "additionalProperties": false, "title": "File metadata", - "description": "Workspace file metadata enriched with nullable public-share state." + "description": "Workspace file metadata and current public-share configuration." }, "V2FileMetadataResponse": { "type": "object", @@ -4325,7 +4325,7 @@ "type": "null" } ], - "description": "Identifier of the affected resource. Always null when `resourceType` is `folder`: folders are addressed by canonical path on this API, so their internal identifiers are withheld rather than published as an id no other endpoint accepts." + "description": "Affected resource ID. Null for folder events, which identify folders by path." }, "resourceName": { "anyOf": [ @@ -4350,7 +4350,7 @@ "description": "Human-readable description of the action." }, "metadata": { - "description": "Arbitrary per-action JSON metadata. Internal folder identifiers are stripped at every nesting level, for the same reason `resourceId` is null on a folder entry." + "description": "Additional JSON details specific to the action." }, "createdAt": { "type": "string", @@ -4896,7 +4896,7 @@ }, "complete": { "type": "boolean", - "description": "True when no file in the searched scope is still pending or failed indexing. It does NOT cover `skippedFiles` (never indexed, such as binaries) or `partialFiles` (indexed only in part), so a missing match is authoritative only when all three are clear. Treat any of them as nonzero meaning unknown rather than absent." + "description": "True when no files in the searched scope have pending or failed indexing. Missing matches remain inconclusive unless this is true and both `indexStatus.skippedFiles` and `indexStatus.partialFiles` are zero." }, "indexStatus": { "type": "object", diff --git a/apps/docs/openapi-v2-knowledge.json b/apps/docs/openapi-v2-knowledge.json index 1bf86bebb74..3433f615d29 100644 --- a/apps/docs/openapi-v2-knowledge.json +++ b/apps/docs/openapi-v2-knowledge.json @@ -39,7 +39,7 @@ "get": { "operationId": "listKnowledgeBases", "summary": "List Knowledge Bases", - "description": "List knowledge bases in a workspace with lifecycle scope, folder filtering, search, sorting, and opaque cursor pagination. `scope` defaults to `active`; pass `archived` to list knowledge bases a `DELETE` archived, each carrying the `deletedAt` instant it was archived, and recover one with `POST /api/v2/knowledge/{knowledgeBaseId}/restore`. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List active knowledge bases in a workspace with folder filtering, search, sorting, and cursor pagination. Use `scope=archived` to find knowledge bases available for restoration. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -60,10 +60,10 @@ "name": "scope", "in": "query", "required": false, - "description": "Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too.", + "description": "Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches.", "schema": { "default": "active", - "description": "Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too.", + "description": "Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches.", "type": "string", "enum": ["active", "archived"] } @@ -72,9 +72,9 @@ "name": "folderPath", "in": "query", "required": false, - "description": "Restrict results to knowledge bases in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to knowledge bases in this folder. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to knowledge bases in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to knowledge bases in this folder. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -190,7 +190,7 @@ "post": { "operationId": "createKnowledgeBase", "summary": "Create Knowledge Base", - "description": "Create a knowledge base in a workspace with optional folder placement and chunking configuration. An unknown `folderPath` is a `404`. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Create a knowledge base in a workspace with optional folder placement and chunking configuration. An unknown `folderPath` returns `404`. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.create", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -264,7 +264,7 @@ "get": { "operationId": "getKnowledgeBase", "summary": "Get Knowledge Base", - "description": "Retrieve a knowledge base by identifier. Inaccessible knowledge bases are reported as not found. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Get a knowledge base's metadata and document counts. Inaccessible knowledge bases return `404`. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.read", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -343,7 +343,7 @@ "patch": { "operationId": "updateKnowledgeBase", "summary": "Update Knowledge Base", - "description": "Update a knowledge base name, description, chunking configuration, or folder placement. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Update a knowledge base's name, description, chunking configuration, or folder placement. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.update", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -428,7 +428,7 @@ "delete": { "operationId": "deleteKnowledgeBase", "summary": "Delete Knowledge Base", - "description": "Delete a knowledge base and its documents.\n\nOAuth scope: `api:write`.", + "description": "Archive a knowledge base, its documents, and its connectors, pausing synchronization. Use List Knowledge Bases with `scope=archived` to find it and Restore Knowledge Base to recover it.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.delete", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -506,7 +506,7 @@ "get": { "operationId": "listKnowledgeConnectors", "summary": "List Knowledge Connectors", - "description": "List external sources connected to a knowledge base with opaque cursor pagination. Stored API keys and encrypted secret material are never returned. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List external sources connected to a knowledge base with cursor pagination. Stored API keys are never returned. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.connectors.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -631,7 +631,7 @@ "post": { "operationId": "createKnowledgeConnector", "summary": "Create Knowledge Connector", - "description": "Validate and connect an external source, then queue its initial synchronization. The apiKey field is write-only and is never returned. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Validate and connect an external source, then queue its initial synchronization. The `apiKey` field is never returned. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.connectors.create", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -718,7 +718,7 @@ "get": { "operationId": "getKnowledgeConnector", "summary": "Get Knowledge Connector", - "description": "Retrieve one connector and its ten most recent synchronization attempts. Stored API keys and encrypted secret material are never returned. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Get one connector and its ten most recent synchronization attempts. Stored API keys are never returned. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.connectors.read", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -806,7 +806,7 @@ "patch": { "operationId": "updateKnowledgeConnector", "summary": "Update Knowledge Connector", - "description": "Update connector source configuration, schedule, or active state. Replacing source configuration on a runnable connector queues an immediate synchronization; paused connectors retain the change without synchronizing until resumed. Source configuration cannot be replaced while synchronization is already in progress. Authentication material cannot be changed through this operation. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Update connector source configuration, schedule, or active state. Replacing source configuration on a runnable connector queues an immediate synchronization; paused connectors retain the change without synchronizing until resumed. Source configuration cannot be replaced while synchronization is already in progress. Authentication material cannot be changed through this operation. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.connectors.update", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -902,7 +902,7 @@ "delete": { "operationId": "deleteKnowledgeConnector", "summary": "Delete Knowledge Connector", - "description": "Delete a connector and optionally its synchronized documents. Documents are retained by default. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Delete a connector and optionally its synchronized documents. Documents are retained by default. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.connectors.delete", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -1002,7 +1002,7 @@ "post": { "operationId": "syncKnowledgeConnector", "summary": "Sync Knowledge Connector", - "description": "Queue a connector synchronization. Rehydration forces existing documents to be fetched and indexed again. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Queue a connector synchronization. Rehydration forces existing documents to be fetched and indexed again. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.connectors.sync", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -1100,7 +1100,7 @@ "get": { "operationId": "listKnowledgeConnectorDocuments", "summary": "List Knowledge Connector Documents", - "description": "List documents produced by one connector with opaque cursor pagination. Excluded documents are omitted unless explicitly requested. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List documents produced by one connector with opaque cursor pagination. Excluded documents are omitted unless explicitly requested. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.connectors.documents.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -1222,7 +1222,7 @@ "patch": { "operationId": "updateKnowledgeConnectorDocuments", "summary": "Update Knowledge Connector Documents", - "description": "Exclude connector documents from knowledge search or restore previously excluded documents. Only documents produced by the selected connector can change. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Exclude connector documents from knowledge search or restore previously excluded documents. Only documents produced by the selected connector can change. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.connectors.documents.update", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -1391,7 +1391,7 @@ "get": { "operationId": "listKnowledgeTags", "summary": "List Tags", - "description": "List the knowledge base's tag vocabulary: each tag's display name, the slot it is stored in, and its field type. Filters and document reads use display names; document writes address slots. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List the knowledge base's tag definitions with display names, write slots, and field types. Filters and document reads use display names; document writes use slots. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.tags.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -1467,7 +1467,7 @@ "post": { "operationId": "createKnowledgeTag", "summary": "Create Tag", - "description": "Define one tag; use `PUT` on this path for several. Write its `tagSlot` on documents, then filter by `displayName`. Omitting `tagSlot` selects the next free slot; exhaustion returns `400`. An occupied slot or duplicate display name returns `409` naming the conflict. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create a tag definition. Write document values by `tagSlot` and filter by `displayName`. Omitting `tagSlot` selects a free slot; exhaustion returns `400`. An occupied slot or duplicate name returns `409`. Use Bulk Save Tag Definitions for multiple definitions. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.tags.create", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -1552,7 +1552,7 @@ "put": { "operationId": "bulkSaveKnowledgeTagDefinitions", "summary": "Bulk Save Tag Definitions", - "description": "Declare multiple tag definitions while leaving unspecified slots unchanged. Updating requires the current name in `originalDisplayName`; otherwise the entry creates a tag. Occupied explicit slots and duplicate display names appear in per-definition `errors`, never overwrite or relocate data, and still return `200`. This writes the vocabulary, not document tag values; set those through the document update endpoint. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create or update tag definitions, preserving unspecified slots. Updates require `originalDisplayName`; other entries create tags. Slot and name conflicts appear in per-definition `errors` with HTTP `200`, leaving conflicting values unchanged. Use Update Document to set tag values. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.tags.bulk_save", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -1634,7 +1634,7 @@ "delete": { "operationId": "deleteKnowledgeTagDefinitions", "summary": "Delete Tag Definitions", - "description": "Remove tag definitions. `unused` defaults to `true`, deleting only definitions with no document values, which can be recreated safely. `unused=false` deletes every definition and irreversibly clears its slot from all documents and chunks. Use `DELETE /api/v2/knowledge/{knowledgeBaseId}/tags/{tagId}` to delete one definition. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Delete unused tag definitions by default. With `unused=false`, permanently delete all definitions and their values from documents and chunks. Use Delete Tag to remove one definition. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.tags.cleanup", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -1723,7 +1723,7 @@ "get": { "operationId": "listKnowledgeDocuments", "summary": "List Documents", - "description": "List documents in a knowledge base with filename search, state filtering, tag filtering, sorting, and opaque cursor pagination. Tag values are keyed by display name; resolve those to write slots with `GET /api/v2/knowledge/{knowledgeBaseId}/tags`.\n\nOAuth scope: `api:read`.", + "description": "List documents with filename search, state and tag filters, sorting, and cursor pagination. Tag values use display names; use List Tags to resolve the slots required for writes.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.documents.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -1892,7 +1892,7 @@ "patch": { "operationId": "bulkUpdateKnowledgeDocuments", "summary": "Bulk Enable or Disable Documents", - "description": "Enable or disable many documents in one request, either by identifier or, with `selectAll`, every document in the knowledge base. Bulk delete is not offered; delete documents one at a time with `DELETE /api/v2/knowledge/{knowledgeBaseId}/documents/{documentId}`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Enable or disable selected documents, or use `selectAll` for the entire knowledge base. Use Delete Document to remove documents individually. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.documents.bulk", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -2160,7 +2160,7 @@ "delete": { "operationId": "abortKnowledgeDocumentUpload", "summary": "Abort Document Upload", - "description": "Abort an incomplete upload and discard provider-side multipart state.\n\nOAuth scope: `api:write`.", + "description": "Abort an incomplete upload session and discard its uploaded data. Completed uploads cannot be aborted.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.documents.upload.cancel", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -2264,7 +2264,7 @@ "post": { "operationId": "createKnowledgeDocumentUploadPartUrls", "summary": "Create Document Upload Part URLs", - "description": "Issue short-lived signed PUT URLs for up to 100 multipart part numbers.\n\nOAuth scope: `api:write`.", + "description": "Create short-lived signed PUT URLs for up to 100 multipart part numbers.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.documents.upload.parts", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -2489,7 +2489,7 @@ "get": { "operationId": "getKnowledgeDocument", "summary": "Get Document", - "description": "Retrieve document detail, processing state, and connector provenance.\n\nOAuth scope: `api:read`.", + "description": "Get document metadata, processing status, and source connector details.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.documents.read", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -2576,7 +2576,7 @@ "patch": { "operationId": "updateKnowledgeDocument", "summary": "Update Document", - "description": "Rename a document, enable or disable it for search, set any of its 17 tag slots, or requeue it for processing. Absent fields are unchanged, and derived indexing state is read-only. Resolve a tag display name to its slot with `GET /api/v2/knowledge/{knowledgeBaseId}/tags`. The returned document omits the connector provenance the detail read carries. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Rename a document, change search availability, update tag slots, or requeue processing. Omitted fields remain unchanged; indexing state is read-only. Use List Tags to resolve names to slots and Get Document for source connector details, which this response omits. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.documents.update", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -2669,7 +2669,7 @@ "delete": { "operationId": "deleteKnowledgeDocument", "summary": "Delete Document", - "description": "Remove one document from a knowledge base. An uploaded document is deleted outright with its indexed chunks. A connector-backed document is instead excluded — its row and embeddings survive, but it stops being searchable and a later sync does not re-add it. Either way it no longer appears in listings or search results.\n\nOAuth scope: `api:write`.", + "description": "Remove a document from listings and search. Uploaded documents and their chunks are deleted. Connector documents are excluded while retaining their stored data; later synchronization does not re-add them.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.documents.delete", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -2758,7 +2758,7 @@ "get": { "operationId": "listKnowledgeFolders", "summary": "List Folders", - "description": "List folders in the knowledge-base folder tree with filtering and sorting. The bounded set is returned in one page; `nextCursor` is always null. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List folders in the knowledge-base folder tree with filtering and sorting. Returns the complete set in one page; `nextCursor` is always null. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.folders.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -2779,9 +2779,9 @@ "name": "parentPath", "in": "query", "required": false, - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -2873,7 +2873,7 @@ "post": { "operationId": "createKnowledgeFolder", "summary": "Create Folder", - "description": "Create a folder in the knowledge-base folder tree. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Create a folder in the knowledge-base folder tree. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.folders.create", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -2945,7 +2945,7 @@ "patch": { "operationId": "relocateKnowledgeFolder", "summary": "Rename or Move Folder", - "description": "Rename or move a folder and atomically rewrite descendant paths. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Rename or move a folder and atomically rewrite descendant paths. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.folders.relocate", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3017,7 +3017,7 @@ "delete": { "operationId": "deleteKnowledgeFolder", "summary": "Delete Folder", - "description": "Delete a folder, optionally including nested folders and knowledge bases.\n\nOAuth scope: `api:write`.", + "description": "Archive an empty folder, or set `recursive=true` to archive its subfolders and knowledge bases. Use Restore Knowledge Base to recover knowledge bases.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.folders.delete", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3126,7 +3126,7 @@ "post": { "operationId": "restoreKnowledgeBase", "summary": "Restore Knowledge Base", - "description": "Un-archive a soft-deleted knowledge base along with its documents and connectors. Idempotent: a knowledge base that is already active is returned unchanged with no audit entry recorded. Restoring into an archived workspace is a `409`, and a knowledge base whose folder is still archived is returned to the workspace root. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Restore a knowledge base and the documents and connectors archived with it. Active knowledge bases return unchanged without a new audit event. An archived workspace returns `409`; an archived containing folder moves the restored knowledge base to the workspace root. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.restore", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3213,7 +3213,7 @@ "post": { "operationId": "addWorkspaceFilesToKnowledgeBase", "summary": "Index Workspace Files", - "description": "Index stored workspace files without re-uploading bytes. Each reference is authorized independently; unreadable, unsupported, or over-100 MB files appear in `failed` while valid files are queued. This partial outcome returns `200`, not multi-status. Queued documents begin as `pending`; the response carries identities only, so read each document endpoint for current processing state. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Queue stored workspace files for indexing without re-uploading bytes. Unreadable, unsupported, or over-100 MB files appear in `failed`; valid files are queued. Partial success returns `200`. Use Get Document to poll processing after receiving document IDs. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.documents.add_workspace_files", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3300,7 +3300,7 @@ "get": { "operationId": "listKnowledgeChunks", "summary": "List Chunks", - "description": "List the passages a document was split into, with content search, enabled filtering, sorting, and opaque cursor pagination. Tag values are projected by slot; resolve slots to display names with `GET /api/v2/knowledge/{knowledgeBaseId}/tags`. A document that has not finished processing answers `409`; the message names the status it is in. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List document chunks with content search, enabled filtering, sorting, and cursor pagination. Tags use slots; use List Tags to resolve display names. Documents that have not finished processing return `409` with their current status. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.chunks.list", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -3463,7 +3463,7 @@ "post": { "operationId": "createKnowledgeChunk", "summary": "Create Chunk", - "description": "Append a chunk, embedding it before the response so it is immediately searchable. It inherits the document's tags and next `chunkIndex`. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Append a chunk, embedding it before the response so it is immediately searchable. It inherits the document's tags and next `chunkIndex`. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.chunks.create", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3556,7 +3556,7 @@ "patch": { "operationId": "bulkUpdateKnowledgeChunks", "summary": "Bulk Update Chunks", - "description": "Enable, disable, or delete multiple chunks in one best-effort request. Unknown chunk IDs appear in `errors` without failing the request; `processed` counts matched chunks, not changes. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Enable, disable, or delete multiple chunks in one best-effort request. Unknown chunk IDs appear in `errors` without failing the request; `processed` counts matched chunks, not changes. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.chunks.bulk", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3651,7 +3651,7 @@ "get": { "operationId": "getKnowledgeChunk", "summary": "Get Chunk", - "description": "Retrieve one chunk of a document, including the exact text that was embedded. A document that has not finished processing answers `409`; the message names the status it is in. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Get one chunk of a document, including the exact text that was embedded. Documents that have not finished processing return `409` with their current status. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.chunks.read", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -3753,7 +3753,7 @@ "patch": { "operationId": "updateKnowledgeChunk", "summary": "Update Chunk", - "description": "Correct chunk text or disable it from search. Changing `content` re-embeds immediately and recalculates document token and character counts; disabling retains the index. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. A document that has not finished processing answers `409`; the message names the status it is in. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Correct chunk text or disable it from search. Changing `content` re-embeds immediately and recalculates document token and character counts; disabling retains the index. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. Documents that have not finished processing return `409` with their current status. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.chunks.update", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3860,7 +3860,7 @@ "delete": { "operationId": "deleteKnowledgeChunk", "summary": "Delete Chunk", - "description": "Permanently remove one chunk and subtract it from document counts. Remaining `chunkIndex` values stay stable and may become non-contiguous. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. A document that has not finished processing answers `409`; the message names the status it is in. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Permanently remove one chunk and subtract it from document counts. Remaining `chunkIndex` values stay stable and may become non-contiguous. Connector-synced chunks are read-only and return `403` with `error.details.code: \"CONNECTOR_MANAGED_RESOURCE_READ_ONLY\"`; change the source and re-sync, or exclude the document. Documents that have not finished processing return `409` with their current status. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.chunks.delete", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -3964,7 +3964,7 @@ "patch": { "operationId": "updateKnowledgeTag", "summary": "Update Tag", - "description": "Rename a tag or change its slot-compatible `fieldType`. Renaming changes read and filter names without moving the slot or its values. Slots are fixed for a tag's lifetime; an incompatible type returns `400` and requires creating a new tag. A duplicate display name returns `409`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Rename a tag or change its slot-compatible `fieldType`. Renaming changes read and filter names without moving the slot or its values. Slots are fixed for a tag's lifetime; an incompatible type returns `400` and requires creating a new tag. A duplicate display name returns `409`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.tags.update", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -4060,7 +4060,7 @@ "delete": { "operationId": "deleteKnowledgeTag", "summary": "Delete Tag", - "description": "Remove a tag definition and clear its slot across every document and chunk in the knowledge base. Without a definition the slot has no meaning, so leaving the values would strand them under a raw slot name — this is not recoverable. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Permanently delete a tag definition and its values from every document and chunk in the knowledge base. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "knowledge.tags.delete", "x-oauth-scope": "api:write", "tags": ["Knowledge Bases"], @@ -4153,7 +4153,7 @@ "get": { "operationId": "getNextKnowledgeTagSlot", "summary": "Get Next Tag Slot", - "description": "Report which slot a create would take for a field type, and how many are left. Advisory rather than a claim: nothing is reserved, and `POST /api/v2/knowledge/{knowledgeBaseId}/tags` assigns the same slot when `tagSlot` is omitted. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Get the next available slot and remaining capacity for a field type. This does not reserve a slot. Create Tag selects a free slot when `tagSlot` is omitted. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.tags.read_next_slot", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -4244,7 +4244,7 @@ "get": { "operationId": "listKnowledgeTagUsage", "summary": "List Tag Usage", - "description": "Report how many documents and chunks carry a value for each defined tag, so a caller can tell a tag that is actually populated from one that was only declared. The bounded set is returned in one page; `nextCursor` is always null. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Count the documents and chunks with a value for each defined tag. Returns the complete set in one page; `nextCursor` is always null. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "knowledge.tags.read_usage", "x-oauth-scope": "api:read", "tags": ["Knowledge Bases"], @@ -4772,7 +4772,7 @@ "type": "null" } ], - "description": "ISO 8601 timestamp when the knowledge base was archived by `DELETE /knowledge/{knowledgeBaseId}`, or null while the knowledge base is active. Only `GET /knowledge?scope=archived` returns knowledge bases with a non-null value.", + "description": "ISO 8601 archive timestamp, or null while active. Use List Knowledge Bases with `scope=archived` to find archived knowledge bases.", "format": "date-time", "examples": ["2026-01-16T09:00:00Z"] } @@ -6329,7 +6329,7 @@ "properties": { "id": { "type": "string", - "description": "Tag definition identifier. Published because `PATCH` and `DELETE /knowledge/{knowledgeBaseId}/tags/{tagId}` address a definition by it; without it those operations are unreachable from a list read." + "description": "Tag definition ID used by Update Tag and Delete Tag." }, "displayName": { "type": "string", @@ -6468,7 +6468,7 @@ ], "description": "Tag value; dates are ISO 8601 strings and an unset tag is null." }, - "description": "Document tag values keyed by tag display name. Writes address the same tags by slot (`tag1`..`tag7`); resolve names to slots with GET /api/v2/knowledge/{knowledgeBaseId}/tags.", + "description": "Document tag values keyed by display name. Writes use slots such as `tag1`; use List Tags to map names to slots.", "examples": [ { "category": "billing", @@ -7205,7 +7205,7 @@ ], "description": "Tag value; dates are ISO 8601 strings and an unset tag is null." }, - "description": "Document tag values keyed by tag display name. Writes address the same tags by slot (`tag1`..`tag7`); resolve names to slots with GET /api/v2/knowledge/{knowledgeBaseId}/tags.", + "description": "Document tag values keyed by display name. Writes use slots such as `tag1`; use List Tags to map names to slots.", "examples": [ { "category": "billing", @@ -8329,7 +8329,7 @@ "properties": { "id": { "type": "string", - "description": "Tag definition identifier. Published for the same reason the vocabulary read publishes it: `PATCH` and `DELETE /knowledge/{knowledgeBaseId}/tags/{tagId}` address a definition by id, so without it a usage row cannot be acted on without a second read and a slot join.", + "description": "Tag definition ID used by Update Tag and Delete Tag.", "examples": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] }, "tagSlot": { diff --git a/apps/docs/openapi-v2-logs.json b/apps/docs/openapi-v2-logs.json index cc6d257fda7..7daa9e200b6 100644 --- a/apps/docs/openapi-v2-logs.json +++ b/apps/docs/openapi-v2-logs.json @@ -39,7 +39,7 @@ "get": { "operationId": "listLogs", "summary": "List Logs", - "description": "List logs with filters, selectable detail, sorting, and cursor pagination. `includeJobRuns=true` includes chat and Sim-agent jobs only with `sortBy=startedAt`, because other orderings are unsupported. `files` contains only run-produced files; use the files API for input attachments. Expired runs are hard-deleted. Retention is 30 days from run start on Free, unbounded on Pro and Team, and configured per organization on Enterprise with an optional workspace override. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List logs with filters, selectable detail, sorting, and cursor pagination. `includeJobRuns=true` includes chat and Sim-agent jobs only with `sortBy=startedAt`, because other orderings are unsupported. `files` contains only run-produced files; use the files API for input attachments. Expired runs are permanently deleted. Retention is 30 days from run start on Free, unlimited on Pro and Team, and configured per organization on Enterprise with workspace overrides. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "logs.list", "x-oauth-scope": "api:read", "tags": ["Logs"], @@ -296,10 +296,10 @@ "name": "folderPaths", "in": "query", "required": false, - "description": "Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree, so `/prod` also selects runs in `/prod/nested`. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches.", "schema": { "type": "string", - "description": "Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree, so `/prod` also selects runs in `/prod/nested`. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error." + "description": "Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches." } } ], @@ -356,7 +356,7 @@ "get": { "operationId": "getLog", "summary": "Get Log", - "description": "Retrieve a run's workflow snapshot, trace spans, final output, and cost. Trace spans have separate retention, so an empty `traceSpans` array does not prove none were recorded. Expired runs are hard-deleted. Retention is 30 days from run start on Free, unbounded on Pro and Team, and configured per organization on Enterprise with an optional workspace override. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Get a run's workflow graph, trace spans, final output, and cost. Trace spans expire separately, so an empty `traceSpans` array does not prove none were recorded. Expired runs are permanently deleted. Retention is 30 days from run start on Free, unlimited on Pro and Team, and configured per organization on Enterprise with workspace overrides. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "logs.read_detail", "x-oauth-scope": "api:read", "tags": ["Logs"], @@ -428,7 +428,7 @@ "get": { "operationId": "getLogStats", "summary": "Get Log Statistics", - "description": "Return workspace/workflow counts, success, errors, and latency. Defaults span runs, or 24 hours if empty; supplied bounds stay exact. Buckets are one-minute minimum and may pass the end. Folders include descendants; `workflowsTruncated` marks capped series, totals include all. Expired runs are hard-deleted. Retention is 30 days from run start on Free, unbounded on Pro and Team, and configured per organization on Enterprise with an optional workspace override. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Get run counts, success and error counts, and latency by workspace or workflow. Default bounds span recorded runs, or the last 24 hours when empty. Buckets may extend past the end. Folder filters include descendants; `workflowsTruncated` affects series, not totals. Expired runs are permanently deleted. Retention is 30 days from run start on Free, unlimited on Pro and Team, and configured per organization on Enterprise with workspace overrides. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "logs.read_stats", "x-oauth-scope": "api:read", "tags": ["Logs"], @@ -459,10 +459,10 @@ "name": "folderPaths", "in": "query", "required": false, - "description": "Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches.", "schema": { "type": "string", - "description": "Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error." + "description": "Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches." } }, { @@ -514,10 +514,10 @@ "name": "segmentCount", "in": "query", "required": false, - "description": "Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty.", + "description": "Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets.", "schema": { "default": 72, - "description": "Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty.", + "description": "Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets.", "type": "integer", "minimum": 1, "maximum": 500 @@ -1109,11 +1109,11 @@ }, "duration": { "type": "number", - "description": "Legacy span duration in milliseconds." + "description": "Current trace-span duration in milliseconds." }, "durationMs": { "type": "number", - "description": "Span duration in milliseconds." + "description": "Compatibility field for span duration in milliseconds. Read `duration` for current trace spans." }, "startTime": { "type": "string", @@ -1549,7 +1549,7 @@ "type": "null" } ], - "description": "Workflow graph snapshot captured for the run, or null when none is retained. Credential-bearing values are redacted to null: `oauth-input`, `password: true`, table sub-block values, sensitive nested tool parameters, and any parameter without authoritative codec metadata. `{{VAR}}` references in non-opaque fields are preserved." + "description": "Workflow graph captured for the run, or null if unavailable. Sensitive values are redacted to null; environment-variable references may be preserved." }, "traceSpans": { "type": "array", diff --git a/apps/docs/openapi-v2-resources.json b/apps/docs/openapi-v2-resources.json index 7644014d99c..d3a1fa9f366 100644 --- a/apps/docs/openapi-v2-resources.json +++ b/apps/docs/openapi-v2-resources.json @@ -175,7 +175,7 @@ "get": { "operationId": "getWorkspace", "summary": "Get Workspace", - "description": "Return public metadata for one accessible workspace. Governance identities, billing identities, and internal membership identifiers are intentionally omitted.\n\nOAuth scope: `api:read`.", + "description": "Get metadata for an accessible workspace.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workspaces.read_public_detail", "x-oauth-scope": "api:read", "tags": ["Workspaces"], @@ -243,7 +243,7 @@ "get": { "operationId": "listWorkspaceMembers", "summary": "List Workspace Members", - "description": "List the workspace's effective members ordered by email. Explicit workspace grants and inherited organization-administrator grants are merged; internal membership and billing identities are omitted.\n\nOAuth scope: `api:read`.", + "description": "List workspace members by email, including explicit grants and inherited organization admin access.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workspaces.members.list_public", "x-oauth-scope": "api:read", "tags": ["Workspaces"], @@ -335,7 +335,7 @@ "get": { "operationId": "listMcpServers", "summary": "List MCP Servers", - "description": "List MCP servers registered in a workspace. Request-header values and OAuth client secrets are never returned. The discovery fields stay at their registration defaults until `GET /api/v2/mcp-servers/{mcpServerId}/tools` runs a discovery.\n\nOAuth scope: `api:read`.", + "description": "List MCP servers registered in a workspace, excluding request-header values and OAuth secrets. Connection metadata remains at registration defaults until List MCP Server Tools performs discovery.\n\nOAuth scope: `api:read`.", "x-sim-operation": "mcp_servers.list", "x-oauth-scope": "api:read", "tags": ["MCP Servers"], @@ -461,7 +461,7 @@ "post": { "operationId": "createMcpServer", "summary": "Create MCP Server", - "description": "Register an MCP server in a workspace. The endpoint URL is the server identity, so a URL already registered here is a `409` — reconfigure that server with `PATCH /api/v2/mcp-servers/{mcpServerId}` instead. Registration never connects to the endpoint: the server comes back `disconnected` and stays unavailable until `GET /api/v2/mcp-servers/{mcpServerId}/tools` succeeds.\n\nOAuth scope: `api:write`.", + "description": "Register an external MCP server without connecting to it. A duplicate URL returns `409`; use Update MCP Server to change the existing registration. The server remains disconnected until List MCP Server Tools succeeds.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.create", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -535,7 +535,7 @@ "get": { "operationId": "getMcpServer", "summary": "Get MCP Server", - "description": "Fetch one MCP server by identifier. Request-header values and OAuth client secrets are never returned.\n\nOAuth scope: `api:read`.", + "description": "Get one MCP server by identifier. Request-header values and OAuth client secrets are never returned.\n\nOAuth scope: `api:read`.", "x-sim-operation": "mcp_servers.read", "x-oauth-scope": "api:read", "tags": ["MCP Servers"], @@ -612,7 +612,7 @@ "patch": { "operationId": "updateMcpServer", "summary": "Update MCP Server", - "description": "Update the supplied MCP server fields. Omitted fields are retained, except where a field says otherwise. Any change that invalidates authentication revokes the stored OAuth grant, resets `connectionStatus` to `disconnected`, and clears `lastConnected` and `lastError`, so the server must be rediscovered.\n\nOAuth scope: `api:write`.", + "description": "Update an MCP server's supplied fields. Omitted fields remain unchanged unless the field specifies otherwise. Authentication changes revoke the stored OAuth grant and reset connection metadata. Use List MCP Server Tools to reconnect.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.update", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -773,7 +773,7 @@ "get": { "operationId": "listMcpServerTools", "summary": "List MCP Server Tools", - "description": "Return up to 1,000 tools and 5 MB with `nextCursor: null`, opening a connection and updating connection metadata. `HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only. Unavailable servers return `503`; invalid OAuth returns `409` with `error.details.code: MCP_SERVER_REAUTHORIZATION_REQUIRED` and requires human reauthorization. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Discover up to 1,000 tools within 5 MB, connect to the server, and update connection metadata. Results are unpaginated. Invalid OAuth returns `409` with `MCP_SERVER_REAUTHORIZATION_REQUIRED`; reauthorize through the browser. Unavailable servers return `503`. `HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.tools.discover", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -805,9 +805,9 @@ "name": "refresh", "in": "query", "required": false, - "description": "Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip.", + "description": "Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools.", "schema": { - "description": "Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip.", + "description": "Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools.", "type": "boolean" } } @@ -865,7 +865,7 @@ "get": { "operationId": "listSkills", "summary": "List Skills", - "description": "List workspace and built-in skills with opaque cursor pagination. Built-ins are marked read-only. The list omits skill bodies; fetch one skill to read its content.\n\nOAuth scope: `api:read`.", + "description": "List workspace and built-in skills with cursor pagination. Built-in skills are read-only. The list omits skill bodies; use Get Skill to read content.\n\nOAuth scope: `api:read`.", "x-sim-operation": "skills.list", "x-oauth-scope": "api:read", "tags": ["Skills"], @@ -991,7 +991,7 @@ "post": { "operationId": "createSkill", "summary": "Create Skill", - "description": "Create one skill in a workspace. Its kebab-case name must be unique and cannot be reserved by a built-in skill. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create one skill in a workspace. Its kebab-case name must be unique and cannot be reserved by a built-in skill. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "skills.create", "x-oauth-scope": "api:write", "tags": ["Skills"], @@ -1065,7 +1065,7 @@ "get": { "operationId": "getSkill", "summary": "Get Skill", - "description": "Fetch one workspace or built-in skill, including its full content. Built-in skills are marked read-only.\n\nOAuth scope: `api:read`.", + "description": "Get one workspace or built-in skill, including its full content. Built-in skills are marked read-only.\n\nOAuth scope: `api:read`.", "x-sim-operation": "skills.read", "x-oauth-scope": "api:read", "tags": ["Skills"], @@ -1142,7 +1142,7 @@ "patch": { "operationId": "updateSkill", "summary": "Update Skill", - "description": "Update the supplied fields on a workspace skill. Omitted fields retain their stored values. Built-in skills are read-only. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Update a workspace skill. Omitted fields remain unchanged. Built-in skills are read-only. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "skills.update", "x-oauth-scope": "api:write", "tags": ["Skills"], @@ -1227,7 +1227,7 @@ "delete": { "operationId": "deleteSkill", "summary": "Delete Skill", - "description": "Delete a workspace skill. Built-in skills are read-only and cannot be deleted. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Delete a workspace skill. Built-in skills are read-only and cannot be deleted. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "skills.delete", "x-oauth-scope": "api:write", "tags": ["Skills"], @@ -1306,7 +1306,7 @@ "get": { "operationId": "listSkillEditors", "summary": "List Skill Editors", - "description": "List explicit skill editors and workspace administrators with opaque cursor pagination. Internal user and membership identifiers are never returned.\n\nOAuth scope: `api:read`.", + "description": "List skill editors and workspace administrators with cursor pagination.\n\nOAuth scope: `api:read`.", "x-sim-operation": "skills.editors.list", "x-oauth-scope": "api:read", "tags": ["Skills"], @@ -1431,7 +1431,7 @@ "post": { "operationId": "grantSkillEditor", "summary": "Grant Skill Editor", - "description": "Grant editor access to a current workspace member by email. The caller must already be a skill editor or workspace administrator. Workspace administrators already have derived editor access and cannot receive an explicit grant. A retried existing grant returns 200; a newly created grant returns 201. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Grant skill editor access to a workspace member by email. Requires an existing editor or workspace admin; admins already have access and cannot receive explicit grants. Existing grants return `200`; new grants return `201`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "skills.editors.grant", "x-oauth-scope": "api:write", "tags": ["Skills"], @@ -1534,7 +1534,7 @@ "delete": { "operationId": "revokeSkillEditor", "summary": "Revoke Skill Editor", - "description": "Revoke an explicit editor grant by email. The caller must already be a skill editor or workspace administrator. Workspace administrators have derived access that cannot be revoked. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Revoke an explicit editor grant by email. The caller must already be a skill editor or workspace administrator. Workspace administrators have derived access that cannot be revoked. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "skills.editors.revoke", "x-oauth-scope": "api:write", "tags": ["Skills"], @@ -1625,7 +1625,7 @@ "get": { "operationId": "listCustomTools", "summary": "List Custom Tools", - "description": "List code-backed custom tools defined in a workspace, with opaque cursor pagination. Legacy personal tools are excluded.\n\nOAuth scope: `api:read`.", + "description": "List code-backed custom tools in a workspace with cursor pagination.\n\nOAuth scope: `api:read`.", "x-sim-operation": "custom_tools.list", "x-oauth-scope": "api:read", "tags": ["Custom Tools"], @@ -1825,7 +1825,7 @@ "get": { "operationId": "getCustomTool", "summary": "Get Custom Tool", - "description": "Fetch one custom tool by identifier, scoped to its workspace.\n\nOAuth scope: `api:read`.", + "description": "Get one custom tool by identifier, scoped to its workspace.\n\nOAuth scope: `api:read`.", "x-sim-operation": "custom_tools.read", "x-oauth-scope": "api:read", "tags": ["Custom Tools"], @@ -1902,7 +1902,7 @@ "patch": { "operationId": "updateCustomTool", "summary": "Update Custom Tool", - "description": "Update the supplied custom tool fields. Omitted fields retain their stored values, and titles must remain unique within the workspace.\n\nOAuth scope: `api:write`.", + "description": "Update a custom tool. Omitted fields remain unchanged; titles must remain unique within the workspace.\n\nOAuth scope: `api:write`.", "x-sim-operation": "custom_tools.update", "x-oauth-scope": "api:write", "tags": ["Custom Tools"], @@ -2066,7 +2066,7 @@ "get": { "operationId": "listSandboxes", "summary": "List Sandboxes", - "description": "List the sandboxes defined in a workspace, with opaque cursor pagination. A sandbox is a reusable dependency set — npm or PyPI packages, pinned managed CLIs, and Debian packages — that Function blocks execute against. Listing is not plan-gated, so a workspace that dropped below the Max tier still sees what it built.\n\nOAuth scope: `api:read`.", + "description": "List reusable dependency environments for Function blocks, including language packages, managed CLIs, and system packages. Sandboxes remain visible after a plan downgrade.\n\nOAuth scope: `api:read`.", "x-sim-operation": "sandboxes.list", "x-oauth-scope": "api:read", "tags": ["Sandboxes"], @@ -2192,7 +2192,7 @@ "post": { "operationId": "createSandbox", "summary": "Create Sandbox", - "description": "Create a uniquely named sandbox. Prebuild deployments schedule an image build reported by `buildStatus`; runtime-install deployments or empty specs report `buildStatus: null`. Invalid dependency or system-package entries return `400` with field details. Requires a workspace admin on Max or Enterprise; lower plans return `403` with `error.details.code: WORKSPACE_PLAN_CAPABILITY_REQUIRED`. Creates and updates share a write budget; bursts return `429` with `Retry-After`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create a uniquely named dependency environment. If a build is needed, track readiness with `buildStatus`; null means no build is required. Invalid dependencies return `400` with field details. Requires workspace admin access on Max or Enterprise. Creates and updates share a rate limit; respect `Retry-After`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "sandboxes.create", "x-oauth-scope": "api:write", "tags": ["Sandboxes"], @@ -2266,7 +2266,7 @@ "get": { "operationId": "getSandbox", "summary": "Get Sandbox", - "description": "Fetch one sandbox by identifier, scoped to its workspace, including its current build state and any build failure.\n\nOAuth scope: `api:read`.", + "description": "Get one sandbox by identifier, scoped to its workspace, including its current build state and any build failure.\n\nOAuth scope: `api:read`.", "x-sim-operation": "sandboxes.read", "x-oauth-scope": "api:read", "tags": ["Sandboxes"], @@ -2343,7 +2343,7 @@ "patch": { "operationId": "updateSandbox", "summary": "Update Sandbox", - "description": "Update supplied fields; omissions preserve values, lists replace whole lists, and names remain unique. Prebuild deployments rebuild changed specs, while resending an unchanged failed spec retries it; runtime-install or empty specs report `buildStatus: null`. Requires a workspace admin on Max or Enterprise; lower plans return `403` with `error.details.code: WORKSPACE_PLAN_CAPABILITY_REQUIRED`. Creates and updates share a write budget; bursts return `429` with `Retry-After`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Update a sandbox, preserving omitted fields and replacing supplied lists. Dependency changes may start a build; resending a failed specification retries its build. `buildStatus: null` means no build is required. Requires workspace admin access on Max or Enterprise. Creates and updates share a rate limit; respect `Retry-After`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "sandboxes.update", "x-oauth-scope": "api:write", "tags": ["Sandboxes"], @@ -2428,7 +2428,7 @@ "delete": { "operationId": "deleteSandbox", "summary": "Delete Sandbox", - "description": "Delete a sandbox. Function blocks still selecting it fail closed until reconfigured. A prebuilt image is released when no sandbox shares it; runtime-install and empty specs have no image to release. Requires a workspace admin on Max or Enterprise; lower plans return `403` with `error.details.code: WORKSPACE_PLAN_CAPABILITY_REQUIRED`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Delete a sandbox. Function blocks using it fail until reconfigured. Requires workspace admin access on Max or Enterprise. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "sandboxes.delete", "x-oauth-scope": "api:write", "tags": ["Sandboxes"], @@ -2655,7 +2655,7 @@ "post": { "operationId": "createServiceAccountCredential", "summary": "Create Service-Account Credential", - "description": "Verify and store one service-account credential. Use provider discovery to select a service-account provider, then encode its required fields as the JSON object string in credentials. The credentials string is write-only and is never returned. A retried source match returns the existing credential with 200; a newly created credential returns 201. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Verify and store a service-account credential using the fields from List Credential Providers, encoded as a JSON object string in `credentials`. Secrets are never returned. A matching source returns the existing credential with `200`; creation returns `201`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "credentials.service_accounts.create", "x-oauth-scope": "api:write", "tags": ["Credentials"], @@ -2750,7 +2750,7 @@ "get": { "operationId": "listCredentialProviders", "summary": "List Credential Providers", - "description": "List catalogued OAuth and service-account connection methods and whether each is available to the caller in this workspace and deployment. Optionally search provider names with a case-insensitive substring match. OAuth authorization options contain the exact provider IDs accepted by the browser connection endpoint; service-account methods list the exact create-body fields and mark secret fields write-only. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List OAuth and service-account connection methods and their availability. OAuth options provide provider IDs for browser connections; service-account methods declare required fields and write-only secrets. Supports provider-name search. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "credentials.providers.list", "x-oauth-scope": "api:read", "tags": ["Credentials"], @@ -2830,7 +2830,7 @@ "post": { "operationId": "createCredentialConnection", "summary": "Create Credential Connection", - "description": "Create a short-lived browser URL for connecting an OAuth provider or reconnecting an existing OAuth credential. Open the URL, sign in as the authenticated user, complete provider authorization, then refresh the credentials list. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create a short-lived browser URL for connecting an OAuth provider or reconnecting an existing OAuth credential. Open the URL, sign in as the authenticated user, complete provider authorization, then refresh the credentials list. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "credentials.connections.create", "x-oauth-scope": "api:write", "tags": ["Credentials"], @@ -2904,7 +2904,7 @@ "delete": { "operationId": "deleteCredential", "summary": "Disconnect Credential", - "description": "Disconnect an OAuth or service-account credential and clear its stored workflow, deployment, paused-run, knowledge-connector, and webhook references. Credential admin access is required. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Disconnect an OAuth or service-account credential and clear its stored workflow, deployment, paused-run, knowledge-connector, and webhook references. Credential admin access is required. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "credentials.delete", "x-oauth-scope": "api:write", "tags": ["Credentials"], @@ -2982,7 +2982,7 @@ "patch": { "operationId": "updateCredential", "summary": "Update Credential", - "description": "Rename a service-account credential or rotate its write-only secret fields. Omissions preserve values; `description: null` clears the description. Secret fields sent for another credential type return `400`. The provider verifies replacements before storage: rejection leaves the old secret intact and returns `400` with `providerErrorCode`; provider outages return `503`. The preserved credential ID keeps all references working. Credential admin access is required. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Rename a service-account credential or rotate its secret fields, preserving omitted values and the credential ID. Requires credential admin access. Provider rejection preserves the old secret and returns `400` with `providerErrorCode`; outages return `503`. Fields for a different credential type return `400`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "credentials.update", "x-oauth-scope": "api:write", "tags": ["Credentials"], @@ -3082,7 +3082,7 @@ "get": { "operationId": "listSecrets", "summary": "List Secrets", - "description": "List workspace and caller-owned personal secret metadata with opaque cursor pagination. Rows for workspace secrets marked visible (unredacted) include the stored value; every other row is metadata-only and no other response ever carries a value. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List workspace and caller-owned personal secrets with cursor pagination. Only workspace secrets marked `unredacted` include values; all other entries contain metadata only. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "secrets.list", "x-oauth-scope": "api:read", "tags": ["Secrets"], @@ -3221,7 +3221,7 @@ "put": { "operationId": "setSecret", "summary": "Set Secret", - "description": "Create or replace a workspace or personal secret. Values are encrypted at rest, write-only, and never returned. For an existing workspace secret, omit `value` to update only `description` or `unredacted`; the value remains untouched. This metadata-only form cannot create a secret and returns `404` when absent. Personal secrets always require `value`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create or replace a workspace or personal secret without returning its value. For existing workspace secrets, omit `value` to update metadata only; this returns `404` if absent. Personal secrets always require `value`. List Secrets can reveal workspace values marked `unredacted`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "secrets.set", "x-oauth-scope": "api:write", "tags": ["Secrets"], @@ -3326,7 +3326,7 @@ "delete": { "operationId": "deleteSecret", "summary": "Delete Secret", - "description": "Delete a workspace or caller-owned personal secret without reading or returning its stored value. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Delete a workspace or caller-owned personal secret without reading or returning its stored value. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "secrets.delete", "x-oauth-scope": "api:write", "tags": ["Secrets"], @@ -3418,7 +3418,7 @@ "get": { "operationId": "getApiMeta", "summary": "Get API Capabilities", - "description": "Report whether v2 is available, what kind of API credential is calling, and when it expires. Requires a valid API key or OAuth access token.\n\nOAuth scope: `api:read`.", + "description": "Get whether v2 is available, what kind of API credential is calling, and when it expires. Requires a valid API key or OAuth access token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "meta.capabilities.read", "x-oauth-scope": "api:read", "tags": ["Meta"], @@ -3466,7 +3466,7 @@ "get": { "operationId": "listWorkflowMcpServers", "summary": "List Workflow MCP Servers", - "description": "List servers that publish deployed workflows to outside MCP clients; `GET /api/v2/mcp-servers` instead lists external servers Sim calls. Entries include client endpoints and tool names. A page shares a 2,000-name budget, so trailing servers may show partial inventories; read a server's tools endpoint for its full set. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List MCP servers that expose deployed workflows to external clients. Use List MCP Servers for external servers Sim calls. Tool names share a 2,000-name page limit; inspect `toolNamesTruncated` and use List Workflow MCP Tools for a server's inventory. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "mcp_servers.workflow_deployments.list", "x-oauth-scope": "api:read", "tags": ["MCP Servers"], @@ -3580,7 +3580,7 @@ "post": { "operationId": "createWorkflowMcpServer", "summary": "Create Workflow MCP Server", - "description": "Publish a new MCP server for a workspace, optionally seeding it with workflows to expose as tools. Every workflow named in `workflowIds` must already be deployed. Setting `isPublic` lets any MCP client holding the server URL execute the workflows it publishes without a Sim API key. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create an MCP server that exposes deployed workflows as tools. Every supplied workflow must already be deployed. With `isPublic: true`, anyone with the server URL can execute its workflows without a Sim API key. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.workflow_deployments.create_server", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -3654,7 +3654,7 @@ "get": { "operationId": "getWorkflowMcpServer", "summary": "Get Workflow MCP Server", - "description": "Read one published MCP server. The list is the only other place this state is published, so a caller holding a server id would otherwise have to page the collection and filter client-side. The tools it publishes are on its `tools` sub-resource. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Get a published workflow MCP server's metadata and client endpoint. Use List Workflow MCP Tools for its tool inventory. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "mcp_servers.workflow_deployments.read_server", "x-oauth-scope": "api:read", "tags": ["MCP Servers"], @@ -3719,7 +3719,7 @@ "patch": { "operationId": "updateWorkflowMcpServer", "summary": "Update Workflow MCP Server", - "description": "Rename, re-describe, or change the public visibility of a published MCP server. Merge-patch shaped: an omitted key is unchanged and `description: null` clears the description. Publishing and unpublishing the workflows it serves are separate operations on its `tools` sub-resource. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Update a workflow MCP server's name, description, or public access. Omitted fields remain unchanged; `description: null` clears the description. Publish or unpublish tools separately. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.workflow_deployments.update_server", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -3804,7 +3804,7 @@ "delete": { "operationId": "deleteWorkflowMcpServer", "summary": "Delete Workflow MCP Server", - "description": "Unpublish an MCP server. Every tool it served stops answering and connected clients lose the endpoint. The workflows themselves are untouched — their own deployments stay live and executable through the workflow API. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Delete a workflow MCP server and stop serving its tools. The underlying workflows remain deployed and executable through the workflow API. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.workflow_deployments.delete_server", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -3874,7 +3874,7 @@ "get": { "operationId": "listWorkflowMcpTools", "summary": "List Workflow MCP Tools", - "description": "Every tool a server publishes, tool-name ordered. The server list reports tool *names* only, so this is where a caller reads the `workflowId` that `DELETE /api/v2/workflow-mcp-servers/{serverId}/tools/{workflowId}` addresses. Returned in one page rather than paged — so `nextCursor` is always null — and capped at 2,000 tools, which is far above any real server's inventory. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "List a server's published tools by name, including workflow IDs used to unpublish them. Returns up to 2,000 tools with `nextCursor: null`; `truncated` indicates an incomplete inventory that cannot be paginated. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "mcp_servers.workflow_deployments.list_tools", "x-oauth-scope": "api:read", "tags": ["MCP Servers"], @@ -3939,7 +3939,7 @@ "post": { "operationId": "deployWorkflowMcpTool", "summary": "Publish Workflow As MCP Tool", - "description": "Publish a deployed workflow as a tool on an MCP server. The tool's input schema is generated from the deployed workflow's input format, so the workflow must already be deployed. Idempotent per workflow: a server carries at most one tool per workflow, so a repeat call replaces the existing tool and answers `200` with `updated: true` rather than conflicting. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Publish a deployed workflow as an MCP tool using its deployed input schema. Each server has at most one tool per workflow; repeating the call replaces that tool and returns `200` with `updated: true`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.workflow_deployments.deploy_tool", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -4026,7 +4026,7 @@ "delete": { "operationId": "undeployWorkflowMcpTool", "summary": "Unpublish Workflow MCP Tool", - "description": "Remove a workflow from an MCP server. Addressed by workflow rather than by tool identifier, because a server carries at most one live tool per workflow. The workflow's own deployment is untouched. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Unpublish an MCP tool by its workflow ID. The workflow's API deployment remains active. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "mcp_servers.workflow_deployments.undeploy_tool", "x-oauth-scope": "api:write", "tags": ["MCP Servers"], @@ -4107,7 +4107,7 @@ "get": { "operationId": "listBlocks", "summary": "List Blocks", - "description": "List the blocks available in a workspace, built-in and workspace-deployed alike, discriminated by `source`. Availability is caller-specific: the workspace’s integration allowlist, the organization’s revealed preview blocks, and the deployment’s allowlist all narrow the result. Use `capability=trigger` for the blocks that can start a workflow. Summaries name their tools and operations by id — resolve one with Get Block or Get Tool.\n\nOAuth scope: `api:read`.", + "description": "List built-in and workspace-deployed blocks visible to the caller. Integration allowlists and preview visibility restrict results. Use `capability=trigger` for workflow starters and Get Block or Get Tool to resolve operation and tool IDs.\n\nOAuth scope: `api:read`.", "x-sim-operation": "catalog.blocks.list", "x-oauth-scope": "api:read", "tags": ["Catalog"], @@ -4162,9 +4162,9 @@ "name": "source", "in": "query", "required": false, - "description": "Restrict to shipped blocks or to this workspace’s deployed custom blocks.", + "description": "Restrict to built-in blocks or this workspace's deployed custom blocks.", "schema": { - "description": "Restrict to shipped blocks or to this workspace’s deployed custom blocks.", + "description": "Restrict to built-in blocks or this workspace's deployed custom blocks.", "type": "string", "enum": ["builtin", "custom"] } @@ -4268,7 +4268,7 @@ "get": { "operationId": "getBlock", "summary": "Get Block", - "description": "Read one block’s full configuration shape: its fields and their conditions, its operations with the tool each runs, every tool’s parameters and outputs, and its triggers. An unversioned base type resolves to the newest version this caller can see — `confluence` answers with `confluence_v2` — and the returned `id` is always the resolved one, matching Get Tool. A block this caller cannot see answers 404, identically to one that does not exist.\n\nOAuth scope: `api:read`.", + "description": "Get a block's fields, conditions, operations, tool schemas, and triggers. Unversioned types resolve to the newest visible version; the returned `id` identifies that version. Hidden or missing blocks return `404`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "catalog.blocks.read", "x-oauth-scope": "api:read", "tags": ["Catalog"], @@ -4348,7 +4348,7 @@ "get": { "operationId": "listTools", "summary": "List Tools", - "description": "List the built-in tools available in a workspace. Built-in tools only: a workspace’s MCP tools are discovered per server on List MCP Server Tools, and its code-backed custom tools are on List Custom Tools. A tool is available when a block the caller can see exposes it, so the same allowlist and visibility rules as List Blocks apply.\n\nOAuth scope: `api:read`.", + "description": "List built-in tools exposed by blocks visible to the caller. Use List MCP Server Tools for an external server's tools and List Custom Tools for workspace code-backed tools.\n\nOAuth scope: `api:read`.", "x-sim-operation": "catalog.tools.list", "x-oauth-scope": "api:read", "tags": ["Catalog"], @@ -4499,7 +4499,7 @@ "get": { "operationId": "getTool", "summary": "Get Tool", - "description": "Read one built-in tool’s declared parameters and outputs. A name that is itself a registered id answers as that exact tool; a name that is not resolves to the newest version of its family. The returned `id` is always the one that answered, so a caller can see which version it got. A tool the workspace’s visible blocks do not expose answers `404`, identically to one that does not exist.\n\nOAuth scope: `api:read`.", + "description": "Get a built-in tool's parameters and outputs. Registered IDs resolve exactly; other names resolve to the newest family version. The returned `id` identifies the resolved tool. Hidden or missing tools return `404`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "catalog.tools.read", "x-oauth-scope": "api:read", "tags": ["Catalog"], @@ -4579,7 +4579,7 @@ "post": { "operationId": "executeTool", "summary": "Run Tool", - "description": "Run a built-in tool using published parameter IDs. Sim resolves `credentialId`, hosted keys, and whole-value `{{VAR_NAME}}` references for `user-only` parameters; other values pass through verbatim. Third-party refusal returns `200` with `status: \"failed\"`; the error envelope covers API failures. Hidden or missing tools return `404`; disallowed integrations return `403` with `error.details.code: INTEGRATION_NOT_ALLOWED`. Hosted-key use is billed to the workspace. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Run a built-in tool using published parameter IDs. Sim resolves `credentialId`, hosted keys, and whole-value `{{VAR_NAME}}` references for `user-only` parameters; other values pass through verbatim. Third-party refusal returns `200` with `status: \"failed\"`; the error envelope covers API failures. Hidden or missing tools return `404`; disallowed integrations return `403` with `error.details.code: INTEGRATION_NOT_ALLOWED`. Hosted-key use is billed to the workspace. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tools.execute", "x-oauth-scope": "api:write", "tags": ["Catalog"], @@ -4664,7 +4664,7 @@ "get": { "operationId": "listConnectorTypes", "summary": "List Connector Types", - "description": "List connector types and accepted source configuration. A field with `multi: true` stores `string[]`. `canonicalParamId` links picker and manual fields that write the same key; send exactly one, keyed by `canonicalParamId` rather than its own `id`. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List connector types and accepted source configuration. A field with `multi: true` stores `string[]`. `canonicalParamId` links picker and manual fields that write the same key; send exactly one, keyed by `canonicalParamId` rather than its own `id`. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "catalog.connector_types.list", "x-oauth-scope": "api:read", "tags": ["Catalog"], @@ -5515,7 +5515,7 @@ "maxLength": 2000 }, "transport": { - "description": "Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.", + "description": "Transport protocol. Defaults to `streamable-http` on creation.", "default": "streamable-http", "type": "string", "enum": ["streamable-http"] @@ -5545,21 +5545,21 @@ } }, "timeout": { - "description": "Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.", + "description": "Per-request timeout in milliseconds. Defaults to 30000 on creation.", "default": 30000, "type": "integer", "minimum": 1000, "maximum": 300000 }, "retries": { - "description": "Number of retries per request. Applied server-side as 3 when omitted on create.", + "description": "Number of retries per request. Defaults to 3 on creation.", "default": 3, "type": "integer", "minimum": 0, "maximum": 10 }, "enabled": { - "description": "Whether the server tools are available to workflows. Applied server-side as true when omitted on create.", + "description": "Whether workflows can use the server's tools. Defaults to true on creation.", "default": true, "type": "boolean" }, @@ -5702,7 +5702,7 @@ "maxLength": 2000 }, "transport": { - "description": "Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.", + "description": "Transport protocol. Defaults to `streamable-http` on creation.", "default": "streamable-http", "type": "string", "enum": ["streamable-http"] @@ -5732,21 +5732,21 @@ } }, "timeout": { - "description": "Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.", + "description": "Per-request timeout in milliseconds. Defaults to 30000 on creation.", "default": 30000, "type": "integer", "minimum": 1000, "maximum": 300000 }, "retries": { - "description": "Number of retries per request. Applied server-side as 3 when omitted on create.", + "description": "Number of retries per request. Defaults to 3 on creation.", "default": 3, "type": "integer", "minimum": 0, "maximum": 10 }, "enabled": { - "description": "Whether the server tools are available to workflows. Applied server-side as true when omitted on create.", + "description": "Whether workflows can use the server's tools. Defaults to true on creation.", "default": true, "type": "boolean" }, @@ -8826,7 +8826,7 @@ }, "toolNamesTruncated": { "type": "boolean", - "description": "Whether `toolCount` and `toolNames` under-report. The names are gathered for the whole page under one ceiling, so a page whose servers publish more tools than that ceiling between them reports only part of each server's inventory. Read one server's tool inventory and check that response's own `truncated`, which reports the same ceiling applied to a single server — only an untruncated response is the authoritative set. Unrelated to `nextCursor`, which is how this list says there are further servers." + "description": "Whether the page-wide tool-name limit left some inventories incomplete. Use List Workflow MCP Tools for one server and check its `truncated` flag before treating the inventory as complete. `nextCursor` paginates servers, not tool names." } }, "required": ["data", "nextCursor", "toolNamesTruncated"], @@ -9094,7 +9094,7 @@ }, "truncated": { "type": "boolean", - "description": "Whether this inventory was cut short by the server-side ceiling on how many tools one response may carry. `nextCursor` is null either way — this list takes no `cursor`, so a truncated set cannot be paged past and this flag is the only way to tell a partial inventory from a complete one. A reconciling caller must not treat a truncated set as the full published inventory." + "description": "Whether the tool limit left this inventory incomplete. The list is unpaginated and `nextCursor` remains null even when truncated. Do not treat a truncated inventory as the complete set of published tools." } }, "required": ["data", "nextCursor", "truncated"], @@ -9596,7 +9596,7 @@ "source": { "type": "string", "enum": ["builtin", "custom"], - "description": "Where the block comes from: `builtin` is the shipped registry, `custom` is a workflow this workspace deployed as a block." + "description": "Block source: `builtin` for built-in blocks, or `custom` for workflows this workspace deployed as blocks." }, "authMode": { "description": "How the block authenticates: `oauth`, `api_key`, or `bot_token`.", @@ -10284,7 +10284,7 @@ "source": { "type": "string", "enum": ["builtin", "custom"], - "description": "Where the block comes from: `builtin` is the shipped registry, `custom` is a workflow this workspace deployed as a block." + "description": "Block source: `builtin` for built-in blocks, or `custom` for workflows this workspace deployed as blocks." }, "authMode": { "description": "How the block authenticates: `oauth`, `api_key`, or `bot_token`.", @@ -10974,7 +10974,7 @@ }, "input": { "default": {}, - "description": "Arguments for the tool, keyed by the parameter ids the tool catalog publishes for it. A parameter whose visibility is `user-only` also accepts an environment-variable reference written as the whole value, `{{VAR_NAME}}`, resolved server-side against the workspace environment; any other value is sent verbatim.", + "description": "Tool arguments keyed by published parameter IDs. For `user-only` parameters, a whole-value `{{VAR_NAME}}` reference resolves a workspace environment variable. Other values pass through unchanged.", "type": "object", "propertyNames": { "type": "string" diff --git a/apps/docs/openapi-v2-tables.json b/apps/docs/openapi-v2-tables.json index dbc31b6c6c8..a148cd88bda 100644 --- a/apps/docs/openapi-v2-tables.json +++ b/apps/docs/openapi-v2-tables.json @@ -39,7 +39,7 @@ "get": { "operationId": "listTables", "summary": "List Tables", - "description": "List tables in a workspace with optional folder filtering, search, sorting, and an opaque cursor envelope. `scope=archived` lists tables a `DELETE` archived, which `POST /api/v2/tables/{tableId}/restore` can bring back. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List active tables with folder filtering, search, sorting, and cursor pagination. Use `scope=archived` to find tables available for restoration. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.list", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -72,9 +72,9 @@ "name": "folderPath", "in": "query", "required": false, - "description": "Restrict results to tables in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to tables in this folder. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to tables in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to tables in this folder. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -262,7 +262,7 @@ "get": { "operationId": "getTable", "summary": "Get Table", - "description": "Retrieve a table with its metadata, column schema, locks, and current job. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Get a table with its metadata, column schema, locks, and current job. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -341,7 +341,7 @@ "delete": { "operationId": "deleteTable", "summary": "Delete Table", - "description": "Archive a table and return an explicit deletion acknowledgement. The table is soft-deleted, not erased: its rows are retained and `POST /api/v2/tables/{tableId}/restore` brings it back.\n\nOAuth scope: `api:write`.", + "description": "Archive a table while retaining its rows. Use List Tables with `scope=archived` to find it and Restore Table to recover it.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.delete", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -420,7 +420,7 @@ "patch": { "operationId": "updateTable", "summary": "Update Table", - "description": "Rename a table, edit its description, or move it to a canonical folder. At least one mutable field is required; lock flags are read-only.\n\nNOT atomic: fields are written independently, so a 4xx may follow a partial update. When fields were applied, `details.applied` names them; retry only the missing fields. If it is absent, nothing changed.\n\nA workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Rename a table, edit its description, or move it to a folder. Fields are saved independently: a failed request may leave partial changes. `error.details.applied` lists saved fields; retry only the remaining fields. If absent, nothing changed. Lock flags are read-only. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.update", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -764,7 +764,7 @@ "get": { "operationId": "listTableRows", "summary": "List Rows", - "description": "List a plain cursor page in default row order. Pages are capped at 5MB by default and may contain fewer rows than the requested limit; continue until nextCursor is null. Use the query endpoint for predicate filtering and sorting. Set `includeRunState=true` to attach each row's per-workflow-group run outcomes; the row limit is capped when it is set.\n\nOAuth scope: `api:read`.", + "description": "List rows in default order with cursor pagination. Pages default to a 5 MB limit and may contain fewer rows than requested; continue until `nextCursor` is null. Use Query Rows for filtering and sorting. `includeRunState=true` adds per-group run outcomes and reduces the row limit.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.rows.list", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -1132,7 +1132,7 @@ "get": { "operationId": "getTableRow", "summary": "Get Row", - "description": "Retrieve one row by identifier. Set `includeRunState=true` to attach the row's per-workflow-group run outcomes.\n\nOAuth scope: `api:read`.", + "description": "Get one row by identifier. Set `includeRunState=true` to attach the row's per-workflow-group run outcomes.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.rows.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -1417,7 +1417,7 @@ "post": { "operationId": "upsertTableRow", "summary": "Upsert Row", - "description": "Insert a row or update the existing row that conflicts on a selected unique column.\n\nWARNING — the update branch REPLACES the row, it does not merge. `data` is the complete new row value, so every column you omit is cleared on the matched row. Send the full row here, or use `PATCH /api/v2/tables/{tableId}/rows/{rowId}` to change a subset.\n\nOAuth scope: `api:write`.", + "description": "Insert a row or replace the row matching a selected unique column. On replacement, omitted columns are cleared; send the complete row. Use Update Row for a partial patch.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.rows.upsert", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -1504,7 +1504,7 @@ "post": { "operationId": "queryTableRows", "summary": "Query Rows", - "description": "Query rows using an optional typed condition or `all`/`any` group, ordered sorting, and opaque cursor pagination. Omit the predicate to match all rows. Pages default to a 5 MB cap and may return fewer rows than requested; continue until `nextCursor` is null. Oversized predicates return `413`. `includeRunState` adds per-group outcomes and lowers the row cap. Counts come from a separate snapshot at `POST /query/count`; take the count first and treat it as a floor.\n\nOAuth scope: `api:read`.", + "description": "Query rows with typed predicates, sorting, and cursor pagination. Omit the predicate to match all rows. Pages default to a 5 MB limit; continue until `nextCursor` is null. Oversized predicates return `413`. `includeRunState` adds per-group outcomes and reduces the row limit. Counts are read separately and can differ from paged results if rows change.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.rows.query", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -1588,7 +1588,7 @@ "post": { "operationId": "countTableRows", "summary": "Count Rows", - "description": "Count the rows matching a typed predicate across the entire table. A predicate may be one condition or an `all`/`any` group. The paged reads carry no total, and `rowCount` on the table resource counts every row rather than the matches. Omit the predicate to count the whole table. A predicate larger than the request-body ceiling is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Count rows matching a typed predicate, or omit the predicate to count all rows. The count is read separately from row pages and can change between requests. Oversized predicates return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.rows.query", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -1672,7 +1672,7 @@ "get": { "operationId": "listTableViews", "summary": "List Views", - "description": "List the bounded set of saved table views, with references to removed columns pruned on read. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List saved table views, omitting references to removed columns. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.views.list", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -1832,7 +1832,7 @@ "get": { "operationId": "getTableView", "summary": "Get View", - "description": "Retrieve one saved table view by identifier.\n\nOAuth scope: `api:read`.", + "description": "Get one saved table view by identifier.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.views.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -2101,7 +2101,7 @@ "get": { "operationId": "listTableWorkflowGroups", "summary": "List Workflow Groups", - "description": "List the workflow and enrichment groups that can be dispatched for a table. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List the workflow and enrichment groups that can be dispatched for a table. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.groups.list", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -2437,7 +2437,7 @@ "post": { "operationId": "createTableDispatch", "summary": "Create Run Dispatch", - "description": "Asynchronously run workflow or enrichment groups across all rows or a selected row subset. Poll the returned `dispatchId` with `GET /api/v2/tables/{tableId}/dispatches/{dispatchId}` until its status is `complete` or `canceled`, and cancel it with `DELETE` on the same path. A `null` `dispatchId` means the run settled inline and there is nothing to poll.\n\nOAuth scope: `api:write`.", + "description": "Start workflow or enrichment groups across all rows or selected rows. Poll Get Run Dispatch until `complete` or `canceled`. A null `dispatchId` means no dispatch is available to poll; check row outcomes with `includeRunState`. Use Cancel Run Dispatch to stop further scheduling.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.runs.start", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -2519,7 +2519,7 @@ "get": { "operationId": "listTableDispatches", "summary": "List Active Run Dispatches", - "description": "List the run dispatches still in flight on one table. Bounded by the dispatcher rather than by a page size, so this list is unpaginated and `nextCursor` is always null. A settled dispatch is read by identifier.\n\nOAuth scope: `api:read`.", + "description": "List in-flight run dispatches for a table in one page; `nextCursor` is always null. Use Get Run Dispatch to read a settled dispatch.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.runs.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -2597,7 +2597,7 @@ "post": { "operationId": "runRowEnrichment", "summary": "Run Enrichment For One Row", - "description": "Asynchronously run one workflow or enrichment group for one table row. Poll the returned `dispatchId` with `GET /api/v2/tables/{tableId}/dispatches/{dispatchId}`; a `null` `dispatchId` means the cell already settled inline.\n\nOAuth scope: `api:write`.", + "description": "Start one workflow or enrichment group for a table row. Poll Get Run Dispatch using the returned `dispatchId`. A null `dispatchId` means no dispatch is available to poll; check row outcomes with `includeRunState`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.runs.start", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -2701,7 +2701,7 @@ "get": { "operationId": "getRowEnrichment", "summary": "Get Enrichment Run Detail", - "description": "Retrieve the provider cascade behind one enrichment cell: every configured provider in cascade order, each one's status, hosted-key cost, and duration, plus which provider produced the match. `null` means the cell has never run, or ran before cascade detail was recorded — distinct from a `404`, which means the table, row, or group does not exist.\n\nOAuth scope: `api:read`.", + "description": "Get an enrichment cell's provider attempts, statuses, hosted-key costs, durations, and matching provider. Null means no run detail was recorded; `404` means the table, row, or group does not exist.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.rows.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -2801,7 +2801,7 @@ "post": { "operationId": "searchTableRows", "summary": "Search Rows", - "description": "Search every cell case-insensitively for substring `q`, optionally within a predicate-filtered, sorted view. This is text search; `POST /query` performs structured predicate reads. Results are cell coordinates `{ ordinal, rowId, column }`, never row data; `ordinal` indexes the same view paged by `POST /query`. Results are uncursored and capped at 1000; `truncated` signals more matches. Narrow `q` or the predicate instead of paging.\n\nOAuth scope: `api:read`.", + "description": "Search cell text for a case-insensitive substring within an optional filtered and sorted view. Returns cell coordinates, not row data; `ordinal` matches the view used by Query Rows. Results are unpaginated and capped at 1000. If `truncated` is true, narrow the search or predicate.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.rows.search", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -2885,7 +2885,7 @@ "post": { "operationId": "createTableImport", "summary": "Create Table Import", - "description": "Create a durable CSV import. Upload sources receive signed transfer instructions; workspace-file sources begin processing directly.\n\nOAuth scope: `api:write`.", + "description": "Create a CSV import. Upload sources receive signed transfer instructions; workspace-file sources start processing directly.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.imports.create", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -2962,7 +2962,7 @@ "get": { "operationId": "getTableImport", "summary": "Get Table Import", - "description": "Read progress and terminal state for a durable table import.\n\nAn upload-backed import has no durable record until its upload completes, so send the signed upload control token to read it during the `uploading` phase; without the token that phase is a `404`.\n\nOAuth scope: `api:read`.", + "description": "Get an import's progress and status. During `uploading`, the signed upload token is required; omitting it returns `404`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.imports.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -3050,7 +3050,7 @@ "delete": { "operationId": "cancelTableImport", "summary": "Cancel Table Import", - "description": "Cancel an upload or processing import without rolling back committed row batches.\n\nAn import that is not in a cancelable state, including an `expired` one, is a `409` naming the current status. An unknown or already-purged import id is a `404`.\n\nOAuth scope: `api:write`.", + "description": "Cancel an upload or processing import. Committed row batches remain. Non-cancelable states, including `expired`, return `409`; unknown or purged imports return `404`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.imports.cancel", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -3143,7 +3143,7 @@ "post": { "operationId": "createTableImportPartUrls", "summary": "Create Table Import Part URLs", - "description": "Issue short-lived signed PUT URLs for a bounded set of multipart part numbers.\n\nThe import must still be `uploading`; one that has moved on, including an `expired` one, is a `409` naming the current status. An unknown or already-purged import id is a `404`.\n\nOAuth scope: `api:write`.", + "description": "Create signed URLs for multipart upload parts. Requires the `uploading` state; other states return `409`. Unknown or purged imports return `404`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.imports.create_parts", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -3253,7 +3253,7 @@ "post": { "operationId": "completeTableImportUpload", "summary": "Complete Table Import Upload", - "description": "Verify or assemble the uploaded CSV and begin processing with the same import id.\n\nAn import no longer awaiting an upload, including an `expired` one, is a `409` naming the current status. An unknown or already-purged import id is a `404`.\n\nOAuth scope: `api:write`.", + "description": "Verify or assemble uploaded CSV bytes and start processing under the same import ID. Requires an import awaiting upload completion; other states return `409`. Unknown or purged imports return `404`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.imports.complete", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -3349,7 +3349,7 @@ "post": { "operationId": "createTableExport", "summary": "Create Table Export", - "description": "Create a durable CSV or JSON export that completes inline for small tables and queues larger work.\n\nOAuth scope: `api:write`.", + "description": "Create a CSV or JSON export. Exports of small tables finish during the request; larger exports run asynchronously.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.exports.create", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -3436,7 +3436,7 @@ "get": { "operationId": "getTableExport", "summary": "Get Table Export", - "description": "Read progress and terminal state for a durable table export.\n\nOAuth scope: `api:read`.", + "description": "Get a table export's progress and status.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.exports.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -3524,7 +3524,7 @@ "delete": { "operationId": "cancelTableExport", "summary": "Cancel Table Export", - "description": "Cancel an export that has not reached a terminal state.\n\nOAuth scope: `api:write`.", + "description": "Cancel an export that is still in progress.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.exports.cancel", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -3617,7 +3617,7 @@ "get": { "operationId": "downloadTableExport", "summary": "Download Table Export", - "description": "Return a short-lived signed download URL for a completed table export.\n\nThe export must have reached `completed`; one still processing, failed, or canceled is a `409` naming the current status. An export whose file is no longer available is a `404`, not a `410`.\n\nOAuth scope: `api:read`.", + "description": "Get a short-lived signed download URL for a completed export. Other states return `409`; an unavailable export file returns `404`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.exports.download", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -3794,7 +3794,7 @@ "get": { "operationId": "listTablesFolders", "summary": "List Folders", - "description": "List table folders, optionally restricting the result to direct children of a canonical parent path. The bounded set is returned in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", + "description": "List table folders, optionally limiting results to direct children of a parent path. Returns the complete set in one page; `nextCursor` is always null.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.folders.list", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -3815,9 +3815,9 @@ "name": "parentPath", "in": "query", "required": false, - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -4053,7 +4053,7 @@ "delete": { "operationId": "deleteTablesFolder", "summary": "Delete Folder", - "description": "Delete an empty table folder, or recursively delete its descendants and tables when explicitly requested.\n\nOAuth scope: `api:write`.", + "description": "Archive an empty folder, or set `recursive=true` to archive its tables and subfolders. Use Restore Folder to recover the archived contents.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.folders.delete", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -4165,7 +4165,7 @@ "post": { "operationId": "restoreTablesFolder", "summary": "Restore Folder", - "description": "Restore a recursively archived table folder with its subfolders and tables, addressed by its former path. If its parent remains archived, it is re-rooted to `/`; active-name conflicts are deduplicated, so use the returned `path`. Non-archived paths return `404`. Preserve the path returned by `DELETE /api/v2/tables/folders`: unlike the files API, the table-folder list cannot discover archived paths.\n\nOAuth scope: `api:write`.", + "description": "Restore an archived table folder, its descendants, and tables using its former path. An archived parent moves it to the root; name conflicts may change the returned `path`. Non-archived paths return `404`. Save the path from Delete Folder, because List Folders does not include archived table folders.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.folders.restore", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -4239,7 +4239,7 @@ "post": { "operationId": "restoreTable", "summary": "Restore Table", - "description": "Un-archive a table a `DELETE` archived, along with the rows, views, and workflow groups archived with it. Find archived tables with `scope=archived` on the table list. Idempotent: a table that is already active is returned unchanged with no audit entry recorded, so a retry after a dropped response cannot look like a failure. A name collision is resolved by renaming, so the restored table may come back under a different `name`.\n\nOAuth scope: `api:write`.", + "description": "Restore a table and its archived rows, views, and workflow groups. Active tables return unchanged without a new audit event. Name conflicts may change the returned `name`. Find archived tables with List Tables and `scope=archived`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.restore", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -4326,7 +4326,7 @@ "post": { "operationId": "bulkUpdateTableRows", "summary": "Bulk Update Rows", - "description": "Apply a distinct partial data patch to each of up to 1000 rows in one request. Each patch merges into its row, so a column absent from `data` is left alone. Membership is atomic: a `rowId` naming no row in this table fails the whole request with a `400` listing the missing identifiers. Use `PATCH /api/v2/tables/{tableId}/rows` when one patch applies to every matching row.\n\nOAuth scope: `api:write`.", + "description": "Apply separate partial patches to up to 1,000 rows, preserving omitted columns. A row outside the table rejects the entire request with `400` and lists missing IDs. Use Update Rows by Filter to apply one patch to every matching row.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.rows.update_many", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -4413,7 +4413,7 @@ "get": { "operationId": "getTableDispatch", "summary": "Get Run Dispatch", - "description": "Poll one workflow-column run dispatch by the `dispatchId` the run endpoints returned. Answers in every lifecycle state — `pending`, `dispatching`, `complete`, and `canceled` — so a poller can wait for a run to settle. Per-cell outcomes are read with `includeRunState` on the row endpoints.\n\nOAuth scope: `api:read`.", + "description": "Get a dispatch's current state. Poll until `complete` or `canceled`; use row reads with `includeRunState` for per-cell outcomes.\n\nOAuth scope: `api:read`.", "x-sim-operation": "tables.runs.read", "x-oauth-scope": "api:read", "tags": ["Tables"], @@ -4501,7 +4501,7 @@ "delete": { "operationId": "cancelTableDispatch", "summary": "Cancel Run Dispatch", - "description": "Cancel one run dispatch by the `dispatchId` the run endpoint returned. This stops the scheduler: the dispatcher observes the cancellation at its next iteration and enqueues no further cells. Cells already handed to the queue are NOT canceled here — nothing links a queued cell back to the dispatch that enqueued it — so use `POST /api/v2/tables/{tableId}/cancel-runs` to stop work already in flight. Idempotent: a dispatch already `complete` or `canceled` is returned unchanged.\n\nOAuth scope: `api:write`.", + "description": "Stop a dispatch from scheduling more cells. Already queued or running cells continue; use Cancel Column Runs to stop them. Completed or canceled dispatches return unchanged.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.runs.cancel", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -4591,7 +4591,7 @@ "post": { "operationId": "moveTables", "summary": "Move Tables and Folders", - "description": "Move up to 100 tables and canonical-path folders to one destination; `null` or `/` means the workspace root. Processing is best-effort per item: tables already carried by selected folders are `skipped`, missing items are `notFound`, and lock or cycle refusals are `failed` with reasons. An invalid destination rejects the entire request before any move.\n\nOAuth scope: `api:write`.", + "description": "Move up to 100 tables and folders to one destination. Items succeed or fail independently: covered tables are `skipped`, missing items are `notFound`, and lock or cycle failures include reasons in `failed`. An invalid destination rejects the request before any move.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.bulk_move", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -4662,7 +4662,7 @@ "post": { "operationId": "bulkDeleteTables", "summary": "Bulk Delete Tables and Folders", - "description": "Archive up to 100 tables and delete table folders in a single authorized request. Folders are named by canonical path and each cascades to everything inside it; `deletedItems` reports the totals across every cascade. Archived tables stay recoverable through `POST /api/v2/tables/{tableId}/restore`. Best-effort per item, with the same `skipped` / `notFound` / `failed` dispositions as the bulk move.\n\nOAuth scope: `api:write`.", + "description": "Archive up to 100 selected tables and folders, including folder contents. Items succeed or fail independently, with `skipped`, `notFound`, and `failed` outcomes. `deletedItems` includes all descendants. Use Restore Table or Restore Folder to recover archived items.\n\nOAuth scope: `api:write`.", "x-sim-operation": "tables.bulk_delete", "x-oauth-scope": "api:write", "tags": ["Tables"], @@ -8135,7 +8135,7 @@ "type": "null" } ], - "description": "Background dispatch identifier, or null when execution is inline." + "description": "Run dispatch ID, or null when no dispatch is available to poll. Use row reads with `includeRunState` to check cell outcomes." } }, "required": ["dispatchId"], @@ -8534,7 +8534,7 @@ "type": "integer", "minimum": 0, "maximum": 9007199254740991, - "description": "Lower bound on the source records the CSV parser could not read and dropped, counted as one per parser failure. A single failure can discard more than one record — an unterminated quote swallows the rest of the file and is reported once — so the true loss may be larger. Non-zero means the import is partial even when the status is completed; zero is not a guarantee that nothing was dropped." + "description": "Minimum number of source records dropped by parser failures. One failure can discard multiple records, such as an unterminated quote consuming the rest of the file. A non-zero value means partial import even with `completed` status; zero does not guarantee no loss." }, "cellsRejected": { "type": "integer", @@ -8776,7 +8776,7 @@ "type": "integer", "minimum": 0, "maximum": 9007199254740991, - "description": "Lower bound on the source records the CSV parser could not read and dropped, counted as one per parser failure. A single failure can discard more than one record — an unterminated quote swallows the rest of the file and is reported once — so the true loss may be larger. Non-zero means the import is partial even when the status is completed; zero is not a guarantee that nothing was dropped." + "description": "Minimum number of source records dropped by parser failures. One failure can discard multiple records, such as an unterminated quote consuming the rest of the file. A non-zero value means partial import even with `completed` status; zero does not guarantee no loss." }, "cellsRejected": { "type": "integer", @@ -9120,7 +9120,7 @@ "type": "integer", "minimum": 0, "maximum": 9007199254740991, - "description": "Lower bound on the source records the CSV parser could not read and dropped, counted as one per parser failure. A single failure can discard more than one record — an unterminated quote swallows the rest of the file and is reported once — so the true loss may be larger. Non-zero means the import is partial even when the status is completed; zero is not a guarantee that nothing was dropped." + "description": "Minimum number of source records dropped by parser failures. One failure can discard multiple records, such as an unterminated quote consuming the rest of the file. A non-zero value means partial import even with `completed` status; zero does not guarantee no loss." }, "cellsRejected": { "type": "integer", @@ -10047,7 +10047,7 @@ }, "status": { "type": "string", - "description": "How this provider ended: `matched`, `no_match`, `skipped`, `error`, or `not_run`. Declared as a string rather than a closed enum because the value is read back out of a schemaless JSONB blob — a member added by a newer runner must widen a client's switch, not fail its read." + "description": "Provider outcome: `matched`, `no_match`, `skipped`, `error`, or `not_run`. Handle unrecognized values, since additional statuses may be returned." }, "cost": { "type": "number", diff --git a/apps/docs/openapi-v2-workflows.json b/apps/docs/openapi-v2-workflows.json index d23a31aac73..2a79bb7be6b 100644 --- a/apps/docs/openapi-v2-workflows.json +++ b/apps/docs/openapi-v2-workflows.json @@ -43,7 +43,7 @@ "get": { "operationId": "listWorkflows", "summary": "List Workflows", - "description": "List workflows in a workspace with lifecycle scope, folder and deployment filters, search, sorting, and opaque cursor pagination. `scope` defaults to `active`; pass `archived` to list workflows a `DELETE` archived. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List active workflows in a workspace. Use `scope=archived` to find workflows available for restoration. Supports folder and deployment filters, search, sorting, and cursor pagination. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.list", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -76,9 +76,9 @@ "name": "folderPath", "in": "query", "required": false, - "description": "Restrict results to workflows in this folder path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to workflows in this folder path. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to workflows in this folder path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to workflows in this folder path. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -204,7 +204,7 @@ "post": { "operationId": "createWorkflowV2", "summary": "Create Workflow", - "description": "Create a workflow in a workspace root or canonical workflow folder. The response carries the blocks the platform seeded the workflow with, so the start block's id is available without a second request — attach edges to it directly. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Create a workflow at the workspace root or in a workflow folder. The response includes seeded blocks and their IDs for attaching edges. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.create", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -281,7 +281,7 @@ "get": { "operationId": "getWorkflowState", "summary": "Get Workflow State", - "description": "Get the editable draft graph: blocks, edges, derived loop and parallel containers, and variables. This pollable read records no audit event, and `HEAD` mirrors `GET`. The unsanitized payload includes workspace-scoped credential, knowledge-base, and table ids, so it is not portable. Use `export` for a sanitized copy, but not for read-modify-write because credential bindings are removed. Returned keys exactly match what `PUT /workflows/{workflowId}/state` accepts.\n\nOAuth scope: `api:read`.", + "description": "Get the editable draft graph, including blocks, edges, loop and parallel containers, and variables. Use this state with Replace Workflow State to preserve workspace bindings; Export Workflow removes those bindings for portability. This read records no audit event, and `HEAD` mirrors `GET`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.read", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -347,7 +347,7 @@ "put": { "operationId": "replaceWorkflowState", "summary": "Replace Workflow State", - "description": "Atomically replace the editable draft graph. Concurrent writes are row-locked and last-write-wins; no partial state is stored. `loops` and `parallels` are recomputed from `blocks`; omitted `variables` remain unchanged. Foreign ids return `409`. This leaves deployment unchanged and marks the draft for redeployment; lint is advisory. `dryRun=true` runs the same validation, lint, and conflict checks without persistence, audit, or notification; `needsRedeployment` reflects pre-write state. Workspace keys are rejected; use personal keys or OAuth.\n\nOAuth scope: `api:write`.", + "description": "Replace the draft graph atomically; concurrent writes are last-write-wins. Recompute containers from blocks and preserve omitted variables. Foreign IDs return `409`; lint is advisory. The live deployment is unchanged. `dryRun=true` validates without saving, auditing, or notifying; `needsRedeployment` describes the pre-write state. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.state.replace", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -448,7 +448,7 @@ "post": { "operationId": "applyWorkflowOperations", "summary": "Apply Workflow Operations", - "description": "Apply graph edits and optional block enablement atomically. Failed operations appear in `skipped`; `deferred` edges resolve when targets exist and must not be retried. With `atomic`, any skip or dropped input returns `409` with `OPERATIONS_NOT_APPLIED` and persists nothing. Non-UUID labels are minted and same-batch references remapped in `mintedBlockIds`. Lint is advisory. `dryRun=true` runs the same checks without persistence, audit, or notification. This changes only the draft. Workspace keys are rejected; use personal keys or OAuth.\n\nOAuth scope: `api:write`.", + "description": "Edit the draft graph and block enablement in one write. Inspect `skipped` for failures; do not retry `deferred` edges. With `atomic=true`, skipped operations or dropped inputs return `409` (`OPERATIONS_NOT_APPLIED`) without saving. `mintedBlockIds` maps labels to generated IDs. Lint is advisory; `dryRun=true` validates without saving, auditing, or notifying. The live deployment is unchanged. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.operations.apply", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -549,7 +549,7 @@ "patch": { "operationId": "applyWorkflowVariables", "summary": "Update Workflow Variables", - "description": "Add, edit, and delete a workflow’s variables. Operations are matched by variable `name` and applied in order; a batch that changes nothing answers `200` with `changed: false`. Values are coerced to the declared `type`, and a value that cannot be coerced is stored as supplied. Read the current set from `variables` on `GET /workflows/{workflowId}`.\n\nOAuth scope: `api:write`.", + "description": "Add, edit, or delete variables by name, applying operations in order. Values are coerced to their declared type when possible; otherwise they are stored as supplied. A batch with no changes returns `200` with `changed: false`. Read current variables with Get Workflow.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.variables.apply_operations", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -640,7 +640,7 @@ "post": { "operationId": "duplicateWorkflow", "summary": "Duplicate Workflow", - "description": "Copy a workflow, including its blocks, edges, subflows, and variables, into the same workspace. Omitting `name` reuses the source name; a collision inside the destination folder is deduplicated rather than refused. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Copy a workflow's graph and variables into the same workspace. Omit `name` to reuse the source name; name collisions in the destination folder are resolved automatically. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.duplicate", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -731,7 +731,7 @@ "post": { "operationId": "restoreWorkflow", "summary": "Restore Workflow", - "description": "Bring an archived workflow back, along with the schedules, webhooks, MCP tools, and chats that were archived with it. A workflow that is not archived answers `409`. A workflow whose folder was archived is restored to the workspace root. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Restore an archived workflow and the schedules, webhooks, MCP tools, and chats archived with it. An active workflow returns `409`. If its folder is archived, the workflow returns to the workspace root. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.restore", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -808,7 +808,7 @@ "post": { "operationId": "moveWorkflows", "summary": "Move Workflows", - "description": "Relocate up to 100 workflows into one folder. Explicitly best-effort: each workflow moves in its own transaction, and one that is absent from the workspace, archived, or locked lands in `failed` while the rest still move. Duplicate ids are collapsed. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Move up to 100 workflows into one folder. Moves succeed or fail independently; missing, archived, or locked workflows appear in `failed`. Duplicate IDs are ignored. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.bulk.move", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -879,7 +879,7 @@ "get": { "operationId": "getWorkflow", "summary": "Get Workflow", - "description": "Get a workflow with its variables and deployed API-trigger inputs. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Get a workflow with its variables and deployed API-trigger inputs. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.read", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -948,7 +948,7 @@ "patch": { "operationId": "updateWorkflowV2", "summary": "Update Workflow", - "description": "Rename, describe, or move a workflow to a canonical folder path. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Update a workflow's name, description, or folder path. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.update", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1037,7 +1037,7 @@ "delete": { "operationId": "deleteWorkflowV2", "summary": "Delete Workflow", - "description": "Archive a workflow. Despite the verb, this is not an erasure: the workflow, and the schedules, webhooks, MCP tools, and chats attached to it, are stamped archived and stop running, and `POST /workflows/{workflowId}/restore` brings all of them back. An archived workflow disappears from the default list and is reachable with `scope=archived`. The `deleted` field is retained for shipped clients; `archived` states what actually happened.\n\nOAuth scope: `api:write`.", + "description": "Archive a workflow and stop its schedules, webhooks, MCP tools, and chats. Use List Workflows with `scope=archived` to find it and Restore Workflow to recover it and its archived resources. Both `deleted` and `archived` acknowledge archival.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.delete", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1278,7 +1278,7 @@ "patch": { "operationId": "updateWorkflowVersionV2", "summary": "Update Workflow Version", - "description": "Relabel a deployment version. Merge-patch shaped: an omitted key is unchanged and `description: null` clears the release note. Metadata only — the pinned graph is immutable, and this never changes which version is live. Promote a version with `POST /workflows/{workflowId}/versions/{version}/activate`.\n\nOAuth scope: `api:write`.", + "description": "Update a deployment version's name or release note. Omitted fields remain unchanged; `description: null` clears the note. The graph and live version remain unchanged. Use Activate Workflow Version to make this version live.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.versions.update", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1375,7 +1375,7 @@ "post": { "operationId": "activateWorkflowVersion", "summary": "Activate Workflow Version", - "description": "Promote an existing deployment version to live. Activation is asynchronous; inspect `isDeployed` and `latestDeploymentAttempt` for current state. Unlike `rollback`, the target is named by the path and the workflow need not already be deployed. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Asynchronously activate a specific deployment version, including when the workflow is not currently deployed. The draft remains unchanged. Read Get Workflow Deployment for `isDeployed` and `latestDeploymentAttempt`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.versions.activate", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1478,7 +1478,7 @@ "post": { "operationId": "revertWorkflowVersion", "summary": "Revert Workflow To Version", - "description": "Overwrite the editable draft with a deployment version, irreversibly discarding unsaved edits. This does not change the live version; use `activate` or `rollback` for production, both of which leave the draft unchanged. Pass `active` to reset the draft to the live graph. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Replace the editable draft with a deployment version, discarding current draft edits. Use `active` for the live version. The live deployment remains unchanged; Activate Workflow Version or Rollback Workflow changes it. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.versions.revert", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1589,7 +1589,7 @@ "get": { "operationId": "getWorkflowDeployment", "summary": "Get Workflow Deployment", - "description": "Read the live version, latest deployment attempt and readiness, draft drift (`needsRedeployment`), and `isPublicApi`. When `isPublicApi` is true, anyone with the execution URL can run and consume billed usage without an API key; change it with `PATCH /workflows/{workflowId}/deployment`. `/workflows/{workflowId}/deployment` controls overall API executability; `/deployments/chat` controls only the hosted-chat surface. A workflow can remain deployed without a chat.\n\nOAuth scope: `api:read`.", + "description": "Get the live version, latest deployment attempt, readiness, draft changes (`needsRedeployment`), and public API access. With `isPublicApi: true`, anyone with the execution URL can run the workflow and consume billed usage without an API key. Hosted chat is managed separately.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.read", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -1655,7 +1655,7 @@ "patch": { "operationId": "updateWorkflowPublicApi", "summary": "Update Workflow Public API Access", - "description": "Enable or disable unauthenticated public execution of the deployed workflow. While enabled, anyone holding the execution URL can run the workflow without an API key. An organization that forbids public sharing refuses this with `403` and `PUBLIC_SHARING_NOT_ALLOWED`. `/workflows/{workflowId}/deployment` controls overall API executability; `/deployments/chat` controls only the hosted-chat surface. A workflow can remain deployed without a chat. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Enable or disable unauthenticated execution of the deployed workflow. Enabling allows anyone with the execution URL to consume billed usage. Organization sharing restrictions return `403` with `PUBLIC_SHARING_NOT_ALLOWED`. Hosted chat is managed separately. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.public_api.update", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1743,7 +1743,7 @@ "post": { "operationId": "deployWorkflow", "summary": "Deploy Workflow", - "description": "Create and asynchronously activate a deployment version. Not idempotent: every call mints a new version, so a retry after a timeout creates a second one. A deployment that would conflict with an existing webhook path is a `409`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Create and asynchronously activate a deployment version. Every call creates a new version; retrying after a timeout can create a duplicate. Read Get Workflow Deployment to check activation. A conflicting webhook path returns `409`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.deploy", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1832,7 +1832,7 @@ "delete": { "operationId": "undeployWorkflow", "summary": "Undeploy Workflow", - "description": "Deactivate the currently serving workflow version. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Deactivate the currently serving workflow version. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.undeploy", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1903,7 +1903,7 @@ "post": { "operationId": "rollbackWorkflow", "summary": "Rollback Workflow", - "description": "Asynchronously reactivate a previous deployment version, selecting the preceding active version when no version is supplied. Use this to step back from the currently live version; to make a specific version live by naming it in the path — including when the workflow is not currently deployed — use `POST /workflows/{workflowId}/versions/{version}/activate`. Neither touches the draft. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Asynchronously activate a previous deployment version, defaulting to the preceding active version. Requires a deployed workflow and leaves the draft unchanged. Use Activate Workflow Version to select a version when the workflow is undeployed. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.versions.activate", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -1994,7 +1994,7 @@ "get": { "operationId": "exportWorkflow", "summary": "Export Workflow", - "description": "Export a portable, secret-sanitized workflow; workspace-scoped bindings must be selected again after import. Exporting records an audit event. `HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "Export a portable, secret-sanitized workflow; workspace-scoped bindings must be selected again after import. Exporting records an audit event. `HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.export", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -2065,7 +2065,7 @@ "post": { "operationId": "importWorkflow", "summary": "Import Workflow", - "description": "Create a workflow from a portable export object, bare state, or JSON string. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Create a workflow from a portable export object, bare state, or JSON string. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.import", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -2142,7 +2142,7 @@ "get": { "operationId": "listChatDeployments", "summary": "List Chat Deployments", - "description": "List hosted chats in a workspace with opaque cursor pagination. Filter by `workflowId` to resolve one workflow’s singleton chat. Each item includes its public URL, whose identifier is a path segment, but omits `allowedEmails`, `hasPassword`, and `customizations`; read those through the admin-only singleton endpoint. This list requires workspace read access and accepts workspace API keys. Stored passwords are never returned.\n\nOAuth scope: `api:read`.", + "description": "List hosted chats and their public URLs with cursor pagination. Filter by `workflowId` for one workflow's chat. The list requires workspace read access; Get Workflow Chat Deployment requires admin access and includes visitor access settings and customizations. Passwords are never returned.\n\nOAuth scope: `api:read`.", "x-sim-operation": "chat_deployments.list", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -2279,7 +2279,7 @@ "get": { "operationId": "getWorkflowChatDeployment", "summary": "Get Workflow Chat Deployment", - "description": "Read a workflow’s singleton hosted chat, or return `404` when none exists. `/workflows/{workflowId}/deployment` controls API execution; this singleton path controls hosted chat. `PUT` creates or replaces it without a chat-id path. The password is never returned; `hasPassword` reports its presence. Visitor-gate fields (`authType`, `hasPassword`, and `allowedEmails`) require workspace admin access. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:read`.", + "description": "Get a workflow's hosted chat and visitor access settings. Requires workspace admin access; a missing chat returns `404`. Passwords are never returned; `hasPassword` indicates whether one is set. Hosted chat and workflow API deployment are managed separately. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:read`.", "x-sim-operation": "chat_deployments.read", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -2345,7 +2345,7 @@ "put": { "operationId": "replaceWorkflowChatDeployment", "summary": "Create or Replace Workflow Chat Deployment", - "description": "Create or replace hosted chat. Omitted fields reset to defaults except per-field `customizations`. `password` is write-only and required for password auth; `allowedEmails` is required and non-empty for email or SSO. This also deploys the draft. A duplicate identifier or pending deployment returns `409`; public auth exposes the URL. `/workflows/{workflowId}/deployment` controls API execution; this singleton path controls hosted chat. `PUT` creates or replaces it without a chat-id path. Workspace keys are rejected; use personal keys or OAuth.\n\nOAuth scope: `api:write`.", + "description": "Create or replace a workflow's hosted chat and deploy its draft. Omitted fields reset to defaults except per-field customizations. Password authentication requires `password`; email or SSO requires non-empty `allowedEmails`. Public authentication allows anyone with the chat URL to use it. A duplicate identifier or pending deployment returns `409`. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "chat_deployments.replace", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -2434,7 +2434,7 @@ "delete": { "operationId": "deleteWorkflowChatDeployment", "summary": "Delete Workflow Chat Deployment", - "description": "Stop serving a workflow's hosted chat. Its URL stops answering and the identifier becomes free again. The workflow's own deployment is untouched and stays executable through the workflow API — to undeploy that, use `DELETE /workflows/{workflowId}/deploy`. Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.\n\nOAuth scope: `api:write`.", + "description": "Remove a workflow's hosted chat and release its URL identifier. The workflow API deployment remains active; use Undeploy Workflow to stop it. Workspace API keys return `403`; use a personal API key or scoped OAuth token.\n\nOAuth scope: `api:write`.", "x-sim-operation": "chat_deployments.delete", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -2502,7 +2502,7 @@ "post": { "operationId": "executeWorkflowV2", "summary": "Execute Workflow", - "description": "Execute a deployment or use `run.source: \"manual\"` for draft state. Manual runs require personal or OAuth write access and reject workspace keys, anonymous callers, and async. Start at a trigger or resume from same-workflow `sourceRunId`. Public deployments allow anonymous sync or streaming. Request `application/x-ndjson` for 15-second heartbeats and final resource. Timeouts return `200` with failed status and `TIMEOUT`. Each option carries the modes it requires and the modes that reject it; a violated combination is a 400.\n\nOAuth scope: `api:write`.", + "description": "Execute a deployment or use `run.source: \"manual\"` for the draft. Manual runs require personal or OAuth write access and reject async. Public deployments allow anonymous sync or streaming. Request `application/x-ndjson` for heartbeats and the final result. Timeouts return `200` with failed status and `TIMEOUT`. Supply `X-Run-Id` to prevent duplicate execution; reuse returns `409`, never a replay. Input descriptions specify compatible modes; invalid combinations return `400`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.execute", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -2532,9 +2532,9 @@ "name": "x-run-id", "in": "header", "required": false, - "description": "Caller-supplied run identifier, available only to API-key callers. A one-shot uniqueness claim, NOT an idempotency key: reusing a value fails with `409` and `error.details.code: \"RUN_ID_CONFLICT\"` rather than replaying the original result. To retry safely, send a fresh value per attempt, or omit the header and let the server allocate one.", + "description": "Run ID for API-key or OAuth callers; ignored for anonymous requests. Reuse it after an uncertain response: a claimed ID returns `409` with `RUN_ID_CONFLICT`, without replaying results. Check Get Workflow Run, but `404` can persist while the ID remains claimed and does not establish whether execution started. Do not automatically restart with a fresh or omitted ID; either can start another run.", "schema": { - "description": "Caller-supplied run identifier, available only to API-key callers. A one-shot uniqueness claim, NOT an idempotency key: reusing a value fails with `409` and `error.details.code: \"RUN_ID_CONFLICT\"` rather than replaying the original result. To retry safely, send a fresh value per attempt, or omit the header and let the server allocate one.", + "description": "Run ID for API-key or OAuth callers; ignored for anonymous requests. Reuse it after an uncertain response: a claimed ID returns `409` with `RUN_ID_CONFLICT`, without replaying results. Check Get Workflow Run, but `404` can persist while the ID remains claimed and does not establish whether execution started. Do not automatically restart with a fresh or omitted ID; either can start another run.", "type": "string", "minLength": 1, "maxLength": 128, @@ -2555,7 +2555,7 @@ ], "requestBody": { "required": true, - "description": "Input, workflow-state selection, and execution-mode options. Each option carries the modes it requires and the modes that reject it; a violated combination is a 400.", + "description": "Input, workflow-state selection, and execution-mode options. Input descriptions specify compatible modes; invalid combinations return `400`.", "content": { "application/json": { "schema": { @@ -2666,7 +2666,7 @@ "get": { "operationId": "listWorkflowRunsV2", "summary": "List Workflow Runs", - "description": "List recorded runs of a workflow with filtering and opaque cursor pagination. Expired runs are hard-deleted. Retention is 30 days from run start on Free, unbounded on Pro and Team, and configured per organization on Enterprise with an optional workspace override.\n\nOAuth scope: `api:read`.", + "description": "List recorded runs of a workflow with filtering and opaque cursor pagination. Expired runs are permanently deleted. Retention is 30 days from run start on Free, unlimited on Pro and Team, and configured per organization on Enterprise with workspace overrides.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.runs.list", "x-oauth-scope": "api:read", "tags": ["Workflow Runs"], @@ -2816,7 +2816,7 @@ "get": { "operationId": "getWorkflowRunV2", "summary": "Get Workflow Run", - "description": "Get current run state with optional final and block outputs. With `includeOutput`, `files` includes download paths; `includeFileBase64` reads object storage to inline bytes and returns `413` with the download path when one file or the total exceeds 16 MiB. `HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only.\n\nOAuth scope: `api:read`.", + "description": "Get current run state with optional final and block outputs. With `includeOutput`, `files` includes download paths; `includeFileBase64` inlines file bytes and returns `413` with the download path when one file or the total exceeds 16 MiB. `HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.runs.read", "x-oauth-scope": "api:read", "tags": ["Workflow Runs"], @@ -2945,7 +2945,7 @@ "get": { "operationId": "downloadWorkflowRunFileV2", "summary": "Download Workflow Run File", - "description": "Download one run-produced file by id. Downloads record an audit event. Expired runs are hard-deleted. Retention is 30 days from run start on Free, unbounded on Pro and Team, and configured per organization on Enterprise with an optional workspace override. `HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only. `HEAD` omits `Content-Length`; use file metadata to size downloads.\n\nOAuth scope: `api:read`.", + "description": "Download one run-produced file by ID. Downloads record an audit event. Expired runs are permanently deleted. Retention is 30 days from run start on Free, unlimited on Pro and Team, and configured per organization on Enterprise with workspace overrides. `HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success. `HEAD` omits `Content-Length`; use file metadata to size downloads.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.download_run_file", "x-oauth-scope": "api:read", "tags": ["Workflow Runs"], @@ -3051,7 +3051,7 @@ "post": { "operationId": "resumeWorkflowRunV2", "summary": "Resume Workflow Run", - "description": "Resume one human-in-the-loop pause context. The resumed attempt receives a new run identifier and may complete synchronously or return a queue receipt.\n\nOAuth scope: `api:write`.", + "description": "Resume one human-in-the-loop pause. The resumed attempt receives a new run ID and returns either a synchronous result or a queue receipt.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.runs.resume", "x-oauth-scope": "api:write", "tags": ["Workflow Runs"], @@ -3182,7 +3182,7 @@ "post": { "operationId": "cancelRunV2", "summary": "Cancel Workflow Run", - "description": "Request cancellation of a running, queued, or paused workflow run. Cancelling a run already in a terminal state succeeds with no effect. A run produced by a table workflow group is a `409` when its cell can no longer accept the cancellation.\n\nOAuth scope: `api:write`.", + "description": "Request cancellation of a running, queued, or paused workflow run. Terminal runs return successfully without changes. A table workflow-group run returns `409` if its cell can no longer accept cancellation.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.runs.cancel", "x-oauth-scope": "api:write", "tags": ["Workflow Runs"], @@ -3266,7 +3266,7 @@ "get": { "operationId": "listWorkflowsFolders", "summary": "List Workflow Folders", - "description": "List canonical workflow folders in a workspace. The bounded set is returned in one page; `nextCursor` is always null. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:read`.", + "description": "List workflow folders in a workspace. Returns the complete set in one page; `nextCursor` is always null. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:read`.", "x-sim-operation": "workflows.folders.list", "x-oauth-scope": "api:read", "tags": ["Workflows"], @@ -3287,9 +3287,9 @@ "name": "parentPath", "in": "query", "required": false, - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "schema": { - "description": "Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.", + "description": "Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.", "$ref": "#/components/schemas/FolderPathInput" } }, @@ -3381,7 +3381,7 @@ "post": { "operationId": "createWorkflowsFolder", "summary": "Create Workflow Folder", - "description": "Create a canonical workflow folder in a workspace. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Create a workflow folder in a workspace. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.folders.create", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -3456,7 +3456,7 @@ "patch": { "operationId": "relocateWorkflowsFolder", "summary": "Rename or Move Workflow Folder", - "description": "Rename or move a workflow folder and its descendants to a canonical path. A workspace folder tree over 10,000 folders is a `413`.\n\nOAuth scope: `api:write`.", + "description": "Rename or move a workflow folder and update all descendant paths. Workspace folder trees exceeding 10,000 folders return `413`.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.folders.relocate", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -3531,7 +3531,7 @@ "delete": { "operationId": "deleteWorkflowsFolder", "summary": "Delete Workflow Folder", - "description": "Delete a workflow folder, optionally including its descendants and workflows.\n\nOAuth scope: `api:write`.", + "description": "Archive an empty workflow folder, or set `recursive=true` to archive its subfolders and workflows. Use Restore Workflow to recover workflows.\n\nOAuth scope: `api:write`.", "x-sim-operation": "workflows.folders.delete", "x-oauth-scope": "api:write", "tags": ["Workflows"], @@ -4139,7 +4139,7 @@ "type": "integer", "minimum": 0, "maximum": 9007199254740991, - "description": "Runs that finished successfully. Failed, cancelled, and paused runs are not counted, and the counter is never reduced when a run ages out of log retention — so it does not match the size of `GET /api/v2/workflows/{workflowId}/runs`, in either direction." + "description": "Lifetime count of successful runs, excluding failed, canceled, and paused runs. Log retention does not reduce this count; it may differ from the number returned by List Workflow Runs." }, "lastRunAt": { "anyOf": [ @@ -4311,7 +4311,7 @@ "type": "integer", "minimum": 0, "maximum": 9007199254740991, - "description": "Runs that finished successfully. Failed, cancelled, and paused runs are not counted, and the counter is never reduced when a run ages out of log retention — so it does not match the size of `GET /api/v2/workflows/{workflowId}/runs`, in either direction." + "description": "Lifetime count of successful runs, excluding failed, canceled, and paused runs. Log retention does not reduce this count; it may differ from the number returned by List Workflow Runs." }, "lastRunAt": { "anyOf": [ @@ -5996,7 +5996,7 @@ "items": { "$ref": "#/components/schemas/WorkflowSkippedItem" }, - "description": "Forward-referencing edges the engine recorded rather than applied. These are NOT failures: the engine wires each one as soon as its target block exists, in this batch or a later one. Do not re-issue them." + "description": "Edges waiting for target blocks. They apply automatically when their targets exist, in this batch or a later one. Do not resubmit them." }, "inputValidationErrors": { "type": "array", @@ -6378,7 +6378,7 @@ "description": "Catalog block id, such as `cloudwatch` or `slack`. Use the block id, never an underlying tool id." }, "operation": { - "description": "Operation id from `GET /api/v2/blocks/{blockId}`. Required when the block exposes multiple operations; it may differ from the underlying tool id.", + "description": "Operation ID from Get Block. Required when the block exposes multiple operations; it may differ from the tool ID.", "type": "string", "minLength": 1, "maxLength": 255 @@ -6428,7 +6428,7 @@ "type": "string", "minLength": 1, "maxLength": 255, - "description": "Custom tool id returned by `GET /api/v2/custom-tools`." + "description": "Custom tool ID from List Custom Tools." }, "usageControl": { "type": "string", @@ -6511,7 +6511,7 @@ } ], "title": "Agent custom tool", - "description": "A workspace custom tool. Reference `customToolId` is the preferred shape; the inline declaration is retained for legacy workflow round trips.", + "description": "A workspace custom tool. Prefer `customToolId`; inline declarations are also accepted.", "examples": [ { "type": "custom-tool", @@ -7080,7 +7080,7 @@ "type": "integer", "minimum": 0, "maximum": 9007199254740991, - "description": "Runs that finished successfully. Failed, cancelled, and paused runs are not counted, and the counter is never reduced when a run ages out of log retention — so it does not match the size of `GET /api/v2/workflows/{workflowId}/runs`, in either direction." + "description": "Lifetime count of successful runs, excluding failed, canceled, and paused runs. Log retention does not reduce this count; it may differ from the number returned by List Workflow Runs." }, "lastRunAt": { "anyOf": [ @@ -7251,7 +7251,7 @@ "archived": { "type": "boolean", "const": true, - "description": "The workflow was archived, not erased. Its schedules, webhooks, MCP tools, and chats were archived with it, and `POST /workflows/{workflowId}/restore` brings all of them back." + "description": "Whether the workflow was archived. Restore Workflow recovers it and the schedules, webhooks, MCP tools, and chats archived with it." } }, "required": ["id", "deleted", "archived"], @@ -7449,7 +7449,7 @@ "format": "date-time" }, "state": { - "description": "Deployed workflow graph snapshot pinned by this version, with credential-bearing values redacted to null: `oauth-input`, `password: true`, table sub-block values, sensitive nested tool parameters, and any parameter without authoritative codec metadata.", + "description": "Workflow graph saved with this deployment version. Sensitive values are redacted to null.", "$ref": "#/components/schemas/DeployedWorkflowState" } }, @@ -8048,7 +8048,7 @@ }, "isPublicApi": { "type": "boolean", - "description": "Whether the deployed workflow accepts unauthenticated public API execution. While true, anyone holding the execution URL can run the workflow — and be billed for it — without an API key, so this is the field an audit of what a deployment exposes reads. Changed with `PATCH /workflows/{workflowId}/deployment`." + "description": "Whether anyone with the execution URL can run the deployed workflow and consume billed usage without an API key. Change this with Update Workflow Public API Access." } }, "required": [ @@ -8238,7 +8238,7 @@ ], "additionalProperties": false, "title": "Deploy result", - "description": "Deployment attempt accepted for processing. Activation is asynchronous, and `latestDeploymentAttempt` is the attempt handle — returned by every deployment mutation as well as this read. Poll activation with `isDeployed` and `deployedAt` on the workflow, or `isActive` on `GET /workflows/{workflowId}/versions`." + "description": "Deployment attempt accepted for asynchronous activation. `latestDeploymentAttempt` identifies the attempt. Poll Get Workflow Deployment for `isDeployed` and `deployedAt`, or List Workflow Versions for `isActive`." }, "DeployWorkflowResponse": { "type": "object", @@ -9474,7 +9474,7 @@ "sourceRunId": { "type": "string", "minLength": 1, - "description": "Exact prior run whose persisted execution snapshot supplies upstream block state." + "description": "Run ID supplying upstream block results when starting from a selected block." } }, "required": ["type", "blockId", "sourceRunId"], @@ -9494,7 +9494,7 @@ "type": "boolean" }, "executionTimeoutSeconds": { - "description": "Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true.", + "description": "Maximum duration of an asynchronous run, in seconds, capped by the plan's execution timeout. Requires `async: true`; otherwise returns `400`.", "type": "integer", "minimum": 1, "maximum": 604800 @@ -9505,7 +9505,7 @@ "type": "boolean" }, "selectedOutputs": { - "description": "Block output references to include in a streamed response. Use `.` for the executed workflow or `..` for a child workflow; block names are normalized workflow reference names. Selecting a child workflow applies to every invocation of it. Requires `stream: true` — it shapes the streamed envelope only, so it is rejected on a sync request and when `async` is true. To narrow a finished run, pass `selectedOutputs` to the run resource instead.", + "description": "Output references for streaming: `.` or `..`, using normalized block names. Child references apply to every invocation. Requires `stream: true` and rejects synchronous or async requests. Use `selectedOutputs` with Get Workflow Run to narrow an existing run.", "maxItems": 100, "type": "array", "items": { @@ -9536,7 +9536,7 @@ }, "additionalProperties": false, "title": "Execute workflow request", - "description": "Input, workflow-state selection, and execution-mode options. Each option carries the modes it requires and the modes that reject it; a violated combination is a 400.", + "description": "Input, workflow-state selection, and execution-mode options. Input descriptions specify compatible modes; invalid combinations return `400`.", "examples": [ { "input": { diff --git a/apps/sim/lib/api/contracts/logs.ts b/apps/sim/lib/api/contracts/logs.ts index 849d87c7595..7f7aa2c92bc 100644 --- a/apps/sim/lib/api/contracts/logs.ts +++ b/apps/sim/lib/api/contracts/logs.ts @@ -218,8 +218,13 @@ export const traceSpanSchema: z.ZodType = z id: z.string().describe('Trace-span identifier.'), name: z.string().describe('Trace-span name.'), type: z.string().describe('Trace-span category.'), - duration: z.number().describe('Legacy span duration in milliseconds.').optional(), - durationMs: z.number().describe('Span duration in milliseconds.').optional(), + duration: z.number().describe('Current trace-span duration in milliseconds.').optional(), + durationMs: z + .number() + .describe( + 'Compatibility field for span duration in milliseconds. Read `duration` for current trace spans.' + ) + .optional(), startTime: z.string().describe('ISO 8601 span start timestamp.').optional(), endTime: z.string().describe('ISO 8601 span end timestamp.').optional(), status: z.string().describe('Trace-span status.').optional(), diff --git a/apps/sim/lib/api/contracts/v2/__tests__/transport-neutral-descriptions.test.ts b/apps/sim/lib/api/contracts/v2/__tests__/transport-neutral-descriptions.test.ts index 66870ec7ec5..4f91be8cca7 100644 --- a/apps/sim/lib/api/contracts/v2/__tests__/transport-neutral-descriptions.test.ts +++ b/apps/sim/lib/api/contracts/v2/__tests__/transport-neutral-descriptions.test.ts @@ -6,79 +6,11 @@ import { listContractFiles } from '@/lib/api/contracts/v2/__tests__/contract-swe import { MAX_SCHEMA_DEPTH } from '@/lib/api/contracts/v2/__tests__/schema-introspection' /** - * Every v2 schema description is user-facing prose on two surfaces at once: the - * published API reference and `sim --help`, which is generated from - * these exact strings. A description spelling an HTTP method and path tells a - * CLI caller to do something the CLI cannot do, so descriptions name the - * operation and its object rather than the transport. - * - * This is a sweep rather than a handful of per-file assertions because the - * strings that regressed last time sat a few lines from ones already fixed by - * hand. Anything deliberately left alone goes in ALLOWED below with its reason, - * and the sweep fails when an allowlisted description no longer appears, so the - * list cannot rot. - * - * Allowlisting is keyed by the description text, not by schema path: these - * schemas are shared between contracts, so one sentence surfaces under many - * paths and fixing it must clear every one of them at once. + * Shared descriptions appear in API documentation and CLI help. Name related + * operations instead of HTTP paths so instructions work on both surfaces. */ - const ENDPOINT_SPELLING = /\b(GET|POST|PATCH|PUT|DELETE)\s+\// -/** - * Descriptions still naming a transport, deferred rather than endorsed. Each one - * lives in a v2 contract file this change does not touch, and the reason names - * that file so a later pass knows where to go. The second test below fails as - * soon as one of these stops offending, so a fix elsewhere cannot leave a stale - * entry behind. - */ -const ALLOWED = new Map([ - [ - 'Tag definition identifier. Published because `PATCH` and `DELETE /knowledge/{knowledgeBaseId}/tags/{tagId}` address a definition by it; without it those operations are unreachable from a list read.', - 'not touched here: lives in v2/knowledge.ts', - ], - [ - 'Tag definition identifier. Published for the same reason the vocabulary read publishes it: `PATCH` and `DELETE /knowledge/{knowledgeBaseId}/tags/{tagId}` address a definition by id, so without it a usage row cannot be acted on without a second read and a slot join.', - 'not touched here: lives in v2/knowledge-tags.ts', - ], - [ - 'Document tag values keyed by tag display name. Writes address the same tags by slot (`tag1`..`tag7`); resolve names to slots with GET /api/v2/knowledge/{knowledgeBaseId}/tags.', - 'not touched here: lives in v2/knowledge.ts', - ], - [ - 'ISO 8601 timestamp when the knowledge base was archived by `DELETE /knowledge/{knowledgeBaseId}`, or null while the knowledge base is active. Only `GET /knowledge?scope=archived` returns knowledge bases with a non-null value.', - 'not touched here: lives in v2/knowledge.ts', - ], - [ - 'Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too.', - 'not touched here: lives in v2/knowledge.ts', - ], - [ - 'Runs that finished successfully. Failed, cancelled, and paused runs are not counted, and the counter is never reduced when a run ages out of log retention — so it does not match the size of `GET /api/v2/workflows/{workflowId}/runs`, in either direction.', - 'not touched here: lives in v2/workflows.ts', - ], - [ - 'The workflow was archived, not erased. Its schedules, webhooks, MCP tools, and chats were archived with it, and `POST /workflows/{workflowId}/restore` brings all of them back.', - 'not touched here: lives in v2/workflows.ts', - ], - [ - 'Whether the deployed workflow accepts unauthenticated public API execution. While true, anyone holding the execution URL can run the workflow — and be billed for it — without an API key, so this is the field an audit of what a deployment exposes reads. Changed with `PATCH /workflows/{workflowId}/deployment`.', - 'not touched here: lives in v2/workflows.ts', - ], - [ - 'Operation id from `GET /api/v2/blocks/{blockId}`. Required when the block exposes multiple operations; it may differ from the underlying tool id.', - 'not touched here: lives in v2/workflows.ts', - ], - [ - 'Custom tool id returned by `GET /api/v2/custom-tools`.', - 'not touched here: lives in v2/workflows.ts', - ], - [ - 'Deployment attempt accepted for processing. Activation is asynchronous, and `latestDeploymentAttempt` is the attempt handle — returned by every deployment mutation as well as this read. Poll activation with `isDeployed` and `deployedAt` on the workflow, or `isActive` on `GET /workflows/{workflowId}/versions`.', - 'not touched here: lives in v2/workflows.ts', - ], -]) - interface Described { /** `file.ts#exportName.field`, so a failure names the symbol to edit. */ key: string @@ -177,17 +109,10 @@ describe('v2 schema descriptions', () => { const described = await sweepDescriptions() expect(described.length).toBeGreaterThan(1000) - const unexpected = [...offendingDescriptions(described)] - .filter(([description]) => !ALLOWED.has(description)) - .map(([description, keys]) => `${keys[0]} :: ${description}`) + const unexpected = [...offendingDescriptions(described)].map( + ([description, keys]) => `${keys[0]} :: ${description}` + ) expect(unexpected).toEqual([]) }) - - it('keeps the allowlist honest', async () => { - const offending = offendingDescriptions(await sweepDescriptions()) - const stale = [...ALLOWED.keys()].filter((description) => !offending.has(description)) - - expect(stale).toEqual([]) - }) }) diff --git a/apps/sim/lib/api/contracts/v2/audit-logs.ts b/apps/sim/lib/api/contracts/v2/audit-logs.ts index 47a3d0273be..b2b41ff5477 100644 --- a/apps/sim/lib/api/contracts/v2/audit-logs.ts +++ b/apps/sim/lib/api/contracts/v2/audit-logs.ts @@ -61,16 +61,10 @@ export const v2AuditLogEntrySchema = z resourceId: z .string() .nullable() - .describe( - 'Identifier of the affected resource. Always null when `resourceType` is `folder`: folders are addressed by canonical path on this API, so their internal identifiers are withheld rather than published as an id no other endpoint accepts.' - ), + .describe('Affected resource ID. Null for folder events, which identify folders by path.'), resourceName: z.string().nullable().describe('Display name of the affected resource.'), description: z.string().nullable().describe('Human-readable description of the action.'), - metadata: z - .unknown() - .describe( - 'Arbitrary per-action JSON metadata. Internal folder identifiers are stripped at every nesting level, for the same reason `resourceId` is null on a folder entry.' - ), + metadata: z.unknown().describe('Additional JSON details specific to the action.'), createdAt: z .string() .describe('ISO 8601 timestamp when the action occurred.') diff --git a/apps/sim/lib/api/contracts/v2/catalog.ts b/apps/sim/lib/api/contracts/v2/catalog.ts index 9645a04be24..9337541fab5 100644 --- a/apps/sim/lib/api/contracts/v2/catalog.ts +++ b/apps/sim/lib/api/contracts/v2/catalog.ts @@ -204,7 +204,7 @@ export type V2BlockField = z.output const catalogBlockSourceSchema = z .enum(['builtin', 'custom']) .describe( - 'Where the block comes from: `builtin` is the shipped registry, `custom` is a workflow this workspace deployed as a block.' + 'Block source: `builtin` for built-in blocks, or `custom` for workflows this workspace deployed as blocks.' ) /** Summary view of a block. */ @@ -421,7 +421,7 @@ export const v2ExecuteToolBodySchema = z ) .default({}) .describe( - 'Arguments for the tool, keyed by the parameter ids the tool catalog publishes for it. A parameter whose visibility is `user-only` also accepts an environment-variable reference written as the whole value, `{{VAR_NAME}}`, resolved server-side against the workspace environment; any other value is sent verbatim.' + 'Tool arguments keyed by published parameter IDs. For `user-only` parameters, a whole-value `{{VAR_NAME}}` reference resolves a workspace environment variable. Other values pass through unchanged.' ), credentialId: catalogIdSchema .optional() @@ -726,7 +726,7 @@ export const v2ListBlocksQuerySchema = catalogWorkspaceQuerySchema source: z .enum(['builtin', 'custom']) .optional() - .describe('Restrict to shipped blocks or to this workspace’s deployed custom blocks.'), + .describe("Restrict to built-in blocks or this workspace's deployed custom blocks."), ...v2SortFields(v2BlockSortFields, { sortBy: 'id', sortOrder: 'asc' }), ...v2PaginationFields({ description: 'Maximum blocks to return per page.' }), }) diff --git a/apps/sim/lib/api/contracts/v2/files.ts b/apps/sim/lib/api/contracts/v2/files.ts index f88101075a7..644c51557f1 100644 --- a/apps/sim/lib/api/contracts/v2/files.ts +++ b/apps/sim/lib/api/contracts/v2/files.ts @@ -159,7 +159,7 @@ export const v2FileMetadataSchema = v2FileSchema .meta({ id: 'V2FileMetadata', title: 'File metadata', - description: 'Workspace file metadata enriched with nullable public-share state.', + description: 'Workspace file metadata and current public-share configuration.', }) export type V2FileMetadata = z.output @@ -326,7 +326,7 @@ export const v2ListFilesQuerySchema = z folderPath: v2FolderPathInputSchema .optional() .describe( - `Restrict results to files inside this folder — its direct children, or its whole subtree when \`recursive\` is true. ${V2_FOLDER_FILTER_MISS}` + `Restrict files to this folder, including subfolders when \`recursive\` is true. ${V2_FOLDER_FILTER_MISS}` ), /** * Descend into subfolders. Meaningful only alongside `folderPath`: with no folder filter @@ -346,7 +346,7 @@ export const v2ListFilesQuerySchema = z .stringbool({ case: 'sensitive' }) .optional() .describe( - 'Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.' + 'Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter.' ) .meta({ enum: [...V2_TRUE_VALUES, ...V2_FALSE_VALUES] }), scope: v2FileScopeSchema @@ -1036,7 +1036,7 @@ export const v2BulkDownloadFilesQuerySchema = z `File identifiers to include, comma-separated. At most ${MAX_ZIP_DOWNLOAD_FILES} entries.` ), folderPaths: v2QuerySelectionListSchema('folderPaths').describe( - `Folder paths to include with all their descendants, comma-separated. At most ${MAX_ZIP_DOWNLOAD_FILES} entries, and the files they resolve to count against the same ${MAX_ZIP_DOWNLOAD_FILES}-file download ceiling. A path that matches no folder is rejected rather than ignored.` + `Comma-separated folder paths whose contents are included recursively. Up to ${MAX_ZIP_DOWNLOAD_FILES} paths; resolved files share the ${MAX_ZIP_DOWNLOAD_FILES}-file download limit. Unknown paths are rejected.` ), }) .strict() @@ -1366,7 +1366,7 @@ export const v2FileSearchResultsSchema = z complete: z .boolean() .describe( - 'True when no file in the searched scope is still pending or failed indexing. It does NOT cover `skippedFiles` (never indexed, such as binaries) or `partialFiles` (indexed only in part), so a missing match is authoritative only when all three are clear. Treat any of them as nonzero meaning unknown rather than absent.' + 'True when no files in the searched scope have pending or failed indexing. Missing matches remain inconclusive unless this is true and both `indexStatus.skippedFiles` and `indexStatus.partialFiles` are zero.' ), indexStatus: z .object({ diff --git a/apps/sim/lib/api/contracts/v2/knowledge-tags.ts b/apps/sim/lib/api/contracts/v2/knowledge-tags.ts index 03e671c2fc5..82625ad5bd3 100644 --- a/apps/sim/lib/api/contracts/v2/knowledge-tags.ts +++ b/apps/sim/lib/api/contracts/v2/knowledge-tags.ts @@ -183,9 +183,7 @@ export const v2KnowledgeTagUsageSchema = z .object({ id: z .string() - .describe( - 'Tag definition identifier. Published for the same reason the vocabulary read publishes it: `PATCH` and `DELETE /knowledge/{knowledgeBaseId}/tags/{tagId}` address a definition by id, so without it a usage row cannot be acted on without a second read and a slot join.' - ) + .describe('Tag definition ID used by Update Tag and Delete Tag.') .meta({ examples: ['7c9e6679-7425-40de-944b-e07fc1f90ae7'] }), tagSlot: z .string() diff --git a/apps/sim/lib/api/contracts/v2/knowledge.ts b/apps/sim/lib/api/contracts/v2/knowledge.ts index 6bd8473a3c4..2cd2c6a86e4 100644 --- a/apps/sim/lib/api/contracts/v2/knowledge.ts +++ b/apps/sim/lib/api/contracts/v2/knowledge.ts @@ -173,7 +173,7 @@ export const v2KnowledgeBaseSchema = knowledgeBaseDataSchema deletedAt: v2TimestampSchema .nullable() .describe( - 'ISO 8601 timestamp when the knowledge base was archived by `DELETE /knowledge/{knowledgeBaseId}`, or null while the knowledge base is active. Only `GET /knowledge?scope=archived` returns knowledge bases with a non-null value.' + 'ISO 8601 archive timestamp, or null while active. Use List Knowledge Bases with `scope=archived` to find archived knowledge bases.' ) .meta({ format: 'date-time', examples: ['2026-01-16T09:00:00Z'] }), }) @@ -281,7 +281,7 @@ export const v2KnowledgeDocumentTagsSchema = z .describe('Tag value; dates are ISO 8601 strings and an unset tag is null.') ) .describe( - 'Document tag values keyed by tag display name. Writes address the same tags by slot (`tag1`..`tag7`); resolve names to slots with GET /api/v2/knowledge/{knowledgeBaseId}/tags.' + 'Document tag values keyed by display name. Writes use slots such as `tag1`; use List Tags to map names to slots.' ) .meta({ examples: [{ category: 'billing', priority: 2 }] }) @@ -627,7 +627,7 @@ export const v2ListKnowledgeBasesQuerySchema = z scope: v2KnowledgeBaseScopeSchema .default('active') .describe( - 'Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too.' + 'Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches.' ), folderPath: v2FolderPathInputSchema .optional() @@ -1188,11 +1188,7 @@ export const v2GetKnowledgeDocumentContract = defineRouteContract({ */ export const v2KnowledgeTagSchema = z .object({ - id: z - .string() - .describe( - 'Tag definition identifier. Published because `PATCH` and `DELETE /knowledge/{knowledgeBaseId}/tags/{tagId}` address a definition by it; without it those operations are unreachable from a list read.' - ), + id: z.string().describe('Tag definition ID used by Update Tag and Delete Tag.'), displayName: z .string() .describe('Display name used by tag filters and by tag values on document reads.') diff --git a/apps/sim/lib/api/contracts/v2/logs-stats.ts b/apps/sim/lib/api/contracts/v2/logs-stats.ts index 64a449982ef..9408cac6729 100644 --- a/apps/sim/lib/api/contracts/v2/logs-stats.ts +++ b/apps/sim/lib/api/contracts/v2/logs-stats.ts @@ -34,7 +34,7 @@ const v2SegmentCountSchema = z.coerce .optional() .default(DEFAULT_SEGMENT_COUNT) .describe( - `Number of equal time buckets to divide the window into, from 1 to ${MAX_STATS_SEGMENT_COUNT}. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty.` + `Number of time buckets, up to ${MAX_STATS_SEGMENT_COUNT}. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets.` ) const v2LogSegmentSchema = z @@ -142,7 +142,7 @@ export const v2LogStatsQuerySchema = z folderPaths: z .string() .describe( - `Comma-separated workflow folder paths to include. At most ${V2_LOG_FOLDER_PATHS_MAX} entries. A path covers its whole subtree. ${V2_FOLDER_FILTER_MISS}` + `Comma-separated workflow folder paths, including descendants. Up to ${V2_LOG_FOLDER_PATHS_MAX} paths. ${V2_FOLDER_FILTER_MISS}` ) .optional() .transform((value, ctx) => { diff --git a/apps/sim/lib/api/contracts/v2/logs.ts b/apps/sim/lib/api/contracts/v2/logs.ts index 8c3af5a7f23..4f4eab05c7a 100644 --- a/apps/sim/lib/api/contracts/v2/logs.ts +++ b/apps/sim/lib/api/contracts/v2/logs.ts @@ -164,7 +164,7 @@ const v2LogWorkflowStateSchema = z ) .nullable() .describe( - 'Workflow graph snapshot captured for the run, or null when none is retained. Credential-bearing values are redacted to null: `oauth-input`, `password: true`, table sub-block values, sensitive nested tool parameters, and any parameter without authoritative codec metadata. `{{VAR}}` references in non-opaque fields are preserved.' + 'Workflow graph captured for the run, or null if unavailable. Sensitive values are redacted to null; environment-variable references may be preserved.' ) const v2LogWorkflowSummarySchema = z.object({ @@ -599,7 +599,7 @@ export const v2ListLogsQuerySchema = v1ListLogsQuerySchema folderPaths: z .string() .describe( - `Comma-separated workflow folder paths to include. At most ${V2_LOG_FOLDER_PATHS_MAX} entries. A path covers its whole subtree, so \`/prod\` also selects runs in \`/prod/nested\`. ${V2_FOLDER_FILTER_MISS}` + `Comma-separated workflow folder paths, including descendants. Up to ${V2_LOG_FOLDER_PATHS_MAX} paths. ${V2_FOLDER_FILTER_MISS}` ) .optional() .transform((value, ctx) => { diff --git a/apps/sim/lib/api/contracts/v2/mcp-servers.ts b/apps/sim/lib/api/contracts/v2/mcp-servers.ts index a6e3e3329f9..d711e4ee851 100644 --- a/apps/sim/lib/api/contracts/v2/mcp-servers.ts +++ b/apps/sim/lib/api/contracts/v2/mcp-servers.ts @@ -217,9 +217,7 @@ export const v2CreateMcpServerBodySchema = z .describe('Optional server description.'), transport: mcpTransportSchema .optional() - .describe( - 'Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.' - ) + .describe('Transport protocol. Defaults to `streamable-http` on creation.') .meta({ default: 'streamable-http' }), url: v2McpServerUrlSchema, /** @@ -247,9 +245,7 @@ export const v2CreateMcpServerBodySchema = z .min(1000, 'timeout must be at least 1000ms') .max(300000, 'timeout must be at most 300000ms') .optional() - .describe( - 'Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.' - ) + .describe('Per-request timeout in milliseconds. Defaults to 30000 on creation.') .meta({ default: 30_000 }), retries: z .number() @@ -257,14 +253,12 @@ export const v2CreateMcpServerBodySchema = z .min(0, 'retries cannot be negative') .max(10, 'retries must be at most 10') .optional() - .describe('Number of retries per request. Applied server-side as 3 when omitted on create.') + .describe('Number of retries per request. Defaults to 3 on creation.') .meta({ default: 3 }), enabled: z .boolean() .optional() - .describe( - 'Whether the server tools are available to workflows. Applied server-side as true when omitted on create.' - ) + .describe("Whether workflows can use the server's tools. Defaults to true on creation.") .meta({ default: true }), oauthClientId: z .string() @@ -367,7 +361,7 @@ export const v2ListMcpServerToolsQuerySchema = v2McpServerWorkspaceQuerySchema .optional() .default(false) .describe( - 'Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip.' + "Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools." ), }) .strict() diff --git a/apps/sim/lib/api/contracts/v2/openapi/README.md b/apps/sim/lib/api/contracts/v2/openapi/README.md new file mode 100644 index 00000000000..c450535b2b0 --- /dev/null +++ b/apps/sim/lib/api/contracts/v2/openapi/README.md @@ -0,0 +1,39 @@ +# API description conventions + +Write enough context to choose and use an operation correctly. Concision means removing repetition and implementation history while preserving behavior that changes the caller's next action. + +## Where descriptions belong + +| Location | Content | +| --- | --- | +| Operation `summary` | A short action and resource name, using the existing title-case convention: `Create Workflow`, `Replace File Content`. | +| Operation `description` | The result, relevant scope, and important behavior: side effects, partial success, asynchronous completion, destructive changes, retry safety, or a distinction from a related operation. | +| Parameter or schema description | The field's meaning, units, omission/null behavior, dependencies, and interpretation. Let the schema declare types, required fields, enums, and bounds; explain them in prose only when needed for correct use. | +| Response description | What the result represents, including partial, truncated, pending, or redacted results. | +| Shared metadata | Authentication, pagination exceptions, retention, and other rules reused across operations. Reuse the existing constants and generated OAuth scope annotations. | +| Examples | Valid request and response shapes, especially for nested or format-sensitive inputs. Keep them in structured example fields. | + +## Writing rules + +- Start with an active verb and the resource: “Archive a workspace file.” Follow with the facts that change how a caller uses the operation. +- Aim for one to three sentences for ordinary operations. Simple operations need less; complex operations need more. The existing 80-word description check is a ceiling, not a target or a reason to remove critical behavior. It includes generated OAuth scope text. +- Use consistent terms: **Get** for a single resource, **List** for a collection, **Create** for a new resource, **Update** for partial changes, **Replace** for complete replacement, and **Archive** when data remains recoverable. Existing operation IDs and paths remain stable. +- Describe actual patch semantics. Do not claim JSON Merge Patch compliance or atomicity merely because an operation uses `PATCH` or saves a batch. +- Keep destructive scope, cleared fields, partial commits, duplicate-execution risks, and polling instructions explicit. Never promise retry safety or completeness without implementation evidence. +- Name related operations consistently. Shared schema descriptions also appear in CLI help, so use operation names rather than HTTP method/path instructions there. +- Use the same wording for the same behavior across resource families: “Omitted fields remain unchanged,” “Archive,” and “permanently delete.” Describe completion as “during the request” or “asynchronously” instead of “settled inline.” Keep distinctions where behavior differs. +- Remove implementation rationale, migration history, rhetorical warnings, and claims such as “far above any real inventory.” Keep practical limits and how to handle them. +- Describe user-visible outcomes instead of storage formats, locking, redaction internals, or deployment architecture. For an open-ended status, say to handle unknown values; the caller does not need to know how statuses are stored. Keep implementation details only when they change correct usage. +- Avoid generic filter inventories and repeated schema details. Preserve exceptions such as bounded pages, null cursors with truncated results, and counts read independently of result pages. + +For example, an upsert description should say: “Insert a row or replace the row matching a selected unique column. On replacement, omitted columns are cleared; send the complete row. Use Update Row for a partial patch.” + +## Sources and verification + +OpenAPI distinguishes short summaries from detailed descriptions and recommends explaining information beyond the schema. Its [documentation guide](https://learn.openapis.org/specification/docs.html) and [single-source guidance](https://learn.openapis.org/best-practices.html) support this separation. + +Agent guidance emphasizes clear purpose, inputs, outputs, and caveats. [OpenAI's function-calling guide](https://developers.openai.com/api/docs/guides/function-calling) recommends detail sufficient to use a tool correctly. [Anthropic's tool guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) recommends several sentences and more for complex tools. Neither establishes a universal optimal word count. Evaluate tool selection and argument correctness on representative tasks before claiming an agent-performance improvement from shorter descriptions. + +Edit operation metadata here and field descriptions in their source contracts. Regenerate with `bun run generate:openapi`, `bun run generate:cli-api`, and `bun run generate:cli-docs`; check with the matching `check:*` commands and `bun run check:api-validation`. Do not edit generated artifacts by hand. + +The CLI currently consumes operation summaries and input descriptions. Full operation descriptions are published in OpenAPI but are not displayed in CLI help; do not assume those paragraphs reach every agent. diff --git a/apps/sim/lib/api/contracts/v2/openapi/billing.ts b/apps/sim/lib/api/contracts/v2/openapi/billing.ts index 0710b2bf383..0a1e1e1b4e4 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/billing.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/billing.ts @@ -83,7 +83,7 @@ const routes = [ operationId: 'getBillingStatus', summary: 'Get Billing Status', description: - "Return the current plan, billing standing, credit allowance, and storage quota. `credits` and `storage` report the payer's pooled allowances and are null unless the caller can manage that payer's billing; they are always null for a workspace API key. Billing history lives at `GET /api/v2/billing/logs`.", + "Get the current plan, billing standing, credit allowance, and storage quota. Pooled `credits` and `storage` are visible only to callers who can manage the payer's billing; workspace API keys receive null for both. Use List Billing Logs for credit history.", errors: RESOURCE_ERRORS, success: { description: 'The current billing and storage status.' }, }), @@ -110,7 +110,7 @@ const routes = [ operationId: 'listBillingLogs', summary: 'List Billing Logs', description: - 'List the credit-denominated billing ledger with source filtering and opaque cursor pagination. `period` defaults to `30d`, so an unqualified request covers only the last 30 days: paginating to `nextCursor: null` exhausts that window, not the whole ledger. An inverted custom window is a 400 rather than an empty page.', + 'List credit usage with source filtering and cursor pagination. The default `period` is `30d`; pagination covers only the selected time window. An inverted custom window returns `400`.', errors: RESOURCE_ERRORS, success: { description: 'A page of usage events.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/files-audit.ts b/apps/sim/lib/api/contracts/v2/openapi/files-audit.ts index 98082d2c529..ceb223ae4d2 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/files-audit.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/files-audit.ts @@ -138,7 +138,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.list, operationId: 'listFiles', summary: 'List Files', - description: `List workspace files with search, sorting, folder filtering, and opaque cursor pagination. Defaults to active files; pass \`scope=archived\` to page over soft-deleted ones. ${FOLDER_TREE_TOO_LARGE}`, + description: `List active workspace files with folder filtering, search, sorting, and cursor pagination. Use \`scope=archived\` to find files available for restoration. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'A page of workspace files.' }, }), @@ -234,7 +234,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.uploadRead, operationId: 'getFileUpload', summary: 'Get File Upload', - description: `Read an upload session's current state — whether it is still accepting bytes, has finalized into a file, or has failed. Use it to decide whether an interrupted transfer can be resumed or should be abandoned. Like every other upload control leg it requires the signed upload token, and is re-authorized against the workspace on each call.`, + description: + "Get an upload session's state to determine whether an interrupted transfer can resume. Requires the signed upload token and current workspace access.", errors: RESOURCE_ERRORS, success: { description: 'Current upload-session state.' }, }), @@ -271,7 +272,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.uploadCancel, operationId: 'abortFileUpload', summary: 'Abort File Upload', - description: 'Abort an active upload session and release provider-side multipart state.', + description: + 'Abort an incomplete upload session and discard its uploaded data. Completed uploads cannot be aborted.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The aborted upload session.' }, }), @@ -353,7 +355,7 @@ const declaredRoutes = [ operationId: 'completeFileUpload', summary: 'Complete File Upload', description: - 'Finalize uploaded bytes, verify provider state, and begin atomic workspace-file registration.', + 'Finalize an upload and register its workspace file. Repeating a completed upload returns the existing file.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The completed or finalizing upload session.' }, }), @@ -390,7 +392,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.readContent, operationId: 'readFileText', summary: 'Read File Text', - description: `Extract text from stored file bytes without modifying the file; use \`POST /api/v2/files/{fileId}/unzip\` to unpack archives. Unsupported types return \`400\` and point to raw-byte download; generated documents still compiling return \`409\`, and files above the extraction ceiling return \`413\`. \`degraded: true\` means extraction was incomplete or synthesized from raw bytes and is not authoritative; legacy \`.doc\` and \`.ppt\` extraction may return this best-effort result. \`truncated\` means a parser limit stopped extraction.`, + description: + 'Extract text without changing the file. Use Unzip File to unpack archives or Download File for original bytes. Unsupported types return `400`, compiling documents return `409`, and oversized files return `413`. `degraded: true` indicates incomplete or synthesized text, including some legacy `.doc` and `.ppt` results; `truncated: true` indicates a parser limit.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The extracted text and its extraction-quality flags.' }, }), @@ -421,7 +424,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.download, operationId: 'bulkDownloadFiles', summary: 'Bulk Download Files', - description: `Stream files as a zip. Provide comma-separated file IDs and folder paths; folders expand recursively, and unmatched paths are rejected. Each parameter and the resolved selection allow at most ${MAX_ZIP_DOWNLOAD_FILES} entries, with bytes bounded. Oversized selections return \`400\`; downloads record an audit event. ${HEAD_MIRRORS_GET} ${HEAD_OMITS_PAYLOAD_HEADERS}`, + description: `Stream selected files and recursive folder contents as a ZIP archive. Each selection parameter and the resolved set allow ${MAX_ZIP_DOWNLOAD_FILES} entries; unmatched paths or excess entries return \`400\`. Total bytes are bounded. Downloads record an audit event. ${HEAD_MIRRORS_GET} ${HEAD_OMITS_PAYLOAD_HEADERS}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The selected files as a zip archive.', @@ -445,7 +448,7 @@ const declaredRoutes = [ operationId: 'unzipFile', summary: 'Unzip File', description: - 'Unzip a `.zip` archive into a new sibling folder, creating workspace files and returning only counts and the destination path. Use `GET /api/v2/files/{fileId}/text` to read text; page `GET /api/v2/files?folderPath=...` to inspect unpacked files. Large archives can take minutes. Only one unzip per archive may run; concurrent attempts return `409`. Archives above the size ceiling or operations exceeding their time budget return `413`.', + 'Extract a ZIP archive into a new sibling folder and return counts and the destination path. Use List Files to inspect its contents. Large archives can take minutes; concurrent extraction of the same archive returns `409`. Size or processing-time limits return `413`.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'Counts and destination folder for the unpacked archive.' }, }), @@ -507,7 +510,7 @@ const declaredRoutes = [ operationId: 'deleteFile', summary: 'Delete File', description: - 'Archive a workspace file. This is a soft delete: the file stops appearing in the default listing and is no longer readable through the API, but its stored bytes are never removed. Archiving an already-archived file is a `404`, not a no-op. List archived files with `GET /files?scope=archived`, and reverse the delete with `POST /files/{fileId}/restore`.', + 'Archive a workspace file, retaining its stored bytes and removing API read access. List Files with `scope=archived` finds it; Restore File recovers it. Archiving an already archived file returns `404`.', errors: RESOURCE_ERRORS, success: { description: 'Deletion confirmation.' }, }), @@ -578,7 +581,7 @@ const declaredRoutes = [ operationId: 'restoreFile', summary: 'Restore File', description: - 'Reverse a soft delete and return the file to the workspace. Not a pure undo: the file comes back at the workspace root, and gains a `_restored` suffix when another file there already holds its name, so read `folderPath` and `name` off the response. Restoring an already-active file returns it unchanged, so a retry is safe. An archived workspace is a `400`, and a name the restore could not free is a `409`.', + 'Restore an archived file to the workspace root. Name collisions add a `_restored` suffix; use the returned `name` and `folderPath`. An active file returns unchanged. An archived workspace returns `400`; an unresolved name collision returns `409`.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The file as it exists after the restore.' }, }), @@ -612,7 +615,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.readMetadata, operationId: 'getFile', summary: 'Get File Metadata', - description: 'Return file metadata together with the nullable current public-share state.', + description: + 'Get file metadata and its public-share configuration. The `share` field is null when the file has never been shared.', errors: RESOURCE_ERRORS, success: { description: 'File metadata and public-share state.' }, }), @@ -673,7 +677,7 @@ const declaredRoutes = [ applicationOperation: auditLogOperations.readDetail, operationId: 'getAuditLog', summary: 'Get Audit Log', - description: `Return one organization audit-log entry. Requires an Enterprise subscription and organization admin or owner access. ${WORKSPACE_API_KEY_DENIED}`, + description: `Get one organization audit-log entry. Requires an Enterprise subscription and organization admin or owner access. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'The requested audit-log entry.' }, }), @@ -705,7 +709,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.move, operationId: 'moveFileItems', summary: 'Move Files', - description: 'Move up to 1,000 files to a canonical folder path or the workspace root.', + description: 'Move up to 1,000 files to a folder path or the workspace root.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'Count of moved files.' }, }), @@ -740,7 +744,7 @@ const declaredRoutes = [ operationId: 'getFileShare', summary: 'Get File Share', description: - 'Return the nullable current public-share configuration for a file. A file that has never been shared returns `data: null` rather than a 404; a share that was created and later disabled is still returned, with `isActive: false`.', + "Get a file's public-share configuration. An unshared file returns `data: null`; a disabled share returns its configuration with `isActive: false`.", errors: RESOURCE_ERRORS, success: { description: 'Current nullable file-share state.' }, }), @@ -772,7 +776,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.updateShare, operationId: 'upsertFileShare', summary: 'Enable or Disable File Share', - description: `Create or partially update a server-tokenized public share. Only \`isActive\` is required; each other field states what enabling a mode does to it. Enabling any mode other than \`public\` on a file that has never been shared must carry its credential in the same request. ${WORKSPACE_API_KEY_DENIED}`, + description: `Create or update a file's public share. \`isActive\` is required; other fields describe their behavior when access modes change. Enabling a protected mode on a previously unshared file requires its credential in the same request. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'The updated file share.' }, }), @@ -816,7 +820,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.updateContent, operationId: 'editFileContent', summary: 'Edit File Content', - description: `Modify part of a text file; \`PUT\` on this path replaces the whole file. \`search_replace\` requires one exact match unless \`replaceAll\` is true. The anchored modes match trimmed complete lines: replacement preserves both boundaries, insertion preserves its anchor, and deletion removes the start but preserves the end. Use \`occurrence\` for repeated anchors. Non-UTF-8 files return \`400\`. Concurrent writes return \`409\`; re-read before retrying.`, + description: + 'Edit part of a UTF-8 file; use Replace File Content to replace it entirely. Search-and-replace requires one exact match unless `replaceAll` is true. Anchored modes match trimmed complete lines; their input descriptions specify boundary handling. Non-UTF-8 files return `400`. Concurrent writes return `409`; re-read before retrying.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge', 'Locked'], success: { description: 'The edited file and its new line count.' }, }), @@ -867,7 +872,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.searchContent, operationId: 'searchFileContent', summary: 'Search File Content', - description: `Search indexed text in active workspace files and return matching lines with file IDs and line numbers. \`folderPaths\` limits both results and the coverage reported by \`complete\` and \`indexStatus\`. Because indexing is asynchronous, a missing term is unknown rather than absent when \`complete\` is false. \`truncated\` means additional matches exist beyond \`maxResults\`.`, + description: + 'Search indexed text in active workspace files and return matching lines with file IDs and line numbers. `folderPaths` limits both results and reported coverage. Missing matches are inconclusive if `complete` is false or `indexStatus.skippedFiles` or `indexStatus.partialFiles` is nonzero. `truncated` means additional matches exist beyond `maxResults`.', errors: [...WORKSPACE_ERRORS, 'NotFound', 'Locked'], success: { description: 'Matching lines and the index coverage they were drawn from.' }, }), @@ -955,7 +961,7 @@ const declaredRoutes = [ operationId: 'bulkDeleteFiles', summary: 'Delete Files', description: - 'Delete up to 1,000 workspace files in one operation. This is the same soft delete as \`DELETE /api/v2/files/{fileId}\`: files are archived, not erased, and \`POST /api/v2/files/{fileId}/restore\` reverses each one.', + 'Archive up to 1,000 workspace files while retaining their stored bytes. Use Restore File to recover each file.', errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'Count of deleted files.' }, }), @@ -988,7 +994,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.listFolders, operationId: 'listFilesFolders', summary: 'List Folders', - description: `List workspace file folders with optional parent-path filtering and sorting. Pass \`scope=archived\` to list folders a recursive \`DELETE\` soft-deleted, which is how a caller finds a path to hand to \`POST /api/v2/files/folders/restore\`. ${FULL_SET_LIST}`, + description: `List workspace file folders with parent-path filtering and sorting. Use \`scope=archived\` to find paths accepted by Restore Folder. ${FULL_SET_LIST}`, errors: RESOURCE_ERRORS, success: { description: 'Workspace file folders.' }, }), @@ -1014,7 +1020,7 @@ const declaredRoutes = [ operationId: 'restoreFilesFolder', summary: 'Restore Folder', description: - 'Restore a soft-deleted folder and everything archived with it. `DELETE /api/v2/files/folders` archives recursively, so this is what makes a recursive delete recoverable: without it the archived files stay visible through `GET /api/v2/files?scope=archived` but the folder structure cannot be rebuilt. Address the folder by the path reported by `GET /api/v2/files/folders?scope=archived`; a path that is not archived answers `404`.', + 'Restore a folder and the files and subfolders archived with it. Use the path from List Folders with `scope=archived`. A path that is not archived returns `404`.', errors: [...RESOURCE_CONFLICT_ERRORS], success: { description: 'The restored folder and what it brought back.' }, }), @@ -1040,7 +1046,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.createFolder, operationId: 'createFilesFolder', summary: 'Create Folder', - description: 'Create a canonical folder path in a workspace.', + description: 'Create a folder at the supplied workspace path.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The created folder.' }, }), @@ -1072,7 +1078,7 @@ const declaredRoutes = [ applicationOperation: fileOperations.updateFolder, operationId: 'relocateFilesFolder', summary: 'Rename or Move Folder', - description: 'Rename or move a folder and atomically rewrite descendant canonical paths.', + description: 'Rename or move a folder and atomically update all descendant paths.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The relocated folder.' }, }), @@ -1105,7 +1111,8 @@ const declaredRoutes = [ applicationOperation: fileOperations.deleteFolder, operationId: 'deleteFilesFolder', summary: 'Delete Folder', - description: 'Delete a folder, optionally including every nested file and folder.', + description: + 'Archive an empty folder, or set `recursive=true` to archive its files and subfolders. Use Restore Folder to recover the archived contents.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'Folder deletion confirmation and deleted item counts.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/knowledge-chunks.ts b/apps/sim/lib/api/contracts/v2/openapi/knowledge-chunks.ts index 936f28ee511..989aa2cfd74 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/knowledge-chunks.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/knowledge-chunks.ts @@ -29,7 +29,7 @@ import { knowledgeOperations } from '@/lib/knowledge/application/operations' */ const DOCUMENT_NOT_READY = - 'A document that has not finished processing answers `409`; the message names the status it is in.' + 'Documents that have not finished processing return `409` with their current status.' const CONNECTOR_MANAGED = 'Connector-synced chunks are read-only and return `403` with `error.details.code: "CONNECTOR_MANAGED_RESOURCE_READ_ONLY"`; change the source and re-sync, or exclude the document.' @@ -40,7 +40,7 @@ export const knowledgeChunkOpenApiRoutes = [ applicationOperation: knowledgeOperations.listChunks, operationId: 'listKnowledgeChunks', summary: 'List Chunks', - description: `List the passages a document was split into, with content search, enabled filtering, sorting, and opaque cursor pagination. Tag values are projected by slot; resolve slots to display names with \`GET /api/v2/knowledge/{knowledgeBaseId}/tags\`. ${DOCUMENT_NOT_READY} ${WORKSPACE_API_KEY_DENIED}`, + description: `List document chunks with content search, enabled filtering, sorting, and cursor pagination. Tags use slots; use List Tags to resolve display names. ${DOCUMENT_NOT_READY} ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'A page of document chunks.' }, }), @@ -148,7 +148,7 @@ export const knowledgeChunkOpenApiRoutes = [ applicationOperation: knowledgeOperations.readChunk, operationId: 'getKnowledgeChunk', summary: 'Get Chunk', - description: `Retrieve one chunk of a document, including the exact text that was embedded. ${DOCUMENT_NOT_READY} ${WORKSPACE_API_KEY_DENIED}`, + description: `Get one chunk of a document, including the exact text that was embedded. ${DOCUMENT_NOT_READY} ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The requested chunk.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/knowledge-tags.ts b/apps/sim/lib/api/contracts/v2/openapi/knowledge-tags.ts index e3b9f3fedf6..b356ca63709 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/knowledge-tags.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/knowledge-tags.ts @@ -37,7 +37,7 @@ export const knowledgeTagOpenApiRoutes = [ applicationOperation: knowledgeOperations.createTag, operationId: 'createKnowledgeTag', summary: 'Create Tag', - description: `Define one tag; use \`PUT\` on this path for several. Write its \`tagSlot\` on documents, then filter by \`displayName\`. Omitting \`tagSlot\` selects the next free slot; exhaustion returns \`400\`. An occupied slot or duplicate display name returns \`409\` naming the conflict. ${WORKSPACE_API_KEY_DENIED}`, + description: `Create a tag definition. Write document values by \`tagSlot\` and filter by \`displayName\`. Omitting \`tagSlot\` selects a free slot; exhaustion returns \`400\`. An occupied slot or duplicate name returns \`409\`. Use Bulk Save Tag Definitions for multiple definitions. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The created tag definition.' }, }), @@ -103,7 +103,7 @@ export const knowledgeTagOpenApiRoutes = [ applicationOperation: knowledgeOperations.deleteTag, operationId: 'deleteKnowledgeTag', summary: 'Delete Tag', - description: `Remove a tag definition and clear its slot across every document and chunk in the knowledge base. Without a definition the slot has no meaning, so leaving the values would strand them under a raw slot name — this is not recoverable. ${WORKSPACE_API_KEY_DENIED}`, + description: `Permanently delete a tag definition and its values from every document and chunk in the knowledge base. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'Tag deletion acknowledgement.' }, }), @@ -134,7 +134,7 @@ export const knowledgeTagOpenApiRoutes = [ applicationOperation: knowledgeOperations.readNextTagSlot, operationId: 'getNextKnowledgeTagSlot', summary: 'Get Next Tag Slot', - description: `Report which slot a create would take for a field type, and how many are left. Advisory rather than a claim: nothing is reserved, and \`POST /api/v2/knowledge/{knowledgeBaseId}/tags\` assigns the same slot when \`tagSlot\` is omitted. ${WORKSPACE_API_KEY_DENIED}`, + description: `Get the next available slot and remaining capacity for a field type. This does not reserve a slot. Create Tag selects a free slot when \`tagSlot\` is omitted. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'Slot availability for the requested field type.' }, }), @@ -165,7 +165,7 @@ export const knowledgeTagOpenApiRoutes = [ applicationOperation: knowledgeOperations.readTagUsage, operationId: 'listKnowledgeTagUsage', summary: 'List Tag Usage', - description: `Report how many documents and chunks carry a value for each defined tag, so a caller can tell a tag that is actually populated from one that was only declared. ${FULL_SET_LIST} ${WORKSPACE_API_KEY_DENIED}`, + description: `Count the documents and chunks with a value for each defined tag. ${FULL_SET_LIST} ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'Usage counts for every defined tag.' }, }), @@ -196,7 +196,7 @@ export const knowledgeTagOpenApiRoutes = [ applicationOperation: knowledgeOperations.saveDocumentTagDefinitions, operationId: 'bulkSaveKnowledgeTagDefinitions', summary: 'Bulk Save Tag Definitions', - description: `Declare multiple tag definitions while leaving unspecified slots unchanged. Updating requires the current name in \`originalDisplayName\`; otherwise the entry creates a tag. Occupied explicit slots and duplicate display names appear in per-definition \`errors\`, never overwrite or relocate data, and still return \`200\`. This writes the vocabulary, not document tag values; set those through the document update endpoint. ${WORKSPACE_API_KEY_DENIED}`, + description: `Create or update tag definitions, preserving unspecified slots. Updates require \`originalDisplayName\`; other entries create tags. Slot and name conflicts appear in per-definition \`errors\` with HTTP \`200\`, leaving conflicting values unchanged. Use Update Document to set tag values. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'Definitions created and updated by the save.' }, }), @@ -234,7 +234,7 @@ export const knowledgeTagOpenApiRoutes = [ applicationOperation: knowledgeOperations.deleteDocumentTagDefinitions, operationId: 'deleteKnowledgeTagDefinitions', summary: 'Delete Tag Definitions', - description: `Remove tag definitions. \`unused\` defaults to \`true\`, deleting only definitions with no document values, which can be recreated safely. \`unused=false\` deletes every definition and irreversibly clears its slot from all documents and chunks. Use \`DELETE /api/v2/knowledge/{knowledgeBaseId}/tags/{tagId}\` to delete one definition. ${WORKSPACE_API_KEY_DENIED}`, + description: `Delete unused tag definitions by default. With \`unused=false\`, permanently delete all definitions and their values from documents and chunks. Use Delete Tag to remove one definition. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'Number of tag definitions removed.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/knowledge.ts b/apps/sim/lib/api/contracts/v2/openapi/knowledge.ts index 448c8c23389..e9b951dc677 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/knowledge.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/knowledge.ts @@ -115,7 +115,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.list, operationId: 'listKnowledgeBases', summary: 'List Knowledge Bases', - description: `List knowledge bases in a workspace with lifecycle scope, folder filtering, search, sorting, and opaque cursor pagination. \`scope\` defaults to \`active\`; pass \`archived\` to list knowledge bases a \`DELETE\` archived, each carrying the \`deletedAt\` instant it was archived, and recover one with \`POST /api/v2/knowledge/{knowledgeBaseId}/restore\`. ${FOLDER_TREE_TOO_LARGE}`, + description: `List active knowledge bases in a workspace with folder filtering, search, sorting, and cursor pagination. Use \`scope=archived\` to find knowledge bases available for restoration. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'A page of knowledge bases.' }, }), @@ -140,7 +140,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.create, operationId: 'createKnowledgeBase', summary: 'Create Knowledge Base', - description: `Create a knowledge base in a workspace with optional folder placement and chunking configuration. An unknown \`folderPath\` is a \`404\`. ${FOLDER_TREE_TOO_LARGE}`, + description: `Create a knowledge base in a workspace with optional folder placement and chunking configuration. An unknown \`folderPath\` returns \`404\`. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The created knowledge base.' }, }), @@ -173,7 +173,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.read, operationId: 'getKnowledgeBase', summary: 'Get Knowledge Base', - description: `Retrieve a knowledge base by identifier. Inaccessible knowledge bases are reported as not found. ${FOLDER_TREE_TOO_LARGE}`, + description: `Get a knowledge base's metadata and document counts. Inaccessible knowledge bases return \`404\`. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'The requested knowledge base.' }, }), @@ -204,7 +204,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.update, operationId: 'updateKnowledgeBase', summary: 'Update Knowledge Base', - description: `Update a knowledge base name, description, chunking configuration, or folder placement. ${FOLDER_TREE_TOO_LARGE}`, + description: `Update a knowledge base's name, description, chunking configuration, or folder placement. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The updated knowledge base.' }, }), @@ -237,7 +237,8 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.delete, operationId: 'deleteKnowledgeBase', summary: 'Delete Knowledge Base', - description: 'Delete a knowledge base and its documents.', + description: + 'Archive a knowledge base, its documents, and its connectors, pausing synchronization. Use List Knowledge Bases with `scope=archived` to find it and Restore Knowledge Base to recover it.', errors: RESOURCE_ERRORS, success: { description: 'Knowledge base deletion acknowledgement.' }, }), @@ -268,7 +269,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.listConnectors, operationId: 'listKnowledgeConnectors', summary: 'List Knowledge Connectors', - description: `List external sources connected to a knowledge base with opaque cursor pagination. Stored API keys and encrypted secret material are never returned. ${WORKSPACE_API_KEY_DENIED}`, + description: `List external sources connected to a knowledge base with cursor pagination. Stored API keys are never returned. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'A page of knowledge connectors.' }, }), @@ -300,7 +301,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.createConnector, operationId: 'createKnowledgeConnector', summary: 'Create Knowledge Connector', - description: `Validate and connect an external source, then queue its initial synchronization. The apiKey field is write-only and is never returned. ${WORKSPACE_API_KEY_DENIED}`, + description: `Validate and connect an external source, then queue its initial synchronization. The \`apiKey\` field is never returned. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The created connector without secret material.', @@ -344,7 +345,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.readConnector, operationId: 'getKnowledgeConnector', summary: 'Get Knowledge Connector', - description: `Retrieve one connector and its ten most recent synchronization attempts. Stored API keys and encrypted secret material are never returned. ${WORKSPACE_API_KEY_DENIED}`, + description: `Get one connector and its ten most recent synchronization attempts. Stored API keys are never returned. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'The connector and recent synchronization history.', @@ -608,7 +609,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.listTags, operationId: 'listKnowledgeTags', summary: 'List Tags', - description: `List the knowledge base's tag vocabulary: each tag's display name, the slot it is stored in, and its field type. Filters and document reads use display names; document writes address slots. ${FULL_SET_LIST}`, + description: `List the knowledge base's tag definitions with display names, write slots, and field types. Filters and document reads use display names; document writes use slots. ${FULL_SET_LIST}`, errors: RESOURCE_ERRORS, success: { description: 'The knowledge base tag vocabulary.' }, }), @@ -640,7 +641,7 @@ const declaredRoutes = [ operationId: 'listKnowledgeDocuments', summary: 'List Documents', description: - 'List documents in a knowledge base with filename search, state filtering, tag filtering, sorting, and opaque cursor pagination. Tag values are keyed by display name; resolve those to write slots with `GET /api/v2/knowledge/{knowledgeBaseId}/tags`.', + 'List documents with filename search, state and tag filters, sorting, and cursor pagination. Tag values use display names; use List Tags to resolve the slots required for writes.', errors: RESOURCE_ERRORS, success: { description: 'A page of knowledge documents.' }, }), @@ -671,7 +672,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.bulkDocuments, operationId: 'bulkUpdateKnowledgeDocuments', summary: 'Bulk Enable or Disable Documents', - description: `Enable or disable many documents in one request, either by identifier or, with \`selectAll\`, every document in the knowledge base. Bulk delete is not offered; delete documents one at a time with \`DELETE /api/v2/knowledge/{knowledgeBaseId}/documents/{documentId}\`. ${WORKSPACE_API_KEY_DENIED}`, + description: `Enable or disable selected documents, or use \`selectAll\` for the entire knowledge base. Use Delete Document to remove documents individually. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'The number and identifiers of the documents that changed.', @@ -808,7 +809,8 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.uploadCancel, operationId: 'abortKnowledgeDocumentUpload', summary: 'Abort Document Upload', - description: 'Abort an incomplete upload and discard provider-side multipart state.', + description: + 'Abort an incomplete upload session and discard its uploaded data. Completed uploads cannot be aborted.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The aborted upload session.' }, }), @@ -845,7 +847,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.uploadParts, operationId: 'createKnowledgeDocumentUploadPartUrls', summary: 'Create Document Upload Part URLs', - description: 'Issue short-lived signed PUT URLs for up to 100 multipart part numbers.', + description: 'Create short-lived signed PUT URLs for up to 100 multipart part numbers.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'Signed URLs for the requested upload parts.' }, }), @@ -927,7 +929,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.readDocument, operationId: 'getKnowledgeDocument', summary: 'Get Document', - description: 'Retrieve document detail, processing state, and connector provenance.', + description: 'Get document metadata, processing status, and source connector details.', errors: RESOURCE_ERRORS, success: { description: 'The requested knowledge document.' }, }), @@ -958,7 +960,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.updateDocument, operationId: 'updateKnowledgeDocument', summary: 'Update Document', - description: `Rename a document, enable or disable it for search, set any of its 17 tag slots, or requeue it for processing. Absent fields are unchanged, and derived indexing state is read-only. Resolve a tag display name to its slot with \`GET /api/v2/knowledge/{knowledgeBaseId}/tags\`. The returned document omits the connector provenance the detail read carries. ${WORKSPACE_API_KEY_DENIED}`, + description: `Rename a document, change search availability, update tag slots, or requeue processing. Omitted fields remain unchanged; indexing state is read-only. Use List Tags to resolve names to slots and Get Document for source connector details, which this response omits. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'The updated document, or the requeue acknowledgement.', @@ -994,7 +996,7 @@ const declaredRoutes = [ operationId: 'deleteKnowledgeDocument', summary: 'Delete Document', description: - 'Remove one document from a knowledge base. An uploaded document is deleted outright with its indexed chunks. A connector-backed document is instead excluded — its row and embeddings survive, but it stops being searchable and a later sync does not re-add it. Either way it no longer appears in listings or search results.', + 'Remove a document from listings and search. Uploaded documents and their chunks are deleted. Connector documents are excluded while retaining their stored data; later synchronization does not re-add them.', errors: RESOURCE_ERRORS, success: { description: 'Knowledge document deletion acknowledgement.' }, }), @@ -1110,7 +1112,8 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.deleteFolder, operationId: 'deleteKnowledgeFolder', summary: 'Delete Folder', - description: 'Delete a folder, optionally including nested folders and knowledge bases.', + description: + 'Archive an empty folder, or set `recursive=true` to archive its subfolders and knowledge bases. Use Restore Knowledge Base to recover knowledge bases.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'Folder deletion acknowledgement and deleted item counts.', @@ -1137,7 +1140,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.restore, operationId: 'restoreKnowledgeBase', summary: 'Restore Knowledge Base', - description: `Un-archive a soft-deleted knowledge base along with its documents and connectors. Idempotent: a knowledge base that is already active is returned unchanged with no audit entry recorded. Restoring into an archived workspace is a \`409\`, and a knowledge base whose folder is still archived is returned to the workspace root. ${FOLDER_TREE_TOO_LARGE}`, + description: `Restore a knowledge base and the documents and connectors archived with it. Active knowledge bases return unchanged without a new audit event. An archived workspace returns \`409\`; an archived containing folder moves the restored knowledge base to the workspace root. ${FOLDER_TREE_TOO_LARGE}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The restored knowledge base.' }, }), @@ -1170,9 +1173,7 @@ const declaredRoutes = [ applicationOperation: knowledgeOperations.addWorkspaceFiles, operationId: 'addWorkspaceFilesToKnowledgeBase', summary: 'Index Workspace Files', - description: - 'Index stored workspace files without re-uploading bytes. Each reference is authorized independently; unreadable, unsupported, or over-100 MB files appear in `failed` while valid files are queued. This partial outcome returns `200`, not multi-status. Queued documents begin as `pending`; the response carries identities only, so read each document endpoint for current processing state. ' + - WORKSPACE_API_KEY_DENIED, + description: `Queue stored workspace files for indexing without re-uploading bytes. Unreadable, unsupported, or over-100 MB files appear in \`failed\`; valid files are queued. Partial success returns \`200\`. Use Get Document to poll processing after receiving document IDs. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'UsageLimitExceeded'], success: { description: 'Files queued for indexing, with any that could not be.', diff --git a/apps/sim/lib/api/contracts/v2/openapi/logs.ts b/apps/sim/lib/api/contracts/v2/openapi/logs.ts index 6774a4487aa..be8383b036b 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/logs.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/logs.ts @@ -191,7 +191,7 @@ const declaredRoutes = [ applicationOperation: logOperations.readDetail, operationId: 'getLog', summary: 'Get Log', - description: `Retrieve a run's workflow snapshot, trace spans, final output, and cost. Trace spans have separate retention, so an empty \`traceSpans\` array does not prove none were recorded. ${RUN_RETENTION} ${FOLDER_TREE_TOO_LARGE}`, + description: `Get a run's workflow graph, trace spans, final output, and cost. Trace spans expire separately, so an empty \`traceSpans\` array does not prove none were recorded. ${RUN_RETENTION} ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'The requested diagnostic log representation.' }, }), @@ -218,7 +218,7 @@ const declaredRoutes = [ applicationOperation: logOperations.readStats, operationId: 'getLogStats', summary: 'Get Log Statistics', - description: `Return workspace/workflow counts, success, errors, and latency. Defaults span runs, or 24 hours if empty; supplied bounds stay exact. Buckets are one-minute minimum and may pass the end. Folders include descendants; \`workflowsTruncated\` marks capped series, totals include all. ${RUN_RETENTION} ${FOLDER_TREE_TOO_LARGE}`, + description: `Get run counts, success and error counts, and latency by workspace or workflow. Default bounds span recorded runs, or the last 24 hours when empty. Buckets may extend past the end. Folder filters include descendants; \`workflowsTruncated\` affects series, not totals. ${RUN_RETENTION} ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'Bucketed execution statistics for the workspace.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/resources.ts b/apps/sim/lib/api/contracts/v2/openapi/resources.ts index 826b55bcb50..4f9a3acbdae 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/resources.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/resources.ts @@ -579,8 +579,7 @@ const declaredRoutes = [ applicationOperation: workspaceOperations.readPublicDetail, operationId: 'getWorkspace', summary: 'Get Workspace', - description: - 'Return public metadata for one accessible workspace. Governance identities, billing identities, and internal membership identifiers are intentionally omitted.', + description: 'Get metadata for an accessible workspace.', errors: RESOURCE_ERRORS, success: { description: 'Public workspace metadata.' }, }), @@ -608,7 +607,7 @@ const declaredRoutes = [ operationId: 'listWorkspaceMembers', summary: 'List Workspace Members', description: - "List the workspace's effective members ordered by email. Explicit workspace grants and inherited organization-administrator grants are merged; internal membership and billing identities are omitted.", + 'List workspace members by email, including explicit grants and inherited organization admin access.', errors: RESOURCE_ERRORS, success: { description: 'An email-ordered page of effective workspace members.' }, }), @@ -641,7 +640,7 @@ const declaredRoutes = [ operationId: 'listMcpServers', summary: 'List MCP Servers', description: - 'List MCP servers registered in a workspace. Request-header values and OAuth client secrets are never returned. The discovery fields stay at their registration defaults until `GET /api/v2/mcp-servers/{mcpServerId}/tools` runs a discovery.', + 'List MCP servers registered in a workspace, excluding request-header values and OAuth secrets. Connection metadata remains at registration defaults until List MCP Server Tools performs discovery.', errors: RESOURCE_ERRORS, success: { description: 'MCP servers registered in the workspace.' }, }), @@ -668,7 +667,7 @@ const declaredRoutes = [ operationId: 'createMcpServer', summary: 'Create MCP Server', description: - 'Register an MCP server in a workspace. The endpoint URL is the server identity, so a URL already registered here is a `409` — reconfigure that server with `PATCH /api/v2/mcp-servers/{mcpServerId}` instead. Registration never connects to the endpoint: the server comes back `disconnected` and stays unavailable until `GET /api/v2/mcp-servers/{mcpServerId}/tools` succeeds.', + 'Register an external MCP server without connecting to it. A duplicate URL returns `409`; use Update MCP Server to change the existing registration. The server remains disconnected until List MCP Server Tools succeeds.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The MCP server was registered.' }, }), @@ -705,7 +704,7 @@ const declaredRoutes = [ operationId: 'getMcpServer', summary: 'Get MCP Server', description: - 'Fetch one MCP server by identifier. Request-header values and OAuth client secrets are never returned.', + 'Get one MCP server by identifier. Request-header values and OAuth client secrets are never returned.', errors: RESOURCE_ERRORS, success: { description: 'The MCP server.' }, }), @@ -738,7 +737,7 @@ const declaredRoutes = [ operationId: 'updateMcpServer', summary: 'Update MCP Server', description: - 'Update the supplied MCP server fields. Omitted fields are retained, except where a field says otherwise. Any change that invalidates authentication revokes the stored OAuth grant, resets `connectionStatus` to `disconnected`, and clears `lastConnected` and `lastError`, so the server must be rediscovered.', + "Update an MCP server's supplied fields. Omitted fields remain unchanged unless the field specifies otherwise. Authentication changes revoke the stored OAuth grant and reset connection metadata. Use List MCP Server Tools to reconnect.", errors: RESOURCE_ERRORS, success: { description: 'The updated MCP server.' }, }), @@ -805,7 +804,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.discoverTools, operationId: 'listMcpServerTools', summary: 'List MCP Server Tools', - description: `Return up to 1,000 tools and 5 MB with \`nextCursor: null\`, opening a connection and updating connection metadata. ${HEAD_MIRRORS_GET} Unavailable servers return \`503\`; invalid OAuth returns \`409\` with \`error.details.code: MCP_SERVER_REAUTHORIZATION_REQUIRED\` and requires human reauthorization. ${WORKSPACE_API_KEY_DENIED}`, + description: `Discover up to 1,000 tools within 5 MB, connect to the server, and update connection metadata. Results are unpaginated. Invalid OAuth returns \`409\` with \`MCP_SERVER_REAUTHORIZATION_REQUIRED\`; reauthorize through the browser. Unavailable servers return \`503\`. ${HEAD_MIRRORS_GET} ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'Tools exposed by the MCP server.' }, }), @@ -838,7 +837,7 @@ const declaredRoutes = [ operationId: 'listSkills', summary: 'List Skills', description: - 'List workspace and built-in skills with opaque cursor pagination. Built-ins are marked read-only. The list omits skill bodies; fetch one skill to read its content.', + 'List workspace and built-in skills with cursor pagination. Built-in skills are read-only. The list omits skill bodies; use Get Skill to read content.', errors: RESOURCE_ERRORS, success: { description: 'Skills available in the workspace.' }, }), @@ -900,7 +899,7 @@ const declaredRoutes = [ operationId: 'getSkill', summary: 'Get Skill', description: - 'Fetch one workspace or built-in skill, including its full content. Built-in skills are marked read-only.', + 'Get one workspace or built-in skill, including its full content. Built-in skills are marked read-only.', errors: RESOURCE_ERRORS, success: { description: 'The skill.' }, }), @@ -932,7 +931,7 @@ const declaredRoutes = [ applicationOperation: skillOperations.update, operationId: 'updateSkill', summary: 'Update Skill', - description: `Update the supplied fields on a workspace skill. Omitted fields retain their stored values. Built-in skills are read-only. ${WORKSPACE_API_KEY_DENIED}`, + description: `Update a workspace skill. Omitted fields remain unchanged. Built-in skills are read-only. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The updated skill.' }, }), @@ -998,8 +997,7 @@ const declaredRoutes = [ applicationOperation: skillOperations.listEditors, operationId: 'listSkillEditors', summary: 'List Skill Editors', - description: - 'List explicit skill editors and workspace administrators with opaque cursor pagination. Internal user and membership identifiers are never returned.', + description: 'List skill editors and workspace administrators with cursor pagination.', errors: RESOURCE_ERRORS, success: { description: 'Users who can edit the skill.' }, }), @@ -1031,7 +1029,7 @@ const declaredRoutes = [ applicationOperation: skillOperations.grantEditor, operationId: 'grantSkillEditor', summary: 'Grant Skill Editor', - description: `Grant editor access to a current workspace member by email. The caller must already be a skill editor or workspace administrator. Workspace administrators already have derived editor access and cannot receive an explicit grant. A retried existing grant returns 200; a newly created grant returns 201. ${WORKSPACE_API_KEY_DENIED}`, + description: `Grant skill editor access to a workspace member by email. Requires an existing editor or workspace admin; admins already have access and cannot receive explicit grants. Existing grants return \`200\`; new grants return \`201\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { byStatus: { @@ -1102,8 +1100,7 @@ const declaredRoutes = [ applicationOperation: customToolOperations.list, operationId: 'listCustomTools', summary: 'List Custom Tools', - description: - 'List code-backed custom tools defined in a workspace, with opaque cursor pagination. Legacy personal tools are excluded.', + description: 'List code-backed custom tools in a workspace with cursor pagination.', errors: RESOURCE_ERRORS, success: { description: 'Custom tools defined in the workspace.' }, }), @@ -1165,7 +1162,7 @@ const declaredRoutes = [ applicationOperation: customToolOperations.read, operationId: 'getCustomTool', summary: 'Get Custom Tool', - description: 'Fetch one custom tool by identifier, scoped to its workspace.', + description: 'Get one custom tool by identifier, scoped to its workspace.', errors: RESOURCE_ERRORS, success: { description: 'The custom tool.' }, }), @@ -1198,7 +1195,7 @@ const declaredRoutes = [ operationId: 'updateCustomTool', summary: 'Update Custom Tool', description: - 'Update the supplied custom tool fields. Omitted fields retain their stored values, and titles must remain unique within the workspace.', + 'Update a custom tool. Omitted fields remain unchanged; titles must remain unique within the workspace.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The updated custom tool.' }, }), @@ -1266,7 +1263,7 @@ const declaredRoutes = [ operationId: 'listSandboxes', summary: 'List Sandboxes', description: - 'List the sandboxes defined in a workspace, with opaque cursor pagination. A sandbox is a reusable dependency set — npm or PyPI packages, pinned managed CLIs, and Debian packages — that Function blocks execute against. Listing is not plan-gated, so a workspace that dropped below the Max tier still sees what it built.', + 'List reusable dependency environments for Function blocks, including language packages, managed CLIs, and system packages. Sandboxes remain visible after a plan downgrade.', errors: RESOURCE_ERRORS, success: { description: 'Sandboxes defined in the workspace.' }, }), @@ -1292,7 +1289,7 @@ const declaredRoutes = [ applicationOperation: sandboxOperations.create, operationId: 'createSandbox', summary: 'Create Sandbox', - description: `Create a uniquely named sandbox. Prebuild deployments schedule an image build reported by \`buildStatus\`; runtime-install deployments or empty specs report \`buildStatus: null\`. Invalid dependency or system-package entries return \`400\` with field details. ${SANDBOX_ADMIN_PLAN_NOTE} ${SANDBOX_BUILD_BUDGET_NOTE} ${WORKSPACE_API_KEY_DENIED}`, + description: `Create a uniquely named dependency environment. If a build is needed, track readiness with \`buildStatus\`; null means no build is required. Invalid dependencies return \`400\` with field details. Requires workspace admin access on Max or Enterprise. Creates and updates share a rate limit; respect \`Retry-After\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: @@ -1332,7 +1329,7 @@ const declaredRoutes = [ operationId: 'getSandbox', summary: 'Get Sandbox', description: - 'Fetch one sandbox by identifier, scoped to its workspace, including its current build state and any build failure.', + 'Get one sandbox by identifier, scoped to its workspace, including its current build state and any build failure.', errors: RESOURCE_ERRORS, success: { description: 'The sandbox.' }, }), @@ -1364,7 +1361,7 @@ const declaredRoutes = [ applicationOperation: sandboxOperations.update, operationId: 'updateSandbox', summary: 'Update Sandbox', - description: `Update supplied fields; omissions preserve values, lists replace whole lists, and names remain unique. Prebuild deployments rebuild changed specs, while resending an unchanged failed spec retries it; runtime-install or empty specs report \`buildStatus: null\`. ${SANDBOX_ADMIN_PLAN_NOTE} ${SANDBOX_BUILD_BUDGET_NOTE} ${WORKSPACE_API_KEY_DENIED}`, + description: `Update a sandbox, preserving omitted fields and replacing supplied lists. Dependency changes may start a build; resending a failed specification retries its build. \`buildStatus: null\` means no build is required. Requires workspace admin access on Max or Enterprise. Creates and updates share a rate limit; respect \`Retry-After\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The updated sandbox.' }, }), @@ -1407,7 +1404,7 @@ const declaredRoutes = [ applicationOperation: sandboxOperations.delete, operationId: 'deleteSandbox', summary: 'Delete Sandbox', - description: `Delete a sandbox. Function blocks still selecting it fail closed until reconfigured. A prebuilt image is released when no sandbox shares it; runtime-install and empty specs have no image to release. ${SANDBOX_ADMIN_PLAN_NOTE} ${WORKSPACE_API_KEY_DENIED}`, + description: `Delete a sandbox. Function blocks using it fail until reconfigured. Requires workspace admin access on Max or Enterprise. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'The sandbox was deleted.' }, }), @@ -1466,7 +1463,7 @@ const declaredRoutes = [ applicationOperation: credentialOperations.listProviders, operationId: 'listCredentialProviders', summary: 'List Credential Providers', - description: `List catalogued OAuth and service-account connection methods and whether each is available to the caller in this workspace and deployment. Optionally search provider names with a case-insensitive substring match. OAuth authorization options contain the exact provider IDs accepted by the browser connection endpoint; service-account methods list the exact create-body fields and mark secret fields write-only. ${FULL_SET_LIST}`, + description: `List OAuth and service-account connection methods and their availability. OAuth options provide provider IDs for browser connections; service-account methods declare required fields and write-only secrets. Supports provider-name search. ${FULL_SET_LIST}`, errors: RESOURCE_ERRORS, success: { description: 'Credential provider catalog with caller-specific availability.' }, }), @@ -1497,7 +1494,7 @@ const declaredRoutes = [ applicationOperation: credentialOperations.createServiceAccount, operationId: 'createServiceAccountCredential', summary: 'Create Service-Account Credential', - description: `Verify and store one service-account credential. Use provider discovery to select a service-account provider, then encode its required fields as the JSON object string in credentials. The credentials string is write-only and is never returned. A retried source match returns the existing credential with 200; a newly created credential returns 201. ${WORKSPACE_API_KEY_DENIED}`, + description: `Verify and store a service-account credential using the fields from List Credential Providers, encoded as a JSON object string in \`credentials\`. Secrets are never returned. A matching source returns the existing credential with \`200\`; creation returns \`201\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { byStatus: { @@ -1598,7 +1595,7 @@ const declaredRoutes = [ applicationOperation: secretOperations.list, operationId: 'listSecrets', summary: 'List Secrets', - description: `List workspace and caller-owned personal secret metadata with opaque cursor pagination. Rows for workspace secrets marked visible (unredacted) include the stored value; every other row is metadata-only and no other response ever carries a value. ${WORKSPACE_API_KEY_DENIED}`, + description: `List workspace and caller-owned personal secrets with cursor pagination. Only workspace secrets marked \`unredacted\` include values; all other entries contain metadata only. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'Secret metadata visible to the caller.' }, }), @@ -1624,7 +1621,7 @@ const declaredRoutes = [ applicationOperation: secretOperations.set, operationId: 'setSecret', summary: 'Set Secret', - description: `Create or replace a workspace or personal secret. Values are encrypted at rest, write-only, and never returned. For an existing workspace secret, omit \`value\` to update only \`description\` or \`unredacted\`; the value remains untouched. This metadata-only form cannot create a secret and returns \`404\` when absent. Personal secrets always require \`value\`. ${WORKSPACE_API_KEY_DENIED}`, + description: `Create or replace a workspace or personal secret without returning its value. For existing workspace secrets, omit \`value\` to update metadata only; this returns \`404\` if absent. Personal secrets always require \`value\`. List Secrets can reveal workspace values marked \`unredacted\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { byStatus: { @@ -1718,7 +1715,7 @@ const declaredRoutes = [ operationId: 'getApiMeta', summary: 'Get API Capabilities', description: - 'Report whether v2 is available, what kind of API credential is calling, and when it expires. Requires a valid API key or OAuth access token.', + 'Get whether v2 is available, what kind of API credential is calling, and when it expires. Requires a valid API key or OAuth access token.', errors: META_ERRORS, success: { description: 'Availability and lifecycle facts about the calling credential.' }, }), @@ -1739,7 +1736,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.listWorkflowDeployments, operationId: 'listWorkflowMcpServers', summary: 'List Workflow MCP Servers', - description: `List servers that publish deployed workflows to outside MCP clients; \`GET /api/v2/mcp-servers\` instead lists external servers Sim calls. Entries include client endpoints and tool names. A page shares a 2,000-name budget, so trailing servers may show partial inventories; read a server's tools endpoint for its full set. ${WORKSPACE_API_KEY_DENIED}`, + description: `List MCP servers that expose deployed workflows to external clients. Use List MCP Servers for external servers Sim calls. Tool names share a 2,000-name page limit; inspect \`toolNamesTruncated\` and use List Workflow MCP Tools for a server's inventory. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'A page of published MCP servers.' }, }), @@ -1760,7 +1757,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.createWorkflowDeploymentServer, operationId: 'createWorkflowMcpServer', summary: 'Create Workflow MCP Server', - description: `Publish a new MCP server for a workspace, optionally seeding it with workflows to expose as tools. Every workflow named in \`workflowIds\` must already be deployed. Setting \`isPublic\` lets any MCP client holding the server URL execute the workflows it publishes without a Sim API key. ${WORKSPACE_API_KEY_DENIED}`, + description: `Create an MCP server that exposes deployed workflows as tools. Every supplied workflow must already be deployed. With \`isPublic: true\`, anyone with the server URL can execute its workflows without a Sim API key. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The published MCP server.' }, }), @@ -1782,7 +1779,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.readWorkflowDeploymentServer, operationId: 'getWorkflowMcpServer', summary: 'Get Workflow MCP Server', - description: `Read one published MCP server. The list is the only other place this state is published, so a caller holding a server id would otherwise have to page the collection and filter client-side. The tools it publishes are on its \`tools\` sub-resource. ${WORKSPACE_API_KEY_DENIED}`, + description: `Get a published workflow MCP server's metadata and client endpoint. Use List Workflow MCP Tools for its tool inventory. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'The MCP server.' }, }), @@ -1804,7 +1801,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.listWorkflowDeploymentTools, operationId: 'listWorkflowMcpTools', summary: 'List Workflow MCP Tools', - description: `Every tool a server publishes, tool-name ordered. The server list reports tool *names* only, so this is where a caller reads the \`workflowId\` that \`DELETE /api/v2/workflow-mcp-servers/{serverId}/tools/{workflowId}\` addresses. Returned in one page rather than paged — so \`nextCursor\` is always null — and capped at 2,000 tools, which is far above any real server's inventory. ${WORKSPACE_API_KEY_DENIED}`, + description: `List a server's published tools by name, including workflow IDs used to unpublish them. Returns up to 2,000 tools with \`nextCursor: null\`; \`truncated\` indicates an incomplete inventory that cannot be paginated. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: { description: 'The tools this server publishes.' }, }), @@ -1832,7 +1829,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.updateWorkflowDeploymentServer, operationId: 'updateWorkflowMcpServer', summary: 'Update Workflow MCP Server', - description: `Rename, re-describe, or change the public visibility of a published MCP server. Merge-patch shaped: an omitted key is unchanged and \`description: null\` clears the description. Publishing and unpublishing the workflows it serves are separate operations on its \`tools\` sub-resource. ${WORKSPACE_API_KEY_DENIED}`, + description: `Update a workflow MCP server's name, description, or public access. Omitted fields remain unchanged; \`description: null\` clears the description. Publish or unpublish tools separately. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The updated MCP server.' }, }), @@ -1855,7 +1852,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.deleteWorkflowDeploymentServer, operationId: 'deleteWorkflowMcpServer', summary: 'Delete Workflow MCP Server', - description: `Unpublish an MCP server. Every tool it served stops answering and connected clients lose the endpoint. The workflows themselves are untouched — their own deployments stay live and executable through the workflow API. ${WORKSPACE_API_KEY_DENIED}`, + description: `Delete a workflow MCP server and stop serving its tools. The underlying workflows remain deployed and executable through the workflow API. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The MCP server was unpublished.' }, }), @@ -1877,7 +1874,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.deployWorkflowTool, operationId: 'deployWorkflowMcpTool', summary: 'Publish Workflow As MCP Tool', - description: `Publish a deployed workflow as a tool on an MCP server. The tool's input schema is generated from the deployed workflow's input format, so the workflow must already be deployed. Idempotent per workflow: a server carries at most one tool per workflow, so a repeat call replaces the existing tool and answers \`200\` with \`updated: true\` rather than conflicting. ${WORKSPACE_API_KEY_DENIED}`, + description: `Publish a deployed workflow as an MCP tool using its deployed input schema. Each server has at most one tool per workflow; repeating the call replaces that tool and returns \`200\` with \`updated: true\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The published tool.' }, }), @@ -1900,7 +1897,7 @@ const declaredRoutes = [ applicationOperation: mcpServerOperations.undeployWorkflowTool, operationId: 'undeployWorkflowMcpTool', summary: 'Unpublish Workflow MCP Tool', - description: `Remove a workflow from an MCP server. Addressed by workflow rather than by tool identifier, because a server carries at most one live tool per workflow. The workflow's own deployment is untouched. ${WORKSPACE_API_KEY_DENIED}`, + description: `Unpublish an MCP tool by its workflow ID. The workflow's API deployment remains active. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The tool was removed.' }, }), @@ -1931,7 +1928,7 @@ const declaredRoutes = [ applicationOperation: credentialOperations.update, operationId: 'updateCredential', summary: 'Update Credential', - description: `Rename a service-account credential or rotate its write-only secret fields. Omissions preserve values; \`description: null\` clears the description. Secret fields sent for another credential type return \`400\`. The provider verifies replacements before storage: rejection leaves the old secret intact and returns \`400\` with \`providerErrorCode\`; provider outages return \`503\`. The preserved credential ID keeps all references working. Credential admin access is required. ${WORKSPACE_API_KEY_DENIED}`, + description: `Rename a service-account credential or rotate its secret fields, preserving omitted values and the credential ID. Requires credential admin access. Provider rejection preserves the old secret and returns \`400\` with \`providerErrorCode\`; outages return \`503\`. Fields for a different credential type return \`400\`. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The updated credential without secret material.' }, }), @@ -1971,7 +1968,7 @@ const declaredRoutes = [ operationId: 'listBlocks', summary: 'List Blocks', description: - 'List the blocks available in a workspace, built-in and workspace-deployed alike, discriminated by `source`. Availability is caller-specific: the workspace’s integration allowlist, the organization’s revealed preview blocks, and the deployment’s allowlist all narrow the result. Use `capability=trigger` for the blocks that can start a workflow. Summaries name their tools and operations by id — resolve one with Get Block or Get Tool.', + 'List built-in and workspace-deployed blocks visible to the caller. Integration allowlists and preview visibility restrict results. Use `capability=trigger` for workflow starters and Get Block or Get Tool to resolve operation and tool IDs.', errors: RESOURCE_ERRORS, success: { description: 'A page of blocks available in the workspace.' }, }), @@ -1998,7 +1995,7 @@ const declaredRoutes = [ operationId: 'getBlock', summary: 'Get Block', description: - 'Read one block’s full configuration shape: its fields and their conditions, its operations with the tool each runs, every tool’s parameters and outputs, and its triggers. An unversioned base type resolves to the newest version this caller can see — `confluence` answers with `confluence_v2` — and the returned `id` is always the resolved one, matching Get Tool. A block this caller cannot see answers 404, identically to one that does not exist.', + "Get a block's fields, conditions, operations, tool schemas, and triggers. Unversioned types resolve to the newest visible version; the returned `id` identifies that version. Hidden or missing blocks return `404`.", errors: RESOURCE_ERRORS, success: { description: 'The block.' }, }), @@ -2031,7 +2028,7 @@ const declaredRoutes = [ operationId: 'listTools', summary: 'List Tools', description: - 'List the built-in tools available in a workspace. Built-in tools only: a workspace’s MCP tools are discovered per server on List MCP Server Tools, and its code-backed custom tools are on List Custom Tools. A tool is available when a block the caller can see exposes it, so the same allowlist and visibility rules as List Blocks apply.', + "List built-in tools exposed by blocks visible to the caller. Use List MCP Server Tools for an external server's tools and List Custom Tools for workspace code-backed tools.", errors: RESOURCE_ERRORS, success: { description: 'A page of built-in tools available in the workspace.' }, }), @@ -2058,7 +2055,7 @@ const declaredRoutes = [ operationId: 'getTool', summary: 'Get Tool', description: - 'Read one built-in tool’s declared parameters and outputs. A name that is itself a registered id answers as that exact tool; a name that is not resolves to the newest version of its family. The returned `id` is always the one that answered, so a caller can see which version it got. A tool the workspace’s visible blocks do not expose answers `404`, identically to one that does not exist.', + "Get a built-in tool's parameters and outputs. Registered IDs resolve exactly; other names resolve to the newest family version. The returned `id` identifies the resolved tool. Hidden or missing tools return `404`.", errors: RESOURCE_ERRORS, success: { description: 'The tool.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/shared.ts b/apps/sim/lib/api/contracts/v2/openapi/shared.ts index acee16e5fa2..8e6bfa1ea24 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/shared.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/shared.ts @@ -345,7 +345,7 @@ export const V2_AUTH_SECURITY_SCHEMES = { * rendering one back do not need this sentence: the shared `413` response * description already covers them. */ -export const FOLDER_TREE_TOO_LARGE = 'A workspace folder tree over 10,000 folders is a `413`.' +export const FOLDER_TREE_TOO_LARGE = 'Workspace folder trees exceeding 10,000 folders return `413`.' /** * Appended to a list whose result set is bounded by construction, so it answers @@ -357,7 +357,7 @@ export const FOLDER_TREE_TOO_LARGE = 'A workspace folder tree over 10,000 folder * promise. The authoritative membership is pinned in * `contracts/v2/__tests__/list-pagination.test.ts` as `FULL_SET_LISTS`. */ -export const FULL_SET_LIST = 'The bounded set is returned in one page; `nextCursor` is always null.' +export const FULL_SET_LIST = 'Returns the complete set in one page; `nextCursor` is always null.' /** * Appended to a `GET` whose route declares `headSafe: false` because the read @@ -366,7 +366,7 @@ export const FULL_SET_LIST = 'The bounded set is returned in one page; `nextCurs * Pinned by `contracts/v2/openapi/head-not-safe.test.ts`. */ export const HEAD_MIRRORS_GET = - '`HEAD` skips the effect but uses `GET` authorization, returning `400`, `401`, `403`, or `404`, or an empty `200` with no payload headers; confirms access only.' + '`HEAD` checks access with the same authorization as `GET` but skips side effects, returning an empty `200` without payload headers on success.' /** * Appended where the skipped payload headers are the ones a caller is most @@ -386,7 +386,7 @@ export const HEAD_OMITS_PAYLOAD_HEADERS = * so it is not something a workspace owner can grant around. */ export const WORKSPACE_API_KEY_DENIED = - 'Workspace API keys return `403`; use a personal API key or appropriately scoped OAuth token.' + 'Workspace API keys return `403`; use a personal API key or scoped OAuth token.' /** * {@link WORKSPACE_API_KEY_DENIED} for an operation behind the resource-concealment @@ -424,7 +424,7 @@ export const WORKSPACE_API_KEY_DENIED_AS_NOT_FOUND = * cannot drift into two paraphrases of one window. */ export const RUN_RETENTION = - 'Expired runs are hard-deleted. Retention is 30 days from run start on Free, unbounded on Pro and Team, and configured per organization on Enterprise with an optional workspace override.' + 'Expired runs are permanently deleted. Retention is 30 days from run start on Free, unlimited on Pro and Team, and configured per organization on Enterprise with workspace overrides.' /** * Response headers a binary download declares on top of the common set. Shared diff --git a/apps/sim/lib/api/contracts/v2/openapi/tables.ts b/apps/sim/lib/api/contracts/v2/openapi/tables.ts index 7dbc5c4e0e6..0cfd05295df 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/tables.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/tables.ts @@ -141,7 +141,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.list, operationId: 'listTables', summary: 'List Tables', - description: `List tables in a workspace with optional folder filtering, search, sorting, and an opaque cursor envelope. \`scope=archived\` lists tables a \`DELETE\` archived, which \`POST /api/v2/tables/{tableId}/restore\` can bring back. ${FOLDER_TREE_TOO_LARGE}`, + description: `List active tables with folder filtering, search, sorting, and cursor pagination. Use \`scope=archived\` to find tables available for restoration. ${FOLDER_TREE_TOO_LARGE}`, errors: [...WORKSPACE_ERRORS, 'NotFound', 'PayloadTooLarge'], success: { description: 'A page of tables in the workspace.' }, }), @@ -205,7 +205,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.read, operationId: 'getTable', summary: 'Get Table', - description: `Retrieve a table with its metadata, column schema, locks, and current job. ${FOLDER_TREE_TOO_LARGE}`, + description: `Get a table with its metadata, column schema, locks, and current job. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'The requested table.' }, }), @@ -237,7 +237,7 @@ const declaredRoutes = [ operationId: 'deleteTable', summary: 'Delete Table', description: - 'Archive a table and return an explicit deletion acknowledgement. The table is soft-deleted, not erased: its rows are retained and `POST /api/v2/tables/{tableId}/restore` brings it back.', + 'Archive a table while retaining its rows. Use List Tables with `scope=archived` to find it and Restore Table to recover it.', errors: TABLE_MUTATION_ERRORS, success: { description: 'Table deletion acknowledgement.' }, }), @@ -268,7 +268,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.update, operationId: 'updateTable', summary: 'Update Table', - description: `Rename a table, edit its description, or move it to a canonical folder. At least one mutable field is required; lock flags are read-only.\n\nNOT atomic: fields are written independently, so a 4xx may follow a partial update. When fields were applied, \`details.applied\` names them; retry only the missing fields. If it is absent, nothing changed.\n\n${FOLDER_TREE_TOO_LARGE}`, + description: `Rename a table, edit its description, or move it to a folder. Fields are saved independently: a failed request may leave partial changes. \`error.details.applied\` lists saved fields; retry only the remaining fields. If absent, nothing changed. Lock flags are read-only. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The updated table.' }, }), @@ -401,7 +401,7 @@ const declaredRoutes = [ operationId: 'listTableRows', summary: 'List Rows', description: - "List a plain cursor page in default row order. Pages are capped at 5MB by default and may contain fewer rows than the requested limit; continue until nextCursor is null. Use the query endpoint for predicate filtering and sorting. Set `includeRunState=true` to attach each row's per-workflow-group run outcomes; the row limit is capped when it is set.", + 'List rows in default order with cursor pagination. Pages default to a 5 MB limit and may contain fewer rows than requested; continue until `nextCursor` is null. Use Query Rows for filtering and sorting. `includeRunState=true` adds per-group run outcomes and reduces the row limit.', errors: RESOURCE_ERRORS, success: { description: 'A page of table rows.' }, }), @@ -540,7 +540,7 @@ const declaredRoutes = [ operationId: 'getTableRow', summary: 'Get Row', description: - "Retrieve one row by identifier. Set `includeRunState=true` to attach the row's per-workflow-group run outcomes.", + "Get one row by identifier. Set `includeRunState=true` to attach the row's per-workflow-group run outcomes.", errors: RESOURCE_ERRORS, success: { description: 'The requested table row.' }, }), @@ -636,7 +636,7 @@ const declaredRoutes = [ operationId: 'upsertTableRow', summary: 'Upsert Row', description: - 'Insert a row or update the existing row that conflicts on a selected unique column.\n\nWARNING — the update branch REPLACES the row, it does not merge. `data` is the complete new row value, so every column you omit is cleared on the matched row. Send the full row here, or use `PATCH /api/v2/tables/{tableId}/rows/{rowId}` to change a subset.', + 'Insert a row or replace the row matching a selected unique column. On replacement, omitted columns are cleared; send the complete row. Use Update Row for a partial patch.', errors: TABLE_MUTATION_ERRORS, success: { description: 'The upserted row and operation performed.' }, }), @@ -676,7 +676,7 @@ const declaredRoutes = [ operationId: 'queryTableRows', summary: 'Query Rows', description: - 'Query rows using an optional typed condition or `all`/`any` group, ordered sorting, and opaque cursor pagination. Omit the predicate to match all rows. Pages default to a 5 MB cap and may return fewer rows than requested; continue until `nextCursor` is null. Oversized predicates return `413`. `includeRunState` adds per-group outcomes and lowers the row cap. Counts come from a separate snapshot at `POST /query/count`; take the count first and treat it as a floor.', + 'Query rows with typed predicates, sorting, and cursor pagination. Omit the predicate to match all rows. Pages default to a 5 MB limit; continue until `nextCursor` is null. Oversized predicates return `413`. `includeRunState` adds per-group outcomes and reduces the row limit. Counts are read separately and can differ from paged results if rows change.', errors: TABLE_QUERY_ERRORS, success: { description: 'A page of matching table rows.' }, }), @@ -730,7 +730,7 @@ const declaredRoutes = [ operationId: 'countTableRows', summary: 'Count Rows', description: - 'Count the rows matching a typed predicate across the entire table. A predicate may be one condition or an `all`/`any` group. The paged reads carry no total, and `rowCount` on the table resource counts every row rather than the matches. Omit the predicate to count the whole table. A predicate larger than the request-body ceiling is a `413`.', + 'Count rows matching a typed predicate, or omit the predicate to count all rows. The count is read separately from row pages and can change between requests. Oversized predicates return `413`.', errors: TABLE_QUERY_ERRORS, success: { description: 'The number of matching table rows.' }, }), @@ -768,7 +768,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.listViews, operationId: 'listTableViews', summary: 'List Views', - description: `List the bounded set of saved table views, with references to removed columns pruned on read. ${FULL_SET_LIST}`, + description: `List saved table views, omitting references to removed columns. ${FULL_SET_LIST}`, errors: RESOURCE_ERRORS, success: { description: 'The saved table views.' }, }), @@ -841,7 +841,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.readView, operationId: 'getTableView', summary: 'Get View', - description: 'Retrieve one saved table view by identifier.', + description: 'Get one saved table view by identifier.', errors: RESOURCE_ERRORS, success: { description: 'The requested table view.' }, }), @@ -1081,7 +1081,7 @@ const declaredRoutes = [ operationId: 'createTableDispatch', summary: 'Create Run Dispatch', description: - 'Asynchronously run workflow or enrichment groups across all rows or a selected row subset. Poll the returned `dispatchId` with `GET /api/v2/tables/{tableId}/dispatches/{dispatchId}` until its status is `complete` or `canceled`, and cancel it with `DELETE` on the same path. A `null` `dispatchId` means the run settled inline and there is nothing to poll.', + 'Start workflow or enrichment groups across all rows or selected rows. Poll Get Run Dispatch until `complete` or `canceled`. A null `dispatchId` means no dispatch is available to poll; check row outcomes with `includeRunState`. Use Cancel Run Dispatch to stop further scheduling.', errors: RESOURCE_ERRORS, success: { description: 'The accepted run dispatch.' }, }), @@ -1115,7 +1115,7 @@ const declaredRoutes = [ operationId: 'runRowEnrichment', summary: 'Run Enrichment For One Row', description: - 'Asynchronously run one workflow or enrichment group for one table row. Poll the returned `dispatchId` with `GET /api/v2/tables/{tableId}/dispatches/{dispatchId}`; a `null` `dispatchId` means the cell already settled inline.', + 'Start one workflow or enrichment group for a table row. Poll Get Run Dispatch using the returned `dispatchId`. A null `dispatchId` means no dispatch is available to poll; check row outcomes with `includeRunState`.', errors: RESOURCE_ERRORS, success: { description: 'The accepted row enrichment dispatch.' }, }), @@ -1148,7 +1148,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.searchRows, operationId: 'searchTableRows', summary: 'Search Rows', - description: `Search every cell case-insensitively for substring \`q\`, optionally within a predicate-filtered, sorted view. This is text search; \`POST /query\` performs structured predicate reads. Results are cell coordinates \`{ ordinal, rowId, column }\`, never row data; \`ordinal\` indexes the same view paged by \`POST /query\`. Results are uncursored and capped at ${TABLE_LIMITS.MAX_FIND_MATCHES}; \`truncated\` signals more matches. Narrow \`q\` or the predicate instead of paging.`, + description: `Search cell text for a case-insensitive substring within an optional filtered and sorted view. Returns cell coordinates, not row data; \`ordinal\` matches the view used by Query Rows. Results are unpaginated and capped at ${TABLE_LIMITS.MAX_FIND_MATCHES}. If \`truncated\` is true, narrow the search or predicate.`, errors: RESOURCE_ERRORS, success: { description: 'The matching table cells.' }, }), @@ -1188,7 +1188,7 @@ const declaredRoutes = [ operationId: 'createTableImport', summary: 'Create Table Import', description: - 'Create a durable CSV import. Upload sources receive signed transfer instructions; workspace-file sources begin processing directly.', + 'Create a CSV import. Upload sources receive signed transfer instructions; workspace-file sources start processing directly.', errors: [...WORKSPACE_ERRORS, 'NotFound', 'Conflict', 'Locked', 'PayloadTooLarge'], success: { description: 'The created table import and optional transfer instructions.' }, }), @@ -1223,7 +1223,7 @@ const declaredRoutes = [ operationId: 'getTableImport', summary: 'Get Table Import', description: - 'Read progress and terminal state for a durable table import.\n\nAn upload-backed import has no durable record until its upload completes, so send the signed upload control token to read it during the `uploading` phase; without the token that phase is a `404`.', + "Get an import's progress and status. During `uploading`, the signed upload token is required; omitting it returns `404`.", errors: RESOURCE_ERRORS, success: { description: 'The requested table import.' }, }), @@ -1262,7 +1262,7 @@ const declaredRoutes = [ operationId: 'cancelTableImport', summary: 'Cancel Table Import', description: - 'Cancel an upload or processing import without rolling back committed row batches.\n\nAn import that is not in a cancelable state, including an `expired` one, is a `409` naming the current status. An unknown or already-purged import id is a `404`.', + 'Cancel an upload or processing import. Committed row batches remain. Non-cancelable states, including `expired`, return `409`; unknown or purged imports return `404`.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The canceled table import.' }, }), @@ -1300,7 +1300,7 @@ const declaredRoutes = [ operationId: 'createTableImportPartUrls', summary: 'Create Table Import Part URLs', description: - 'Issue short-lived signed PUT URLs for a bounded set of multipart part numbers.\n\nThe import must still be `uploading`; one that has moved on, including an `expired` one, is a `409` naming the current status. An unknown or already-purged import id is a `404`.', + 'Create signed URLs for multipart upload parts. Requires the `uploading` state; other states return `409`. Unknown or purged imports return `404`.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The signed multipart upload URLs.' }, }), @@ -1345,7 +1345,7 @@ const declaredRoutes = [ operationId: 'completeTableImportUpload', summary: 'Complete Table Import Upload', description: - 'Verify or assemble the uploaded CSV and begin processing with the same import id.\n\nAn import no longer awaiting an upload, including an `expired` one, is a `409` naming the current status. An unknown or already-purged import id is a `404`.', + 'Verify or assemble uploaded CSV bytes and start processing under the same import ID. Requires an import awaiting upload completion; other states return `409`. Unknown or purged imports return `404`.', errors: [...RESOURCE_CONFLICT_ERRORS, 'Locked'], success: { description: 'The table import after upload completion.' }, }), @@ -1383,7 +1383,7 @@ const declaredRoutes = [ operationId: 'createTableExport', summary: 'Create Table Export', description: - 'Create a durable CSV or JSON export that completes inline for small tables and queues larger work.', + 'Create a CSV or JSON export. Exports of small tables finish during the request; larger exports run asynchronously.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The created table export.' }, }), @@ -1416,7 +1416,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.readExport, operationId: 'getTableExport', summary: 'Get Table Export', - description: 'Read progress and terminal state for a durable table export.', + description: "Get a table export's progress and status.", errors: RESOURCE_ERRORS, success: { description: 'The requested table export.' }, }), @@ -1448,7 +1448,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.cancelExport, operationId: 'cancelTableExport', summary: 'Cancel Table Export', - description: 'Cancel an export that has not reached a terminal state.', + description: 'Cancel an export that is still in progress.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'The canceled table export.' }, }), @@ -1480,7 +1480,7 @@ const declaredRoutes = [ operationId: 'downloadTableExport', summary: 'Download Table Export', description: - 'Return a short-lived signed download URL for a completed table export.\n\nThe export must have reached `completed`; one still processing, failed, or canceled is a `409` naming the current status. An export whose file is no longer available is a `404`, not a `410`.', + 'Get a short-lived signed download URL for a completed export. Other states return `409`; an unavailable export file returns `404`.', errors: RESOURCE_CONFLICT_ERRORS, success: { description: 'Signed table-export download information.' }, }), @@ -1545,7 +1545,7 @@ const declaredRoutes = [ applicationOperation: tableOperations.listFolders, operationId: 'listTablesFolders', summary: 'List Folders', - description: `List table folders, optionally restricting the result to direct children of a canonical parent path. ${FULL_SET_LIST}`, + description: `List table folders, optionally limiting results to direct children of a parent path. ${FULL_SET_LIST}`, errors: [...WORKSPACE_ERRORS, 'NotFound', 'PayloadTooLarge'], success: { description: 'The table folders.' }, }), @@ -1631,7 +1631,7 @@ const declaredRoutes = [ operationId: 'deleteTablesFolder', summary: 'Delete Folder', description: - 'Delete an empty table folder, or recursively delete its descendants and tables when explicitly requested.', + 'Archive an empty folder, or set `recursive=true` to archive its tables and subfolders. Use Restore Folder to recover the archived contents.', errors: [...RESOURCE_MUTATION_ERRORS, 'PayloadTooLarge'], success: { description: 'Table-folder deletion acknowledgement.' }, }), @@ -1657,7 +1657,7 @@ const declaredRoutes = [ operationId: 'restoreTablesFolder', summary: 'Restore Folder', description: - 'Restore a recursively archived table folder with its subfolders and tables, addressed by its former path. If its parent remains archived, it is re-rooted to `/`; active-name conflicts are deduplicated, so use the returned `path`. Non-archived paths return `404`. Preserve the path returned by `DELETE /api/v2/tables/folders`: unlike the files API, the table-folder list cannot discover archived paths.', + 'Restore an archived table folder, its descendants, and tables using its former path. An archived parent moves it to the root; name conflicts may change the returned `path`. Non-archived paths return `404`. Save the path from Delete Folder, because List Folders does not include archived table folders.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The restored table folder and what it brought back.' }, }), @@ -1685,7 +1685,7 @@ const declaredRoutes = [ operationId: 'restoreTable', summary: 'Restore Table', description: - 'Un-archive a table a `DELETE` archived, along with the rows, views, and workflow groups archived with it. Find archived tables with `scope=archived` on the table list. Idempotent: a table that is already active is returned unchanged with no audit entry recorded, so a retry after a dropped response cannot look like a failure. A name collision is resolved by renaming, so the restored table may come back under a different `name`.', + 'Restore a table and its archived rows, views, and workflow groups. Active tables return unchanged without a new audit event. Name conflicts may change the returned `name`. Find archived tables with List Tables and `scope=archived`.', errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: { description: 'The restored table.' }, }), @@ -1719,7 +1719,7 @@ const declaredRoutes = [ operationId: 'bulkUpdateTableRows', summary: 'Bulk Update Rows', description: - 'Apply a distinct partial data patch to each of up to 1000 rows in one request. Each patch merges into its row, so a column absent from `data` is left alone. Membership is atomic: a `rowId` naming no row in this table fails the whole request with a `400` listing the missing identifiers. Use `PATCH /api/v2/tables/{tableId}/rows` when one patch applies to every matching row.', + 'Apply separate partial patches to up to 1,000 rows, preserving omitted columns. A row outside the table rejects the entire request with `400` and lists missing IDs. Use Update Rows by Filter to apply one patch to every matching row.', errors: [...TABLE_MUTATION_ERRORS, 'PayloadTooLarge'], success: { description: 'The bulk update result.' }, }), @@ -1761,7 +1761,7 @@ const declaredRoutes = [ operationId: 'getRowEnrichment', summary: 'Get Enrichment Run Detail', description: - "Retrieve the provider cascade behind one enrichment cell: every configured provider in cascade order, each one's status, hosted-key cost, and duration, plus which provider produced the match. `null` means the cell has never run, or ran before cascade detail was recorded — distinct from a `404`, which means the table, row, or group does not exist.", + "Get an enrichment cell's provider attempts, statuses, hosted-key costs, durations, and matching provider. Null means no run detail was recorded; `404` means the table, row, or group does not exist.", errors: RESOURCE_ERRORS, success: { description: 'The enrichment run detail, or null when none was recorded.' }, }), @@ -1793,7 +1793,7 @@ const declaredRoutes = [ operationId: 'getTableDispatch', summary: 'Get Run Dispatch', description: - 'Poll one workflow-column run dispatch by the `dispatchId` the run endpoints returned. Answers in every lifecycle state — `pending`, `dispatching`, `complete`, and `canceled` — so a poller can wait for a run to settle. Per-cell outcomes are read with `includeRunState` on the row endpoints.', + "Get a dispatch's current state. Poll until `complete` or `canceled`; use row reads with `includeRunState` for per-cell outcomes.", errors: RESOURCE_ERRORS, success: { description: 'The requested run dispatch.' }, }), @@ -1825,7 +1825,7 @@ const declaredRoutes = [ operationId: 'cancelTableDispatch', summary: 'Cancel Run Dispatch', description: - 'Cancel one run dispatch by the `dispatchId` the run endpoint returned. This stops the scheduler: the dispatcher observes the cancellation at its next iteration and enqueues no further cells. Cells already handed to the queue are NOT canceled here — nothing links a queued cell back to the dispatch that enqueued it — so use `POST /api/v2/tables/{tableId}/cancel-runs` to stop work already in flight. Idempotent: a dispatch already `complete` or `canceled` is returned unchanged.', + 'Stop a dispatch from scheduling more cells. Already queued or running cells continue; use Cancel Column Runs to stop them. Completed or canceled dispatches return unchanged.', errors: RESOURCE_ERRORS, success: { description: 'The dispatch in its post-cancellation state.' }, }), @@ -1857,7 +1857,7 @@ const declaredRoutes = [ operationId: 'listTableDispatches', summary: 'List Active Run Dispatches', description: - 'List the run dispatches still in flight on one table. Bounded by the dispatcher rather than by a page size, so this list is unpaginated and `nextCursor` is always null. A settled dispatch is read by identifier.', + 'List in-flight run dispatches for a table in one page; `nextCursor` is always null. Use Get Run Dispatch to read a settled dispatch.', errors: RESOURCE_ERRORS, success: { description: "The table's active run dispatches." }, }), @@ -1889,7 +1889,7 @@ const declaredRoutes = [ operationId: 'moveTables', summary: 'Move Tables and Folders', description: - 'Move up to 100 tables and canonical-path folders to one destination; `null` or `/` means the workspace root. Processing is best-effort per item: tables already carried by selected folders are `skipped`, missing items are `notFound`, and lock or cycle refusals are `failed` with reasons. An invalid destination rejects the entire request before any move.', + 'Move up to 100 tables and folders to one destination. Items succeed or fail independently: covered tables are `skipped`, missing items are `notFound`, and lock or cycle failures include reasons in `failed`. An invalid destination rejects the request before any move.', errors: [...RESOURCE_ERRORS, 'PayloadTooLarge'], success: { description: 'Per-item outcome of the bulk move.' }, }), @@ -1924,7 +1924,7 @@ const declaredRoutes = [ operationId: 'bulkDeleteTables', summary: 'Bulk Delete Tables and Folders', description: - 'Archive up to 100 tables and delete table folders in a single authorized request. Folders are named by canonical path and each cascades to everything inside it; `deletedItems` reports the totals across every cascade. Archived tables stay recoverable through `POST /api/v2/tables/{tableId}/restore`. Best-effort per item, with the same `skipped` / `notFound` / `failed` dispositions as the bulk move.', + 'Archive up to 100 selected tables and folders, including folder contents. Items succeed or fail independently, with `skipped`, `notFound`, and `failed` outcomes. `deletedItems` includes all descendants. Use Restore Table or Restore Folder to recover archived items.', errors: [...RESOURCE_ERRORS, 'Locked', 'PayloadTooLarge'], success: { description: 'Per-item outcome of the bulk delete.' }, }), diff --git a/apps/sim/lib/api/contracts/v2/openapi/workflows.ts b/apps/sim/lib/api/contracts/v2/openapi/workflows.ts index af71fe09dce..84593b41f28 100644 --- a/apps/sim/lib/api/contracts/v2/openapi/workflows.ts +++ b/apps/sim/lib/api/contracts/v2/openapi/workflows.ts @@ -250,7 +250,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.list, operationId: 'listWorkflows', summary: 'List Workflows', - description: `List workflows in a workspace with lifecycle scope, folder and deployment filters, search, sorting, and opaque cursor pagination. \`scope\` defaults to \`active\`; pass \`archived\` to list workflows a \`DELETE\` archived. ${FOLDER_TREE_TOO_LARGE}`, + description: `List active workflows in a workspace. Use \`scope=archived\` to find workflows available for restoration. Supports folder and deployment filters, search, sorting, and cursor pagination. ${FOLDER_TREE_TOO_LARGE}`, errors: [...WORKSPACE_ERRORS, 'NotFound', 'PayloadTooLarge'], success: jsonSuccess('A page of workflows.'), }), @@ -271,7 +271,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.create, operationId: 'createWorkflowV2', summary: 'Create Workflow', - description: `Create a workflow in a workspace root or canonical workflow folder. The response carries the blocks the platform seeded the workflow with, so the start block's id is available without a second request — attach edges to it directly. ${FOLDER_TREE_TOO_LARGE}`, + description: `Create a workflow at the workspace root or in a workflow folder. The response includes seeded blocks and their IDs for attaching edges. ${FOLDER_TREE_TOO_LARGE}`, errors: [...WORKSPACE_ERRORS, 'NotFound', 'Conflict', 'Locked', 'PayloadTooLarge'], success: jsonSuccess('The created workflow.'), }), @@ -305,7 +305,7 @@ const declaredRoutes = [ operationId: 'getWorkflowState', summary: 'Get Workflow State', description: - 'Get the editable draft graph: blocks, edges, derived loop and parallel containers, and variables. This pollable read records no audit event, and `HEAD` mirrors `GET`. The unsanitized payload includes workspace-scoped credential, knowledge-base, and table ids, so it is not portable. Use `export` for a sanitized copy, but not for read-modify-write because credential bindings are removed. Returned keys exactly match what `PUT /workflows/{workflowId}/state` accepts.', + 'Get the editable draft graph, including blocks, edges, loop and parallel containers, and variables. Use this state with Replace Workflow State to preserve workspace bindings; Export Workflow removes those bindings for portability. This read records no audit event, and `HEAD` mirrors `GET`.', /** * No `413`: unlike the workflow reads beside it this one resolves no * folder path, so it never materializes the workspace's folder tree, and @@ -333,8 +333,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.replaceState, operationId: 'replaceWorkflowState', summary: 'Replace Workflow State', - description: - 'Atomically replace the editable draft graph. Concurrent writes are row-locked and last-write-wins; no partial state is stored. `loops` and `parallels` are recomputed from `blocks`; omitted `variables` remain unchanged. Foreign ids return `409`. This leaves deployment unchanged and marks the draft for redeployment; lint is advisory. `dryRun=true` runs the same validation, lint, and conflict checks without persistence, audit, or notification; `needsRedeployment` reflects pre-write state. Workspace keys are rejected; use personal keys or OAuth.', + description: `Replace the draft graph atomically; concurrent writes are last-write-wins. Recompute containers from blocks and preserve omitted variables. Foreign IDs return \`409\`; lint is advisory. The live deployment is unchanged. \`dryRun=true\` validates without saving, auditing, or notifying; \`needsRedeployment\` describes the pre-write state. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_MUTATION_ERRORS, success: jsonSuccess('The draft graph was replaced.'), }), @@ -372,8 +371,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.applyOperations, operationId: 'applyWorkflowOperations', summary: 'Apply Workflow Operations', - description: - 'Apply graph edits and optional block enablement atomically. Failed operations appear in `skipped`; `deferred` edges resolve when targets exist and must not be retried. With `atomic`, any skip or dropped input returns `409` with `OPERATIONS_NOT_APPLIED` and persists nothing. Non-UUID labels are minted and same-batch references remapped in `mintedBlockIds`. Lint is advisory. `dryRun=true` runs the same checks without persistence, audit, or notification. This changes only the draft. Workspace keys are rejected; use personal keys or OAuth.', + description: `Edit the draft graph and block enablement in one write. Inspect \`skipped\` for failures; do not retry \`deferred\` edges. With \`atomic=true\`, skipped operations or dropped inputs return \`409\` (\`OPERATIONS_NOT_APPLIED\`) without saving. \`mintedBlockIds\` maps labels to generated IDs. Lint is advisory; \`dryRun=true\` validates without saving, auditing, or notifying. The live deployment is unchanged. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_MUTATION_ERRORS, success: jsonSuccess('The batch was applied.'), }), @@ -435,7 +433,7 @@ const declaredRoutes = [ operationId: 'applyWorkflowVariables', summary: 'Update Workflow Variables', description: - 'Add, edit, and delete a workflow\u2019s variables. Operations are matched by variable `name` and applied in order; a batch that changes nothing answers `200` with `changed: false`. Values are coerced to the declared `type`, and a value that cannot be coerced is stored as supplied. Read the current set from `variables` on `GET /workflows/{workflowId}`.', + 'Add, edit, or delete variables by name, applying operations in order. Values are coerced to their declared type when possible; otherwise they are stored as supplied. A batch with no changes returns `200` with `changed: false`. Read current variables with Get Workflow.', errors: RESOURCE_MUTATION_ERRORS, success: jsonSuccess('The variable set after the batch.'), }), @@ -458,7 +456,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.duplicate, operationId: 'duplicateWorkflow', summary: 'Duplicate Workflow', - description: `Copy a workflow, including its blocks, edges, subflows, and variables, into the same workspace. Omitting \`name\` reuses the source name; a collision inside the destination folder is deduplicated rather than refused. ${FOLDER_TREE_TOO_LARGE}`, + description: `Copy a workflow's graph and variables into the same workspace. Omit \`name\` to reuse the source name; name collisions in the destination folder are resolved automatically. ${FOLDER_TREE_TOO_LARGE}`, errors: RESOURCE_MUTATION_ERRORS, success: jsonSuccess('The created copy.'), }), @@ -492,7 +490,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.restore, operationId: 'restoreWorkflow', summary: 'Restore Workflow', - description: `Bring an archived workflow back, along with the schedules, webhooks, MCP tools, and chats that were archived with it. A workflow that is not archived answers \`409\`. A workflow whose folder was archived is restored to the workspace root. ${FOLDER_TREE_TOO_LARGE}`, + description: `Restore an archived workflow and the schedules, webhooks, MCP tools, and chats archived with it. An active workflow returns \`409\`. If its folder is archived, the workflow returns to the workspace root. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_MUTATION_ERRORS, 'PayloadTooLarge'], success: jsonSuccess('The restored workflow.'), }), @@ -514,7 +512,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.moveBulk, operationId: 'moveWorkflows', summary: 'Move Workflows', - description: `Relocate up to 100 workflows into one folder. Explicitly best-effort: each workflow moves in its own transaction, and one that is absent from the workspace, archived, or locked lands in \`failed\` while the rest still move. Duplicate ids are collapsed. ${FOLDER_TREE_TOO_LARGE}`, + description: `Move up to 100 workflows into one folder. Moves succeed or fail independently; missing, archived, or locked workflows appear in \`failed\`. Duplicate IDs are ignored. ${FOLDER_TREE_TOO_LARGE}`, errors: [...WORKSPACE_ERRORS, 'NotFound'], success: jsonSuccess('Which workflows moved and which did not.'), }), @@ -558,7 +556,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.update, operationId: 'updateWorkflowV2', summary: 'Update Workflow', - description: `Rename, describe, or move a workflow to a canonical folder path. ${FOLDER_TREE_TOO_LARGE}`, + description: `Update a workflow's name, description, or folder path. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_MUTATION_ERRORS, 'PayloadTooLarge'], success: jsonSuccess('The updated workflow.'), }), @@ -582,7 +580,7 @@ const declaredRoutes = [ operationId: 'deleteWorkflowV2', summary: 'Delete Workflow', description: - 'Archive a workflow. Despite the verb, this is not an erasure: the workflow, and the schedules, webhooks, MCP tools, and chats attached to it, are stamped archived and stop running, and `POST /workflows/{workflowId}/restore` brings all of them back. An archived workflow disappears from the default list and is reachable with `scope=archived`. The `deleted` field is retained for shipped clients; `archived` states what actually happened.', + 'Archive a workflow and stop its schedules, webhooks, MCP tools, and chats. Use List Workflows with `scope=archived` to find it and Restore Workflow to recover it and its archived resources. Both `deleted` and `archived` acknowledge archival.', errors: [...RESOURCE_ERRORS, 'Locked'], success: jsonSuccess('The workflow was archived.'), }), @@ -661,7 +659,7 @@ const declaredRoutes = [ operationId: 'updateWorkflowVersionV2', summary: 'Update Workflow Version', description: - 'Relabel a deployment version. Merge-patch shaped: an omitted key is unchanged and `description: null` clears the release note. Metadata only — the pinned graph is immutable, and this never changes which version is live. Promote a version with `POST /workflows/{workflowId}/versions/{version}/activate`.', + "Update a deployment version's name or release note. Omitted fields remain unchanged; `description: null` clears the note. The graph and live version remain unchanged. Use Activate Workflow Version to make this version live.", errors: RESOURCE_ERRORS, success: jsonSuccess('The updated version metadata.'), }), @@ -692,7 +690,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.activateVersion, operationId: 'activateWorkflowVersion', summary: 'Activate Workflow Version', - description: `Promote an existing deployment version to live. Activation is asynchronous; inspect \`isDeployed\` and \`latestDeploymentAttempt\` for current state. Unlike \`rollback\`, the target is named by the path and the workflow need not already be deployed. ${WORKSPACE_API_KEY_DENIED}`, + description: `Asynchronously activate a specific deployment version, including when the workflow is not currently deployed. The draft remains unchanged. Read Get Workflow Deployment for \`isDeployed\` and \`latestDeploymentAttempt\`. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'Conflict', 'PayloadTooLarge', 'Locked'], success: jsonSuccess('The accepted activation attempt.'), }), @@ -738,7 +736,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.revertVersion, operationId: 'revertWorkflowVersion', summary: 'Revert Workflow To Version', - description: `Overwrite the editable draft with a deployment version, irreversibly discarding unsaved edits. This does not change the live version; use \`activate\` or \`rollback\` for production, both of which leave the draft unchanged. Pass \`active\` to reset the draft to the live graph. ${WORKSPACE_API_KEY_DENIED}`, + description: `Replace the editable draft with a deployment version, discarding current draft edits. Use \`active\` for the live version. The live deployment remains unchanged; Activate Workflow Version or Rollback Workflow changes it. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'Conflict', 'PayloadTooLarge', 'Locked'], success: jsonSuccess('The draft after it was overwritten.'), }), @@ -761,7 +759,8 @@ const declaredRoutes = [ applicationOperation: workflowOperations.read, operationId: 'getWorkflowDeployment', summary: 'Get Workflow Deployment', - description: `Read the live version, latest deployment attempt and readiness, draft drift (\`needsRedeployment\`), and \`isPublicApi\`. When \`isPublicApi\` is true, anyone with the execution URL can run and consume billed usage without an API key; change it with \`PATCH /workflows/{workflowId}/deployment\`. ${WORKFLOW_DEPLOYMENT_VS_CHAT}`, + description: + 'Get the live version, latest deployment attempt, readiness, draft changes (`needsRedeployment`), and public API access. With `isPublicApi: true`, anyone with the execution URL can run the workflow and consume billed usage without an API key. Hosted chat is managed separately.', errors: RESOURCE_ERRORS, success: jsonSuccess('The current deployment state.'), }), @@ -811,7 +810,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.updatePublicApi, operationId: 'updateWorkflowPublicApi', summary: 'Update Workflow Public API Access', - description: `Enable or disable unauthenticated public execution of the deployed workflow. While enabled, anyone holding the execution URL can run the workflow without an API key. An organization that forbids public sharing refuses this with \`403\` and \`PUBLIC_SHARING_NOT_ALLOWED\`. ${WORKFLOW_DEPLOYMENT_VS_CHAT} ${WORKSPACE_API_KEY_DENIED}`, + description: `Enable or disable unauthenticated execution of the deployed workflow. Enabling allows anyone with the execution URL to consume billed usage. Organization sharing restrictions return \`403\` with \`PUBLIC_SHARING_NOT_ALLOWED\`. Hosted chat is managed separately. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'PayloadTooLarge', 'Locked'], success: jsonSuccess('The updated public API setting.'), }), @@ -834,7 +833,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.deploy, operationId: 'deployWorkflow', summary: 'Deploy Workflow', - description: `Create and asynchronously activate a deployment version. Not idempotent: every call mints a new version, so a retry after a timeout creates a second one. A deployment that would conflict with an existing webhook path is a \`409\`. ${WORKSPACE_API_KEY_DENIED}`, + description: `Create and asynchronously activate a deployment version. Every call creates a new version; retrying after a timeout can create a duplicate. Read Get Workflow Deployment to check activation. A conflicting webhook path returns \`409\`. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'Conflict', 'PayloadTooLarge', 'Locked'], success: jsonSuccess('The accepted deployment attempt.'), }), @@ -913,7 +912,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.activateVersion, operationId: 'rollbackWorkflow', summary: 'Rollback Workflow', - description: `Asynchronously reactivate a previous deployment version, selecting the preceding active version when no version is supplied. Use this to step back from the currently live version; to make a specific version live by naming it in the path — including when the workflow is not currently deployed — use \`POST /workflows/{workflowId}/versions/{version}/activate\`. Neither touches the draft. ${WORKSPACE_API_KEY_DENIED}`, + description: `Asynchronously activate a previous deployment version, defaulting to the preceding active version. Requires a deployed workflow and leaves the draft unchanged. Use Activate Workflow Version to select a version when the workflow is undeployed. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'Conflict', 'PayloadTooLarge', 'Locked'], success: jsonSuccess('The accepted rollback attempt.'), }), @@ -1031,7 +1030,7 @@ const declaredRoutes = [ operationId: 'listChatDeployments', summary: 'List Chat Deployments', description: - 'List hosted chats in a workspace with opaque cursor pagination. Filter by `workflowId` to resolve one workflow’s singleton chat. Each item includes its public URL, whose identifier is a path segment, but omits `allowedEmails`, `hasPassword`, and `customizations`; read those through the admin-only singleton endpoint. This list requires workspace read access and accepts workspace API keys. Stored passwords are never returned.', + "List hosted chats and their public URLs with cursor pagination. Filter by `workflowId` for one workflow's chat. The list requires workspace read access; Get Workflow Chat Deployment requires admin access and includes visitor access settings and customizations. Passwords are never returned.", errors: RESOURCE_ERRORS, success: jsonSuccess('A page of chat deployments.'), }), @@ -1052,7 +1051,7 @@ const declaredRoutes = [ applicationOperation: chatDeploymentOperations.read, operationId: 'getWorkflowChatDeployment', summary: 'Get Workflow Chat Deployment', - description: `Read a workflow’s singleton hosted chat, or return \`404\` when none exists. ${CHAT_VS_WORKFLOW_DEPLOYMENT} The password is never returned; \`hasPassword\` reports its presence. Visitor-gate fields (\`authType\`, \`hasPassword\`, and \`allowedEmails\`) require workspace admin access. ${WORKSPACE_API_KEY_DENIED}`, + description: `Get a workflow's hosted chat and visitor access settings. Requires workspace admin access; a missing chat returns \`404\`. Passwords are never returned; \`hasPassword\` indicates whether one is set. Hosted chat and workflow API deployment are managed separately. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: jsonSuccess("The workflow's chat deployment."), }), @@ -1074,7 +1073,7 @@ const declaredRoutes = [ applicationOperation: chatDeploymentOperations.replace, operationId: 'replaceWorkflowChatDeployment', summary: 'Create or Replace Workflow Chat Deployment', - description: `Create or replace hosted chat. Omitted fields reset to defaults except per-field \`customizations\`. \`password\` is write-only and required for password auth; \`allowedEmails\` is required and non-empty for email or SSO. This also deploys the draft. A duplicate identifier or pending deployment returns \`409\`; public auth exposes the URL. ${CHAT_VS_WORKFLOW_DEPLOYMENT} Workspace keys are rejected; use personal keys or OAuth.`, + description: `Create or replace a workflow's hosted chat and deploy its draft. Omitted fields reset to defaults except per-field customizations. Password authentication requires \`password\`; email or SSO requires non-empty \`allowedEmails\`. Public authentication allows anyone with the chat URL to use it. A duplicate identifier or pending deployment returns \`409\`. ${WORKSPACE_API_KEY_DENIED}`, errors: [...RESOURCE_ERRORS, 'Conflict', 'PayloadTooLarge', 'Locked'], success: jsonSuccess('The published chat deployment.'), }), @@ -1097,7 +1096,7 @@ const declaredRoutes = [ applicationOperation: chatDeploymentOperations.delete, operationId: 'deleteWorkflowChatDeployment', summary: 'Delete Workflow Chat Deployment', - description: `Stop serving a workflow's hosted chat. Its URL stops answering and the identifier becomes free again. The workflow's own deployment is untouched and stays executable through the workflow API — to undeploy that, use \`DELETE /workflows/{workflowId}/deploy\`. ${WORKSPACE_API_KEY_DENIED}`, + description: `Remove a workflow's hosted chat and release its URL identifier. The workflow API deployment remains active; use Undeploy Workflow to stop it. ${WORKSPACE_API_KEY_DENIED}`, errors: RESOURCE_ERRORS, success: jsonSuccess('The chat deployment was removed.'), }), @@ -1119,7 +1118,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.execute, operationId: 'executeWorkflowV2', summary: 'Execute Workflow', - description: `Execute a deployment or use \`run.source: "manual"\` for draft state. Manual runs require personal or OAuth write access and reject workspace keys, anonymous callers, and async. Start at a trigger or resume from same-workflow \`sourceRunId\`. Public deployments allow anonymous sync or streaming. Request \`application/x-ndjson\` for 15-second heartbeats and final resource. Timeouts return \`200\` with failed status and \`TIMEOUT\`. ${EXECUTE_OPTION_CONSTRAINTS}`, + description: `Execute a deployment or use \`run.source: "manual"\` for the draft. Manual runs require personal or OAuth write access and reject async. Public deployments allow anonymous sync or streaming. Request \`application/x-ndjson\` for heartbeats and the final result. Timeouts return \`200\` with failed status and \`TIMEOUT\`. Supply \`X-Run-Id\` to prevent duplicate execution; reuse returns \`409\`, never a replay. ${EXECUTE_OPTION_CONSTRAINTS}`, errors: [ 'BadRequest', 'Unauthorized', @@ -1202,7 +1201,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.readRun, operationId: 'getWorkflowRunV2', summary: 'Get Workflow Run', - description: `Get current run state with optional final and block outputs. With \`includeOutput\`, \`files\` includes download paths; \`includeFileBase64\` reads object storage to inline bytes and returns \`413\` with the download path when one file or the total exceeds 16 MiB. ${HEAD_MIRRORS_GET}`, + description: `Get current run state with optional final and block outputs. With \`includeOutput\`, \`files\` includes download paths; \`includeFileBase64\` inlines file bytes and returns \`413\` with the download path when one file or the total exceeds 16 MiB. ${HEAD_MIRRORS_GET}`, errors: [...RESOURCE_CONFLICT_ERRORS, 'PayloadTooLarge'], success: jsonSuccess('The workflow run status.'), }), @@ -1251,7 +1250,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.downloadRunFile, operationId: 'downloadWorkflowRunFileV2', summary: 'Download Workflow Run File', - description: `Download one run-produced file by id. Downloads record an audit event. ${RUN_RETENTION} ${HEAD_MIRRORS_GET} ${HEAD_OMITS_PAYLOAD_HEADERS}`, + description: `Download one run-produced file by ID. Downloads record an audit event. ${RUN_RETENTION} ${HEAD_MIRRORS_GET} ${HEAD_OMITS_PAYLOAD_HEADERS}`, errors: [...RESOURCE_CONFLICT_ERRORS], success: { description: 'The run file bytes.', @@ -1271,7 +1270,7 @@ const declaredRoutes = [ operationId: 'resumeWorkflowRunV2', summary: 'Resume Workflow Run', description: - 'Resume one human-in-the-loop pause context. The resumed attempt receives a new run identifier and may complete synchronously or return a queue receipt.', + 'Resume one human-in-the-loop pause. The resumed attempt receives a new run ID and returns either a synchronous result or a queue receipt.', errors: [...RESOURCE_ERRORS, 'UsageLimitExceeded', 'Conflict', 'PayloadTooLarge'], success: { byStatus: { @@ -1301,7 +1300,7 @@ const declaredRoutes = [ operationId: 'cancelRunV2', summary: 'Cancel Workflow Run', description: - 'Request cancellation of a running, queued, or paused workflow run. Cancelling a run already in a terminal state succeeds with no effect. A run produced by a table workflow group is a `409` when its cell can no longer accept the cancellation.', + 'Request cancellation of a running, queued, or paused workflow run. Terminal runs return successfully without changes. A table workflow-group run returns `409` if its cell can no longer accept cancellation.', errors: RESOURCE_CONFLICT_ERRORS, success: jsonSuccess('The cancellation outcome.'), }), @@ -1335,7 +1334,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.listFolders, operationId: 'listWorkflowsFolders', summary: 'List Workflow Folders', - description: `List canonical workflow folders in a workspace. ${FULL_SET_LIST} ${FOLDER_TREE_TOO_LARGE}`, + description: `List workflow folders in a workspace. ${FULL_SET_LIST} ${FOLDER_TREE_TOO_LARGE}`, errors: [...WORKSPACE_ERRORS, 'NotFound', 'PayloadTooLarge'], success: jsonSuccess('A list of workflow folders.'), }), @@ -1361,7 +1360,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.createFolder, operationId: 'createWorkflowsFolder', summary: 'Create Workflow Folder', - description: `Create a canonical workflow folder in a workspace. ${FOLDER_TREE_TOO_LARGE}`, + description: `Create a workflow folder in a workspace. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_MUTATION_ERRORS, 'PayloadTooLarge'], success: jsonSuccess('The created workflow folder.'), }), @@ -1389,7 +1388,7 @@ const declaredRoutes = [ applicationOperation: workflowOperations.relocateFolder, operationId: 'relocateWorkflowsFolder', summary: 'Rename or Move Workflow Folder', - description: `Rename or move a workflow folder and its descendants to a canonical path. ${FOLDER_TREE_TOO_LARGE}`, + description: `Rename or move a workflow folder and update all descendant paths. ${FOLDER_TREE_TOO_LARGE}`, errors: [...RESOURCE_MUTATION_ERRORS, 'PayloadTooLarge'], success: jsonSuccess('The relocated workflow folder.'), }), @@ -1423,7 +1422,8 @@ const declaredRoutes = [ applicationOperation: workflowOperations.deleteFolder, operationId: 'deleteWorkflowsFolder', summary: 'Delete Workflow Folder', - description: 'Delete a workflow folder, optionally including its descendants and workflows.', + description: + 'Archive an empty workflow folder, or set `recursive=true` to archive its subfolders and workflows. Use Restore Workflow to recover workflows.', errors: [...RESOURCE_MUTATION_ERRORS, 'PayloadTooLarge'], success: jsonSuccess('The workflow folder was deleted.'), }), diff --git a/apps/sim/lib/api/contracts/v2/shared.ts b/apps/sim/lib/api/contracts/v2/shared.ts index a113a094958..af4aba1b8e9 100644 --- a/apps/sim/lib/api/contracts/v2/shared.ts +++ b/apps/sim/lib/api/contracts/v2/shared.ts @@ -486,8 +486,7 @@ export const v2SearchSchema = z * folder filter in the family. Mutations keep their 404 — creating into or * moving to a folder that does not exist has no empty-set reading. */ -export const V2_FOLDER_FILTER_MISS = - 'A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.' +export const V2_FOLDER_FILTER_MISS = 'Unknown folder paths contribute no matches.' export const v2SortOrderSchema = z.enum(LIST_SORT_ORDERS).describe('Sort direction.') diff --git a/apps/sim/lib/api/contracts/v2/tables.ts b/apps/sim/lib/api/contracts/v2/tables.ts index 95f1686b33a..ab5119ed5a1 100644 --- a/apps/sim/lib/api/contracts/v2/tables.ts +++ b/apps/sim/lib/api/contracts/v2/tables.ts @@ -1801,16 +1801,16 @@ export const v2RunColumnBodySchema = runColumnBodyBaseSchema export type V2RunColumnBody = z.input /** - * A started run. `dispatchId` identifies the `table_run_dispatches` row the - * dispatcher walks; it is `null` in deployments without a background runner, - * where cells execute inline and no dispatch row is created. + * A table run request. A null dispatch ID does not guarantee successful cell execution. */ export const v2RunColumnDataSchema = z .object({ dispatchId: z .string() .nullable() - .describe('Background dispatch identifier, or null when execution is inline.'), + .describe( + 'Run dispatch ID, or null when no dispatch is available to poll. Use row reads with `includeRunState` to check cell outcomes.' + ), }) .meta({ id: 'V2RunColumnData', @@ -1871,7 +1871,7 @@ export const v2EnrichmentProviderOutcomeSchema = z status: z .string() .describe( - "How this provider ended: `matched`, `no_match`, `skipped`, `error`, or `not_run`. Declared as a string rather than a closed enum because the value is read back out of a schemaless JSONB blob — a member added by a newer runner must widen a client's switch, not fail its read." + 'Provider outcome: `matched`, `no_match`, `skipped`, `error`, or `not_run`. Handle unrecognized values, since additional statuses may be returned.' ), cost: z .number() @@ -2251,7 +2251,7 @@ export const v2TableImportSchema = z .int() .nonnegative() .describe( - 'Lower bound on the source records the CSV parser could not read and dropped, counted as one per parser failure. A single failure can discard more than one record — an unterminated quote swallows the rest of the file and is reported once — so the true loss may be larger. Non-zero means the import is partial even when the status is completed; zero is not a guarantee that nothing was dropped.' + 'Minimum number of source records dropped by parser failures. One failure can discard multiple records, such as an unterminated quote consuming the rest of the file. A non-zero value means partial import even with `completed` status; zero does not guarantee no loss.' ), cellsRejected: z .number() @@ -2601,9 +2601,8 @@ export const v2TableDispatchParamsSchema = tableIdParamsSchema.extend({ export type V2TableDispatchParams = z.output /** - * Polls one dispatch to completion — the resource `POST /tables/{tableId}/dispatches`'s - * `dispatchId` names. A `null` `dispatchId` there means the run settled inline - * and there is nothing to poll. + * Reads a dispatch by its returned ID. Creation can return a null ID when no + * dispatch is available to poll. */ export const v2GetTableDispatchContract = defineRouteContract({ method: 'GET', diff --git a/apps/sim/lib/api/contracts/v2/workflow-mcp-servers.ts b/apps/sim/lib/api/contracts/v2/workflow-mcp-servers.ts index 880bc2d50b4..353dd78ee45 100644 --- a/apps/sim/lib/api/contracts/v2/workflow-mcp-servers.ts +++ b/apps/sim/lib/api/contracts/v2/workflow-mcp-servers.ts @@ -356,7 +356,7 @@ export const v2ListWorkflowMcpServersContract = defineRouteContract({ toolNamesTruncated: z .boolean() .describe( - "Whether `toolCount` and `toolNames` under-report. The names are gathered for the whole page under one ceiling, so a page whose servers publish more tools than that ceiling between them reports only part of each server's inventory. Read one server's tool inventory and check that response's own `truncated`, which reports the same ceiling applied to a single server — only an untruncated response is the authoritative set. Unrelated to `nextCursor`, which is how this list says there are further servers." + 'Whether the page-wide tool-name limit left some inventories incomplete. Use List Workflow MCP Tools for one server and check its `truncated` flag before treating the inventory as complete. `nextCursor` paginates servers, not tool names.' ), }), }, @@ -418,7 +418,7 @@ export const v2ListWorkflowMcpToolsContract = defineRouteContract({ truncated: z .boolean() .describe( - 'Whether this inventory was cut short by the server-side ceiling on how many tools one response may carry. `nextCursor` is null either way — this list takes no `cursor`, so a truncated set cannot be paged past and this flag is the only way to tell a partial inventory from a complete one. A reconciling caller must not treat a truncated set as the full published inventory.' + 'Whether the tool limit left this inventory incomplete. The list is unpaginated and `nextCursor` remains null even when truncated. Do not treat a truncated inventory as the complete set of published tools.' ), }), }, diff --git a/apps/sim/lib/api/contracts/v2/workflows.ts b/apps/sim/lib/api/contracts/v2/workflows.ts index e19bd0d0990..13457744111 100644 --- a/apps/sim/lib/api/contracts/v2/workflows.ts +++ b/apps/sim/lib/api/contracts/v2/workflows.ts @@ -69,15 +69,12 @@ export const v2WorkflowRunIdSchema = runIdSchema .meta({ examples: ['run_8f14e45f-ceea-467f-a'] }) /** - * `X-Run-Id` is a **one-shot uniqueness claim, not an idempotency key.** The - * first request to claim a value starts a run; every later request reusing it - * is rejected with a `409` carrying `error.details.code: "RUN_ID_CONFLICT"`, and - * the original result is never - * replayed. Retry logic written against idempotency-key semantics either - * double-executes (fresh id per attempt) or hard-fails (same id per attempt). + * `X-Run-Id` reserves an execution ID without guaranteeing a retrievable run. + * Claims can survive uncertain execution outcomes; a missing run does not make + * a claimed ID reusable. Reusing a claimed ID returns a conflict, never a replay. */ const X_RUN_ID_DESCRIPTION = - 'Caller-supplied run identifier, available only to API-key callers. A one-shot uniqueness claim, NOT an idempotency key: reusing a value fails with `409` and `error.details.code: "RUN_ID_CONFLICT"` rather than replaying the original result. To retry safely, send a fresh value per attempt, or omit the header and let the server allocate one.' + 'Run ID for API-key or OAuth callers; ignored for anonymous requests. Reuse it after an uncertain response: a claimed ID returns `409` with `RUN_ID_CONFLICT`, without replaying results. Check Get Workflow Run, but `404` can persist while the ID remains claimed and does not establish whether execution started. Do not automatically restart with a fresh or omitted ID; either can start another run.' const X_SIM_VIA_DESCRIPTION = 'Comma-separated workflow identifiers naming the workflow-to-workflow call chain that led to this request. Each hop appends its own workflow id, and Sim sets it automatically; supply it yourself only when relaying an existing chain. A chain at the maximum depth is rejected with `409` and `error.details.code: "CALL_CHAIN_DEPTH_EXCEEDED"`.' @@ -219,7 +216,7 @@ export const v2WorkflowListItemSchema = z .int() .nonnegative() .describe( - 'Runs that finished successfully. Failed, cancelled, and paused runs are not counted, and the counter is never reduced when a run ages out of log retention — so it does not match the size of `GET /api/v2/workflows/{workflowId}/runs`, in either direction.' + 'Lifetime count of successful runs, excluding failed, canceled, and paused runs. Log retention does not reduce this count; it may differ from the number returned by List Workflow Runs.' ), lastRunAt: z .string() @@ -379,7 +376,7 @@ export const v2WorkflowDeploymentSchema = v2DeploymentStateSchema isPublicApi: z .boolean() .describe( - 'Whether the deployed workflow accepts unauthenticated public API execution. While true, anyone holding the execution URL can run the workflow — and be billed for it — without an API key, so this is the field an audit of what a deployment exposes reads. Changed with `PATCH /workflows/{workflowId}/deployment`.' + 'Whether anyone with the execution URL can run the deployed workflow and consume billed usage without an API key. Change this with Update Workflow Public API Access.' ), }) .meta({ @@ -404,7 +401,7 @@ export const v2DeployWorkflowDataSchema = v2DeploymentStateSchema id: 'DeployResult', title: 'Deploy result', description: - 'Deployment attempt accepted for processing. Activation is asynchronous, and `latestDeploymentAttempt` is the attempt handle — returned by every deployment mutation as well as this read. Poll activation with `isDeployed` and `deployedAt` on the workflow, or `isActive` on `GET /workflows/{workflowId}/versions`.', + 'Deployment attempt accepted for asynchronous activation. `latestDeploymentAttempt` identifies the attempt. Poll Get Workflow Deployment for `isDeployed` and `deployedAt`, or List Workflow Versions for `isActive`.', }) export type V2DeployWorkflowData = z.output @@ -559,7 +556,7 @@ export const v2DeleteWorkflowDataSchema = z archived: z .literal(true) .describe( - 'The workflow was archived, not erased. Its schedules, webhooks, MCP tools, and chats were archived with it, and `POST /workflows/{workflowId}/restore` brings all of them back.' + 'Whether the workflow was archived. Restore Workflow recovers it and the schedules, webhooks, MCP tools, and chats archived with it.' ), }) .meta({ @@ -785,7 +782,7 @@ export const v2WorkflowVersionDetailSchema = z .describe('ISO 8601 timestamp when this version was created.') .meta({ format: 'date-time' }), state: deployedWorkflowStateSchema.describe( - 'Deployed workflow graph snapshot pinned by this version, with credential-bearing values redacted to null: `oauth-input`, `password: true`, table sub-block values, sensitive nested tool parameters, and any parameter without authoritative codec metadata.' + 'Workflow graph saved with this deployment version. Sensitive values are redacted to null.' ), }) .meta({ @@ -1177,7 +1174,7 @@ export type V2ExecutionError = z.output * `stream`, `executionTimeoutSeconds`, `includeThinking`, or `includeToolCalls`. */ export const EXECUTE_OPTION_CONSTRAINTS = - 'Each option carries the modes it requires and the modes that reject it; a violated combination is a 400.' + 'Input descriptions specify compatible modes; invalid combinations return `400`.' export const v2WorkflowRunSelectionSchema = z.discriminatedUnion('source', [ z @@ -1223,7 +1220,7 @@ export const v2WorkflowRunSelectionSchema = z.discriminatedUnion('source', [ .string() .min(1, 'run.entry.sourceRunId cannot be empty') .describe( - 'Exact prior run whose persisted execution snapshot supplies upstream block state.' + 'Run ID supplying upstream block results when starting from a selected block.' ), }) .strict(), @@ -1279,7 +1276,7 @@ export const v2ExecuteWorkflowBodySchema = z .max(MAX_WORKFLOW_EXECUTION_TIMEOUT_SECONDS) .optional() .describe( - "Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true." + "Maximum duration of an asynchronous run, in seconds, capped by the plan's execution timeout. Requires `async: true`; otherwise returns `400`." ), stream: z .boolean() @@ -1293,7 +1290,7 @@ export const v2ExecuteWorkflowBodySchema = z .max(100) .optional() .describe( - 'Block output references to include in a streamed response. Use `.` for the executed workflow or `..` for a child workflow; block names are normalized workflow reference names. Selecting a child workflow applies to every invocation of it. Requires `stream: true` — it shapes the streamed envelope only, so it is rejected on a sync request and when `async` is true. To narrow a finished run, pass `selectedOutputs` to the run resource instead.' + 'Output references for streaming: `.` or `..`, using normalized block names. Child references apply to every invocation. Requires `stream: true` and rejects synchronous or async requests. Use `selectedOutputs` with Get Workflow Run to narrow an existing run.' ), includeThinking: z .boolean() @@ -2669,7 +2666,7 @@ export const v2AgentIntegrationToolSchema = z .max(255, 'Agent integration tool operation must be at most 255 characters') .optional() .describe( - 'Operation id from `GET /api/v2/blocks/{blockId}`. Required when the block exposes multiple operations; it may differ from the underlying tool id.' + 'Operation ID from Get Block. Required when the block exposes multiple operations; it may differ from the tool ID.' ), usageControl: v2AgentToolUsageControlSchema.optional(), params: v2AgentToolParamsSchema.optional(), @@ -2702,7 +2699,7 @@ const v2AgentCustomToolReferenceSchema = z .trim() .min(1, 'Agent customToolId cannot be empty') .max(255, 'Agent customToolId must be at most 255 characters') - .describe('Custom tool id returned by `GET /api/v2/custom-tools`.'), + .describe('Custom tool ID from List Custom Tools.'), usageControl: v2AgentToolUsageControlSchema.optional(), }) .catchall( @@ -2754,7 +2751,7 @@ export const v2AgentCustomToolSchema = z id: 'AgentCustomTool', title: 'Agent custom tool', description: - 'A workspace custom tool. Reference `customToolId` is the preferred shape; the inline declaration is retained for legacy workflow round trips.', + 'A workspace custom tool. Prefer `customToolId`; inline declarations are also accepted.', examples: [ { type: 'custom-tool', @@ -3146,7 +3143,7 @@ export const v2ApplyWorkflowOperationsDataSchema = v2WorkflowGraphWriteResultSch deferred: z .array(v2WorkflowSkippedItemSchema) .describe( - 'Forward-referencing edges the engine recorded rather than applied. These are NOT failures: the engine wires each one as soon as its target block exists, in this batch or a later one. Do not re-issue them.' + 'Edges waiting for target blocks. They apply automatically when their targets exist, in this batch or a later one. Do not resubmit them.' ), inputValidationErrors: z .array(v2WorkflowInputValidationErrorSchema) diff --git a/packages/sim-cli/src/generated/v2-api.ts b/packages/sim-cli/src/generated/v2-api.ts index 81cdc264fd8..645d18501cb 100644 --- a/packages/sim-cli/src/generated/v2-api.ts +++ b/packages/sim-cli/src/generated/v2-api.ts @@ -9627,6 +9627,7 @@ export const V2_OPERATIONS = { pathParamDocs: { workflowId: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Apply Workflow Operations', + workspaceKeyUnsupported: true, query: { dryRun: { kind: 'boolean', @@ -9721,7 +9722,7 @@ export const V2_OPERATIONS = { folderPaths: { kind: 'string', describe: - 'Folder paths to include with all their descendants, comma-separated. At most 100 entries, and the files they resolve to count against the same 100-file download ceiling. A path that matches no folder is rejected rather than ignored.', + 'Comma-separated folder paths whose contents are included recursively. Up to 100 paths; resolved files share the 100-file download limit. Unknown paths are rejected.', }, }, }, @@ -10387,8 +10388,7 @@ export const V2_OPERATIONS = { kind: 'enum', values: ['streamable-http'] as const, default: 'streamable-http', - describe: - 'Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.', + describe: 'Transport protocol. Defaults to `streamable-http` on creation.', }, url: { kind: 'string', @@ -10410,19 +10410,17 @@ export const V2_OPERATIONS = { timeout: { kind: 'integer', default: 30000, - describe: - 'Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.', + describe: 'Per-request timeout in milliseconds. Defaults to 30000 on creation.', }, retries: { kind: 'integer', default: 3, - describe: 'Number of retries per request. Applied server-side as 3 when omitted on create.', + describe: 'Number of retries per request. Defaults to 3 on creation.', }, enabled: { kind: 'boolean', default: true, - describe: - 'Whether the server tools are available to workflows. Applied server-side as true when omitted on create.', + describe: "Whether workflows can use the server's tools. Defaults to true on creation.", }, oauthClientId: { kind: 'string', @@ -11342,7 +11340,7 @@ export const V2_OPERATIONS = { kind: 'object', default: {}, describe: - 'Arguments for the tool, keyed by the parameter ids the tool catalog publishes for it. A parameter whose visibility is `user-only` also accepts an environment-variable reference written as the whole value, `{{VAR_NAME}}`, resolved server-side against the workspace environment; any other value is sent verbatim.', + 'Tool arguments keyed by published parameter IDs. For `user-only` parameters, a whole-value `{{VAR_NAME}}` reference resolves a workspace environment variable. Other values pass through unchanged.', }, credentialId: { kind: 'string', @@ -11381,7 +11379,7 @@ export const V2_OPERATIONS = { executionTimeoutSeconds: { kind: 'integer', describe: - "Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true.", + "Maximum duration of an asynchronous run, in seconds, capped by the plan's execution timeout. Requires `async: true`; otherwise returns `400`.", }, stream: { kind: 'boolean', @@ -11392,7 +11390,7 @@ export const V2_OPERATIONS = { selectedOutputs: { kind: 'array', describe: - 'Block output references to include in a streamed response. Use `.` for the executed workflow or `..` for a child workflow; block names are normalized workflow reference names. Selecting a child workflow applies to every invocation of it. Requires `stream: true` — it shapes the streamed envelope only, so it is rejected on a sync request and when `async` is true. To narrow a finished run, pass `selectedOutputs` to the run resource instead.', + 'Output references for streaming: `.` or `..`, using normalized block names. Child references apply to every invocation. Requires `stream: true` and rejects synchronous or async requests. Use `selectedOutputs` with Get Workflow Run to narrow an existing run.', }, includeThinking: { kind: 'boolean', @@ -11420,7 +11418,7 @@ export const V2_OPERATIONS = { 'x-run-id': { kind: 'string', describe: - 'Caller-supplied run identifier, available only to API-key callers. A one-shot uniqueness claim, NOT an idempotency key: reusing a value fails with `409` and `error.details.code: "RUN_ID_CONFLICT"` rather than replaying the original result. To retry safely, send a fresh value per attempt, or omit the header and let the server allocate one.', + 'Run ID for API-key or OAuth callers; ignored for anonymous requests. Reuse it after an uncertain response: a claimed ID returns `409` with `RUN_ID_CONFLICT`, without replaying results. Check Get Workflow Run, but `404` can persist while the ID remains claimed and does not establish whether execution started. Do not automatically restart with a fresh or omitted ID; either can start another run.', }, 'x-sim-via': { kind: 'string', @@ -11652,7 +11650,7 @@ export const V2_OPERATIONS = { folderPaths: { kind: 'string', describe: - 'Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches.', }, triggers: { kind: 'string', @@ -11678,7 +11676,7 @@ export const V2_OPERATIONS = { kind: 'integer', default: 72, describe: - 'Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty.', + 'Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets.', }, }, }, @@ -12157,7 +12155,7 @@ export const V2_OPERATIONS = { source: { kind: 'enum', values: ['builtin', 'custom'] as const, - describe: 'Restrict to shipped blocks or to this workspace’s deployed custom blocks.', + describe: "Restrict to built-in blocks or this workspace's deployed custom blocks.", }, sortBy: { kind: 'enum', @@ -12368,7 +12366,7 @@ export const V2_OPERATIONS = { parentPath: { kind: 'string', describe: - 'Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.', }, search: { kind: 'string', @@ -12433,7 +12431,7 @@ export const V2_OPERATIONS = { folderPath: { kind: 'string', describe: - 'Restrict results to files inside this folder — its direct children, or its whole subtree when `recursive` is true. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict files to this folder, including subfolders when `recursive` is true. Unknown folder paths contribute no matches.', }, recursive: { kind: 'enum', @@ -12452,7 +12450,7 @@ export const V2_OPERATIONS = { 'disabled', ] as const, describe: - 'Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.', + 'Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter.', }, scope: { kind: 'enum', @@ -12508,12 +12506,12 @@ export const V2_OPERATIONS = { values: ['active', 'archived'] as const, default: 'active', describe: - 'Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/{knowledgeBaseId}/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too.', + 'Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches.', }, folderPath: { kind: 'string', describe: - 'Restrict results to knowledge bases in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to knowledge bases in this folder. Unknown folder paths contribute no matches.', }, search: { kind: 'string', @@ -12747,7 +12745,7 @@ export const V2_OPERATIONS = { parentPath: { kind: 'string', describe: - 'Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.', }, search: { kind: 'string', @@ -12917,7 +12915,7 @@ export const V2_OPERATIONS = { folderPaths: { kind: 'string', describe: - 'Comma-separated workflow folder paths to include. At most 100 entries. A path covers its whole subtree, so `/prod` also selects runs in `/prod/nested`. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Comma-separated workflow folder paths, including descendants. Up to 100 paths. Unknown folder paths contribute no matches.', }, }, }, @@ -12980,7 +12978,7 @@ export const V2_OPERATIONS = { refresh: { kind: 'boolean', describe: - 'Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip.', + "Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools.", }, }, }, @@ -13172,7 +13170,7 @@ export const V2_OPERATIONS = { parentPath: { kind: 'string', describe: - 'Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.', }, search: { kind: 'string', @@ -13242,7 +13240,7 @@ export const V2_OPERATIONS = { folderPath: { kind: 'string', describe: - 'Restrict results to tables in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to tables in this folder. Unknown folder paths contribute no matches.', }, search: { kind: 'string', @@ -13352,7 +13350,7 @@ export const V2_OPERATIONS = { parentPath: { kind: 'string', describe: - 'Restrict results to direct children of this parent path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to direct children of this parent path. Unknown folder paths contribute no matches.', }, search: { kind: 'string', @@ -13498,7 +13496,7 @@ export const V2_OPERATIONS = { folderPath: { kind: 'string', describe: - 'Restrict results to workflows in this folder path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + 'Restrict results to workflows in this folder path. Unknown folder paths contribute no matches.', }, deployedOnly: { kind: 'boolean', @@ -13819,6 +13817,7 @@ export const V2_OPERATIONS = { pathParamDocs: { workflowId: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Create or Replace Workflow Chat Deployment', + workspaceKeyUnsupported: true, body: { identifier: { kind: 'string', @@ -13874,6 +13873,7 @@ export const V2_OPERATIONS = { pathParamDocs: { workflowId: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Replace Workflow State', + workspaceKeyUnsupported: true, query: { dryRun: { kind: 'boolean', @@ -14582,8 +14582,7 @@ export const V2_OPERATIONS = { kind: 'enum', values: ['streamable-http'] as const, default: 'streamable-http', - describe: - 'Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.', + describe: 'Transport protocol. Defaults to `streamable-http` on creation.', }, url: { kind: 'string', @@ -14604,19 +14603,17 @@ export const V2_OPERATIONS = { timeout: { kind: 'integer', default: 30000, - describe: - 'Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.', + describe: 'Per-request timeout in milliseconds. Defaults to 30000 on creation.', }, retries: { kind: 'integer', default: 3, - describe: 'Number of retries per request. Applied server-side as 3 when omitted on create.', + describe: 'Number of retries per request. Defaults to 3 on creation.', }, enabled: { kind: 'boolean', default: true, - describe: - 'Whether the server tools are available to workflows. Applied server-side as true when omitted on create.', + describe: "Whether workflows can use the server's tools. Defaults to true on creation.", }, oauthClientId: { kind: 'string',