workflows-ai-by-zapier · diff
v0.1.0 to v0.3.0
169 added, 96 removed. Audit A to A.
---
name: workflows-ai-by-zapier
- description: Configure a bounded AI by Zapier step in a durable workflow, including prompt-only semantic work, structured outputs, integration or web tools, and knowledge sources. Use when a workflow must summarize, extract meaning, classify, generate, analyze, disambiguate, or let AI choose and refine searches.
+ description: Configure a basic AI by Zapier step with runtime context, outputs, typed integration tools, public web search, and a failure exit. Use when a workflow needs language reasoning or AI-directed app/web lookups, including when nested schema discovery is unavailable.
license: MIT
metadata:
author: zapier
- version: "0.1.0"
+ version: "0.3.0"
sdk_cli_min: "0.67.4"
- sdk_cli_validated: "0.67.5"
refresh_source: "zapier/agent-skills"
---
- <!-- Generated by scripts/compose.mjs from skills/workflows/ai-by-zapier/ — DO NOT EDIT. Change base.md or surfaces/sdk-cli.md and re-run compose. -->
-
# AI by Zapier Workflow Steps
Use this companion with `workflows-create` or `workflows-modify`; it configures the bounded AI step but does not replace the workflow lifecycle. If the request involves creating, testing, publishing, or enabling a complete workflow and neither lifecycle skill is loaded, load the appropriate one first.
## SDK CLI discovery
Use the public SDK CLI's live catalog before writing the AI step:
- Find the current implementation: `zapier-sdk list-apps --search "AI by Zapier" --json`.
- Inspect actions: `zapier-sdk list-actions <appKey> --json`.
- - Inspect fields: `zapier-sdk list-action-input-fields <appKey> <actionType> <actionKey> --json`.
+ - Inspect editable fields: `zapier-sdk list-action-input-fields <appKey> <actionType> <actionKey> --json`.
+ - Prefer the complete contract: `zapier-sdk get-action-input-fields-schema <appKey> <actionType> <actionKey> --json`; retain the whole `data` schema, including nested `contentSchema` and root references. If that discovery is unavailable, use the version-scoped basic fallback below, not an invented nested contract.
- Resolve dependent choices: `zapier-sdk list-action-input-field-choices` with the settled parent inputs.
- - Resolve an embedded app's connection: `zapier-sdk list-connections <appKey> --owner me --json`.
+ - Resolve an embedded app's connection: `zapier-sdk list-connections <appKey> --owner me --json`. Match the intended account by its returned name/label, paginate as needed, and ask when ambiguous. If none is usable, use the installed SDK's supported connect/reconnect flow and rediscover after the user finishes; never ask for raw credentials.
- For `tools[].auth_id`, accept only the positive integer connection ID required by the live schema. If connection discovery returns only a UUID or other public ID, do not coerce it, substitute an alias, or guess the numeric ID; report that this SDK version cannot safely configure that authenticated tool.
+ Keep three namespaces separate:
+ - Managed model credentials: `inputs.authentication_id: "0"`; no outer connection lookup is needed for this selector.
+ - Model BYOK: use the `authentication_id` field choice's returned value after settling its provider dependencies, not a UUID from `list-connections`.
+ - Embedded app tools: copy the selected connection's public UUID `id` from `list-connections` unchanged into `tools[].connection_id`. Use a version whose live contract or the validated fallback supports this field. A legacy numeric `auth_id` is usable only when discovery supplies that format; never decode or coerce a UUID. For connectionless tools, omit both references.
+
+ Outer SDK `connection` aliases do not populate these action inputs. For an embedded tool, put the UUID in that tool object, not in the outer AbZ call's `connection`. Use the same chosen connection and settled parent values for the target action's field/choice discovery. If the selected version does not accept the reference, report that version/contract mismatch instead of inventing another identifier.
+
Use the exact command signatures shown by the installed CLI's `--help`; command flags may evolve. Record the discovered implementation, action, fields, choices, defaults, and connection metadata so you do not repeat calls or substitute remembered values.
- Configure a bounded AI by Zapier step inside a durable workflow. This skill owns AI by Zapier configuration; the workflow skill still owns the trigger, deterministic graph, testing, deployment, and verification.
+ Configure a basic AI by Zapier step: instructions, runtime context, outputs, integration actions, and public web research. Use the live action schema when available; the shapes below are a bounded fallback for callers whose field discovery omits the nested contract.
- ## Confirm the bounded step
+ ## Discover and choose the scope
- Use prompt-only AI by Zapier when the trigger and previous steps already contain all evidence. Add an embedded read or search tool only when the model must choose a query, inspect the result, and adapt or repeat the lookup. If the query is known before execution, keep the search as an ordinary workflow step.
+ 1. Discover AI by Zapier and its normal `get_completion` action (`write`). Use it for both supplied-context and tool-enabled work, not `get_completion_agentic`.
+ 2. Read editable fields, defaults, and dependent choices using your surface's discovery instructions. If a complete schema is available, retain its `contentSchema`, definitions, references, and annotations; prefer it over these examples.
+ 3. This fallback was checked against `AICLIAPI@4.0.27`. Public UUID tool connections are supported from `4.0.26`. It documents the basic shapes below, not every possible capability or every future version. Do not extrapolate an unsupported shape or override conflicting live metadata.
+ 4. Use the live model default unless the user requests a different supported model. The examples use `premium/auto`, the checked version's managed default, with `authentication_id: "0"`. `standard/auto` is prompt-only and does not support these tool configurations. For other models, resolve provider, authentication, and model choices in their dependency order; a visible model is not proof of tool support or managed-credential eligibility.
- Keep predetermined side effects — messages, emails, CRM updates, rows, tasks, and approvals — as durable steps after the AI returns typed output. Embed a write action only when the write genuinely belongs inside the bounded decision loop, its allowed effects are narrow, and the user approved that behavior. Variable-length output alone is not a reason to embed writes: return a typed array and let durable code perform them.
+ Use language reasoning on supplied data without external lookups when it is sufficient. Add integration tools when the AI needs to choose an action or adapt a lookup. Keep predetermined searches and known final side effects as ordinary durable workflow steps; an embedded write needs an explicitly approved, bounded decision loop. Adding a tool enables a capability; it does not call it.
- ## Discover the current action and defaults
+ For this basic recipe, consider only integration actions and the three builtins `fail`, `web_search`, and `web_scrape`. Do not add other builtins, knowledge-source configuration, prompt-scoring helpers, or tier-classification calls. Those are not required to configure this step.
- Discover AI by Zapier's current versioned implementation, actions, and fields before authoring the call. Derive `sdk.runAction`'s unversioned `appKey` from the discovered implementation and use the discovered action type and key. Re-fetch dependent fields after settling parent values such as authentication or provider.
+ ## Typed tool contract
- Use the discovered `get_completion` action for both prompt-only and tool-enabled work. Never run `get_completion_agentic`; it is an administrative restriction action, not the agentic execution path. Tools and knowledge sources belong on `get_completion`.
+ These types describe the decoded entries in AbZ's `inputs.tools` array, not MCP tool definitions. Do not send this TypeScript or a JSON Schema as the value of `tools`.
- Resolve model configuration in this order:
+ ```typescript
+ type ToolInput =
+ | { type: "guess" | "default" | "ignore" }
+ | { type: "locked"; value: string | string[] | Record<string, string> }
+ | { type: "guess-locked"; value: string[] };
- 1. Honor an explicit managed tier using its exact writable sentinel: `standard/auto`, `advanced/auto`, or `premium/auto`.
- 2. If the user names a provider or concrete model, settle `provider_id`, then discover valid `model_id` and `authentication_id` values. Explain when it requires the user's provider connection and billing.
- 3. Otherwise use the non-null `model_id` and `authentication_id` defaults returned by the live `get_completion` fields.
+ type IntegrationTool = {
+ type: "integration";
+ id: string;
+ selected_api: string;
+ action_key: string;
+ action_type: "read" | "search" | "write";
+ display_name: string;
+ description: string;
+ app_needs_auth: boolean | null;
+ connection_id?: string | null;
+ auth_id?: number | null;
+ inputs?: Record<string, ToolInput> | null;
+ input_labels?: Record<string, string | string[]> | null;
+ approval_policy?: "always_allow" | "require_approval" | "deny" | "auto";
+ };
- Never invent a near-match such as `standard`, `basic`, `standard/default`, a remembered vendor slug, an empty value, or a connection ID. Do not set `provider_id` for a managed-tier sentinel unless the live fields require it.
+ type BuiltinTool = {
+ type: "builtin";
+ id: string;
+ builtin_tool_name: "fail" | "web_search" | "web_scrape";
+ };
- AI by Zapier normally carries its own action-level authentication input rather than a workflow connection alias. The literal `"0"` is valid only when returned as the managed-auth default by live discovery; it is not a general fallback for provider or embedded app connections.
+ type BasicTool = IntegrationTool | BuiltinTool;
+ ```
- ## Separate instructions from runtime context
+ - Generate `id` locally, unique across the entire tools list, and keep it stable when editing. It is not a connection UUID, app/action identifier, or MCP `tool_name`.
+ - Copy `selected_api`, `action_key`, and `action_type` from discovery, retaining an app version when supplied. Do not substitute display names, an MCP wrapper name, or a synthetic trigger alias. Use the discovered capability for `display_name` and `description`.
+ - `app_needs_auth` describes the target app: true for an authenticated app, false only when known to be connectionless, null if unspecified. Null does not remove an authentication requirement.
+ - For authenticated tools, copy a discovered public connection UUID unchanged into `connection_id`. A discovered legacy numeric `auth_id` remains supported; at most one reference may be non-null. For a connectionless action, omit both. Never invent an ID, decode a UUID, substitute an account/user ID, or put an outer connection alias here.
+ - `input_labels` is optional display metadata, not a substitute for the values in `inputs`.
- Configure the action according to its live schema:
+ ### Tool input policies
- - `instructions` — the stable task, constraints, tool-use rules, and return contract.
- - `inputFields` — runtime values from the trigger and earlier steps; serialize nested values when the field requires strings.
- - `tools` — only the bounded capabilities the model may invoke.
- - `knowledgeSources` — exact supported source entries resolved through the available product surface.
- - `outputFields` — flat typed fields consumed by later durable steps.
- - `isOutputArray` — `true` only when the intended result is genuinely a list.
+ Discover the target action's fields and choices with the selected connection. Settle parent values before discovering dependent fields, and refresh affected discovery when a parent changes. Respect configured locks and restrictions. Store policies under the actual app field keys, not under MCP's `params` or `dynamic_properties` wrapper.
- Pass only the context needed for the task. Do not interpolate all runtime data into `instructions` or replace the stable task at runtime.
+ | Intent | Entry in the integration tool's `inputs` |
+ | --- | --- |
+ | Determine a value from instructions/context at execution time | `{"type":"guess"}` |
+ | Pin a fixed value | `{"type":"locked","value":"<actual value or choice ID>"}` |
+ | Let the agent choose among discovered candidates | `{"type":"guess-locked","value":["<choice ID 1>","<choice ID 2>"]}` |
+ | Leave normal field/default resolution in control | `{"type":"default"}` |
+ | Intentionally exclude a field when omission is valid | `{"type":"ignore"}` |
- After discovery, a prompt-only structured call should have this shape; replace every angle-bracketed value with a literal returned by discovery:
+ Do not use bare values such as `"channel": "C123"`. A fixed value belongs inside a `locked` entry. Scalar values use strings, including the selected action's numeric/boolean representations; dictionaries contain string values. Include `value` even when it is `""`. An omitted entry is unpinned, not ignored; `default` neither forces a particular default nor prevents runtime resolution.
- ```typescript
- const analysis = await ctx.step("analyze-with-ai", async () =>
- sdk.runAction({
- appKey: "<discovered-unversioned-app-key>",
- actionType: "write",
- actionKey: "get_completion",
- inputs: {
- authentication_id: "<discovered-authentication>",
- model_id: "<discovered-model>",
- instructions: "Classify the supplied text and explain the decision.",
- inputFields: { source_text: input.text },
- outputFields: [
- {
- name: "category",
- description: "The selected category.",
- type: "category_single",
- isRequired: true,
- options: ["match", "no_match"],
- },
- {
- name: "reason",
- description: "A concise explanation grounded in the supplied text.",
- type: "text",
- isRequired: true,
- },
- ],
- isOutputArray: false,
- },
- }),
- );
+ Array values in both `locked` and `guess-locked` are candidates, not literal multi-value app inputs: one item is locked, multiple items guide selection, and an empty array allows guessing. Use nonempty candidate lists for new configurations. Candidate lists guide resolution; they are not an authorization boundary. Do not infer support for arbitrary nested objects or literal line-item arrays from these types.
+
+ ### Approval policy
+
+ Choose the policy deliberately. `require_approval` is the default when omitted; `always_allow` permits execution without per-tool approval; `deny` excludes the tool; `auto` follows runtime policy and becomes `require_approval` during step tests. Omit rather than send null.
+
+ Only use `always_allow` after the user authorizes the bounded behavior, especially writes. If review is required, confirm the execution surface supports that review/resume flow before running. Do not silently change the policy to get past a halt. Permissions and other execution limits still apply.
+
+ ## Always include fail
+
+ Always include exactly one explicit failure exit in the configured `tools` list:
+
+ ```json
+ {
+ "type": "builtin",
+ "id": "tool_fail",
+ "builtin_tool_name": "fail"
+ }
```
- Prefer `outputFields` over legacy `outputSchema`; never configure both. Use only output types accepted by the live field schema, and provide `options` for category fields. Keep `tools`, `knowledgeSources`, and `outputFields` as directly inspectable object and array literals inside `runAction.inputs`, not hidden behind builders, spreads, or dynamic assembly.
+ Tell the step to use it when required external work cannot be completed rather than fabricate a result or claim success. AbZ also adds this builtin automatically and deduplicates builtin names; one explicit entry makes the intended failure path visible in the authored configuration. Tool availability still depends on the model and account. This recipe assumes tool-enabled access, including when `fail` is the only configured tool; do not use it to bypass prompt-only restrictions.
- Without structured output, read the generated text from `result.data[0].output`. With `outputFields`, read the configured fields from `result.data[0]`. Test the real result before finalizing downstream mappings, especially for array output.
+ ## Complete basic step
- ## Configure integration tools from discovery
+ These are decoded `get_completion` inputs: put them in `sdk.runAction(...).inputs`, or the selected step-authoring interface's equivalent. Supply native object/array literals where supported; serialize only when the interface requires it, not again inside each tool. Do not invent platform-owned Zap/run IDs.
- An app action embedded in AI by Zapier is an `integration` tool, not a builtin. Copy its identifiers, metadata, fields, labels, and connection from live discovery:
+ ```json
+ {
+ "model_id": "premium/auto",
+ "authentication_id": "0",
+ "instructions": "Summarize the supplied note in one sentence. Do not claim to read external data or send any messages. If the task cannot be completed, use fail rather than inventing a result.",
+ "inputFields": {
+ "note": "The launch moved to Friday after final testing."
+ },
+ "includeWorkflowData": false,
+ "outputFields": [
+ { "name": "summary", "type": "text", "isRequired": true }
+ ],
+ "tools": [
+ { "type": "builtin", "id": "tool_fail", "builtin_tool_name": "fail" }
+ ]
+ }
+ ```
- ```typescript
- tools: [
- {
- type: "integration",
- id: "<stable-unique-tool-id>",
- selected_api: "<discovered-versioned-implementation>",
- action_key: "<discovered-action-key>",
- action_type: "<discovered-action-type>",
- display_name: "<discovered-app-and-action-name>",
- description: "<discovered-action-description>",
- app_needs_auth: true,
- auth_id: 123456, // Illustrative only; replace with the discovered numeric connection ID.
- inputs: {
- query: { type: "guess" },
- status: { type: "locked", value: "open" },
- },
- input_labels: { status: "Open" },
- approval_policy: "always_allow",
+ Replace sample context with runtime values from the trigger or prior steps; JSON-stringify structured context into a named `inputFields` string when needed. `includeWorkflowData: false` keeps this example independent of automatic Zap history; it does not remove the explicit inputs.
+
+ `outputFields` entries use `name`, optional `description`, `type`, and `isRequired`. For an enum, use `type: "category_single"` or `"category_multi"` with nonempty `options`, not `text` plus options. With the example above, read `result.data[0].summary` in the SDK. With no configured output fields, read `result.data[0].output`; `outputFields: []` also requests that default and overrides legacy `outputSchema`. Leave `isOutputArray` false/omitted for this basic recipe. Verify the actual result wrapper on your surface.
+
+ ## Complete integration-tool example
+
+ This example is for a user who explicitly wants the AI to decide whether an urgent note warrants a Slack notification and has approved that bounded send without per-call review. Otherwise, return the decision/message as output and let a normal workflow step send it.
+
+ The Slack identifiers, connection UUID, and channel ID are illustrative. Replace them with authorized discovery results before saving or running; never copy the example UUID into a real configuration. The channel is locked, while the runtime agent composes the text.
+
+ ```json
+ {
+ "model_id": "premium/auto",
+ "authentication_id": "0",
+ "instructions": "Read the note. Only if it describes an urgent launch blocker, use the configured Slack tool to send one concise notification to its locked channel. Prefix the notification with :robot:. Otherwise, send nothing. Return what actually happened. If a required send fails, use fail rather than claim it was sent.",
+ "inputFields": {
+ "note": "Launch is blocked: the production health checks are failing."
},
- ]
+ "includeWorkflowData": false,
+ "outputFields": [
+ { "name": "outcome", "type": "text", "isRequired": true }
+ ],
+ "tools": [
+ {
+ "type": "integration",
+ "id": "tool_notify",
+ "selected_api": "SlackCLIAPI",
+ "action_key": "channel_message",
+ "action_type": "write",
+ "display_name": "Slack: Send Channel Message",
+ "description": "Send a notification to the configured Slack channel.",
+ "app_needs_auth": true,
+ "connection_id": "00000000-0000-4000-8000-000000000002",
+ "inputs": {
+ "channel": { "type": "locked", "value": "C0123456789" },
+ "text": { "type": "guess" }
+ },
+ "approval_policy": "always_allow"
+ },
+ { "type": "builtin", "id": "tool_fail", "builtin_tool_name": "fail" }
+ ]
+ }
```
- The placeholders illustrate shape only; every concrete value must come from the selected account's current app, action, field, choice, and connection results. Set `app_needs_auth: true` when a selected connection proves the app requires authentication, `false` when the surface identifies it as connectionless, or `null` when the surface does not expose that metadata; never guess `false`. Configure each relevant action field deliberately:
+ Use the same shape for a discovered integration read/search action, with its own action type, field keys, choices, and connection. Do not configure the AI to search or send through a tool whose discovered capability does something else.
- - `locked` — a fixed user-supplied or resolved value.
- - `guess` — a value the model must choose at runtime.
- - `guess-locked` — the model may choose only from the supplied values.
- - `default` — use the action's declared default.
- - `ignore` — deliberately omit the field.
+ ## Web requests: include both web tools
- Do not leave a search tool's `inputs` empty and expect instructions to supply fields the tool schema never exposed. Set an explicit `approval_policy`: use `always_allow` for an approved read/search capability; keep consequential writes outside AI by Zapier by default, or use `require_approval` when an embedded write genuinely needs review.
+ When the user asks the step to search the public web or research/read public pages, include both `web_search` and `web_scrape`, plus `fail`. `web_search` finds results and links; `web_scrape` retrieves page content. These are builtin tools, not integration actions: no app identifier, connection, or integration input policies belong on them. The runtime agent supplies queries and URLs from the instructions/context.
- Normal workflow actions use connection aliases. An integration tool's numeric `auth_id` is a narrow schema-owned exception that does not accept a workflow alias. Use the selected connection's numeric ID from discovery. Use `auth_id: 0` only when discovery says the embedded app is connectionless, never for authenticated apps.
+ ```json
+ {
+ "model_id": "premium/auto",
+ "authentication_id": "0",
+ "instructions": "Search the public web for the requested topic, read relevant pages, and summarize the findings with source URLs. Search snippets alone are not proof that a page was read. If required evidence cannot be retrieved, use fail rather than inventing it.",
+ "inputFields": {
+ "topic": "Recent public announcements about the user's named company"
+ },
+ "includeWorkflowData": false,
+ "outputFields": [
+ { "name": "research", "type": "text", "isRequired": true }
+ ],
+ "tools": [
+ { "type": "builtin", "id": "tool_web_search", "builtin_tool_name": "web_search" },
+ { "type": "builtin", "id": "tool_web_scrape", "builtin_tool_name": "web_scrape" },
+ { "type": "builtin", "id": "tool_fail", "builtin_tool_name": "fail" }
+ ]
+ }
+ ```
- ## Configure builtins and knowledge sources
+ The two explicit web entries are the underlying pair represented by the web-browsing shortcut; do not add a second shorthand entry or other builtins. When combining web and integration actions, merge their arrays with unique IDs and keep only one `fail`. These web tools do not access private app data or replace an authenticated integration tool. Do not add them for ordinary summarization of data already supplied.
- Use only builtin names accepted by the live `tools` schema. Prefer its concrete `web_search` and `web_scrape` capabilities for durable source; do not assume the editor's `browse_web` shorthand is itself a runtime builtin. Builtins still need unique stable `id` values.
+ ## Verify and return
- Knowledge sources are separate from integration tools. Resolve the user's named source through the available surface and pass its exact returned configuration in `knowledgeSources`; never pass a raw ID in place of that configuration. Modern entries are objects containing `integrationId`; string entries are legacy encoded objects, not plain IDs.
+ Return to the workflow lifecycle after configuring the AbZ step. Confirm real side effects before tests, use only advertised test/run capabilities, and inspect the actual returned fields and tool outcomes. A processing response, pending approval, or failed tool call is not success. Never claim a message was sent or a page read without execution evidence.
- If the surface cannot resolve a requested tool connection, action field, model, or knowledge source, report the limitation instead of guessing.
+ If discovery cannot supply a required authorized app connection, action key, field, or choice, report that specific limitation. This fallback fills in the basic AbZ object contract; it does not grant access, create connections, provide interactive execution controls, or guarantee model quality.
## Return to the workflow lifecycle
After the AI step is configured, return to the loaded workflow skill. Test it inside the complete durable source so runtime `inputFields`, embedded tools, output shape, deterministic continuation, and side effects are exercised together.