Skip to main content
Output workflows can invoke any app a user has authorized in their Zapier account without writing OAuth or refresh-token code. The Zapier SDK resolves the user’s existing connection and exposes typed actions; Output wraps each call in a step() so it’s traced, retried, and safe to replay.

What it enables

  • Access hundreds of third-party apps (Slack, Gmail, Airtable, HubSpot, …) through a single SDK.
  • Zero OAuth code — Zapier injects tokens from the user’s stored connection.
  • Two ways to execute actions: a unified runAction() entry point, or dot notation to access app-specific methods.
  • Machine credentials that run from a headless worker, not a browser.

When to use

  • You need to send, read, or update data in an app a user has already authorized in Zapier.
  • You want to skip managing OAuth tokens and refresh flows yourself.
  • Your workflow should tolerate transient 5xxs and retry without double-posting.
  • You need the Zapier call traced alongside LLM and other steps in the same Output execution.

Install & authenticate

Visit https://zapier.com/app/assets/connections and create connections to the apps you want to use. Zapier App Connections page listing authorized Slack workspaces Then install the Zapier SDK:
Your package.json must set "type": "module" — both SDKs are ESM. Authenticate once locally and register the apps you want to call:
Adding an app installs a typed system, so you can access it through dot notation with typed return values.

Machine credentials for the worker

The Output worker runs in Docker with no browser. Create machine credentials and store them in Output’s encrypted credential store:
You also need to update your .env:

Shared Zapier client

Put the configured SDK in its own file under src/clients/ so every step imports the same instance — matching the API client pattern used elsewhere in Output (e.g. src/clients/jina.ts):
src/clients/zapier.ts

Schemas

Define every step and workflow boundary in types.ts so the same shapes are reused across files instead of being inlined into each z.object(...):
types.ts

Calling a Zapier action from a step

Each authorized app shows up on the App Connections page in Zapier — that’s what findFirstConnection queries under the hood: Put every Zapier call inside a step() so it’s traced and retried. Resolve the user’s connection, then invoke the action through the typed dot-notation API:
steps.ts
Write the prompt to generate the message:
A missing connection throws FatalError — retrying won’t help until the user authorizes Slack in Zapier. Transient 5xxs from Slack or Zapier bubble up and Output’s step retry policy handles them automatically.

Other methods to call Zapier Actions

You can also invoke the action using this syntax:
Always destructure { data } from the response — the Zapier SDK returns a wrapped envelope.
For endpoints with no built-in action, fall back to zapier.fetch() — it injects the user’s credentials and returns a standard Response:
Direct API calls bypass your org’s action restriction policies. See Zapier’s docs.

Orchestrating with a workflow

Keep the workflow pure — it only sequences step calls. No SDK clients, no fetch, no Date.now:
workflow.ts
Define a scenario file so runs are reproducible and reviewable in PRs:
src/workflows/slack_broadcast/scenarios/happy_message.json
Run the workflow against that scenario:
The Output trace captures each step independently, including the full Zapier request and response inside postToSlackChannel.