# Get Action Details
post /api/v2/apps/{app}/actions/{action}/
Get details of a specific action, including its needs, gives, and a sample of the action.
# Get Prefill Choices
post /api/v2/apps/{app}/choices/{prefill}/
Get prefill choices for an app's prefill.
# Search Actions
get /api/v2/apps/{app}/actions/
Search for Zapier actions by app, ordered by relevancy.
# Create AI Action
post /api/v2/ai-actions/
Create a new AI Action that can be executed by the user later.
# Delete AI Action
delete /api/v2/ai-actions/{ai_action_id}/
Delete an AI Action.
Returns `true` if the action was deleted, `false` if it was not found.
# Get AI Action
get /api/v2/ai-actions/{ai_action_id}/
Get the details of a specific AI Action.
# List AI Actions
get /api/v2/ai-actions/
List all the current actions for the current user.
# Update AI Action
put /api/v2/ai-actions/{ai_action_id}/
Update an existing AI Action.
# Get App Details
get /api/v2/apps/{app}/
# List Authentications For App
get /api/v2/apps/{app}/auths/
Get list of Zapier authentications associated with the requesting user and account.
# Search Apps
get /api/v2/apps/search/
# Check User Auth
get /api/v2/auth/check/
Test that the API and auth are working.
# Get Account List
get /api/v2/auth/accounts/
Get a list of Zapier accounts for the current user.
# Get Oauth Login Link
get /api/v2/auth/oauth-login-link/
This will create a link that can be used for "quick account creation" followed by OAuth login for a user.
When going to this link, if the provided email already has a Zapier account, the user will be asked to log in.
If the email does not have a Zapier account, the user will be asked to create an account with the provided email and name.
Upon creating an account or logging in, the user will be taken to
The user will get a Zapier account, be brought to AI Actions where they will see the OAuth consent screen, and then will be brought back to your site
with an OAuth `code` that can be used with `https://actions.zapier.com/oauth/token/` with the following data in the body with `Content-Type: application/x-www-form-urlencoded`:
- client_id: Your AI Actions OAuth client ID
- grant_type: `authorization_code`
- code_verifier: The verifier stored for your user when generating the login URL
- redirect_uri: The same URL you used for the `redirect_uri` in the previous step.
- code: The code in the query parameters of the URL that the user was redirected to.
# Get User Login Link
get /api/v2/auth/login-link/
This will create a link that can be used for "quick account creation" for a user.
When going to this link, if the provided email already has a Zapier account, the user will be asked to log in.
If the email does not have a Zapier account, the user will be asked to create an account with the provided email and name.
Upon creating an account or logging in, the user will be taken to the `redirect_to` URL.
A suggested flow for this is:
- Check if you have an AI Actions OAuth token for a user
- If you do not, send the user to a page on your site for initiating PKCE OAuth flow, storing the `code_verifier` for them securely.
- Use this endpoint, with a `redirect_to` of `https://actions.zapier.com/oauth/authorize/` with the following query parameters:
- client_id: Your AI Actions OAuth client ID
- scope: `openid nla:exposed_actions:execute`
- response_type: `code`
- redirect_uri: The URL on your site that will handle the OAuth callback, must be in the list of allowed redirect URIs for your AI Actions OAuth client.
- code_challenge: The SHA256 hash of the `code_verifier` from the previous step.
- code_challenge_method: `S256`
The user will get a Zapier account, be brought to AI Actions where they will see the OAuth consent screen, and then will be brought back to your site
with an OAuth `code` that can be used with `https://actions.zapier.com/oauth/token/` with the following data in the body with `Content-Type: application/x-www-form-urlencoded`:
- client_id: Your AI Actions OAuth client ID
- grant_type: `authorization_code`
- code_verifier: The verifier stored for your user when generating the login URL
- redirect_uri: The same URL you used for the `redirect_uri` in the previous step.
- code: The code in the query parameters of the URL that the user was redirected to.
# Execute Stateless AI Action
post /api/v2/execute/
# Execute Stored AI Action
post /api/v2/ai-actions/{ai_action_id}/execute/
Given an action ID and instructions, this will execute the action and return the results.
# Preview Stored AI Action
post /api/v2/ai-actions/{ai_action_id}/preview/
# Rate Execution Log
post /api/v2/execute/log/{execution_log_id}/rate/
Rate a specific execution log given feedback from the user.
Rating should either be `-1` (bad, thumbs down), `0` (neutral, the default), or `1` (good, thumbs up).
You can also provide plain text feedback provided by the user. Supplied rating/feedback are patched onto execution log, so you can progressively call this endpoint.
# App Health Check
get /api/v2/health/
Check that the app is up and running.
# Guess Actions
post /api/v2/guess-actions/
# Shrink Result
post /api/v2/shrink-result/
**WARNING: This endpoint is not officially supported, and is subject to be changed or removed at any time.**
Shrinks the given result payload to git into a specified token budget.
This tries its best to keep around more important values and truncate less important ones.
The intent of this endpoint is that after executing an AI Action, you can pass the result into this to get a smaller result that can be passed in to LLMs without hitting the token limit.
# API Key
# API Logs
# OpenAPI Specification
# Submit an issue
# Join our Community
# Zapier account list
get /api/v2/auth/accounts/
Get a list of Zapier accounts for the current user.
Each user can have multiple Zapier accounts. This endpoint returns a list of all the accounts that the user has access to.
When interacting with the AI Actions API, you will need to provide a specific account ID to most endpoints.
# Get action details and needs
post /api/v2/apps/{app}/actions/{action}/
Get details of a specific action, including its needs, gives, and a sample of the action.
"Needs" are the values that need to be provided when the action is run. Needs that are not provided will be guessed by the AI when the action is run.
"Gives" are the values that the action returns when it is run.
"Sample" is a sample of what the action returns when it is run.
When you [Run a stateless action](/ai-actions/how-tos/stateless/run-stateless-action), "needs" are provided to the endpoint. For each need, you can either have AI guess a value for it or you can ask the user to provide a specific value.
## Custom fields
The list of needs that this returns can change based on the `params` provided in the request body.
As such, this endpoint may need to be hit multiple times as needs are chosen by the user.
This endpoint needs to be hit again whenever the selected value for a need changes and *any of the following* are true:
* The changed need has `alters_custom_fields` set to `true`
* The changed need is in the `depends_on` array of another need
* The changed need has `custom_field` set to `true`
As an example, when using the `GoogleSheetsV2API` API with the `add_row` action, the first time you get the action you will get back these `action_needs`:
```json
{
"action_needs": [
{
"key": "spreadsheet",
"label": "Spreadsheet",
"prefill": "spreadsheet.id.title",
"custom_field": false,
"depends_on": [],
"alters_custom_fields": true
},
{
"key": "worksheet",
"label": "Worksheet",
"prefill": "worksheet.id.title",
"custom_field": false,
"depends_on": ["spreadsheet"],
"alters_custom_fields": true
}
]
}
```
In order to get the column names of the spreadsheet, you'll have to get the prefill choices. See [Get Prefill Choices](/ai-actions/how-tos/action-info/prefill-choices) for more information.
In this example scenario, after getting values for the `spreadsheet` and `worksheet` prefills, another call can be made to this endpoint.
This time, in the body of the `POST`, we can include the selected values:
```json
{
"params": {
"spreadsheet": "SOME_SPREADSHEET_ID", // this is the ID of a spreadsheet in Google Sheets
"worksheet": "0" // the first worksheet in the spreadsheet
}
}
```
Now, the action details will include the columns from the spreadsheet as custom fields:
```json
{
"action_details": [
// ... same results as above, in addition to...
{
"key": "COL$A",
"label": "My first column",
"prefill": null,
"custom_field": true,
"depends_on": [],
"alters_custom_fields": null
},
{
"key": "COL$B",
"label": "My second column",
"prefill": null,
"custom_field": true,
"depends_on": [],
"alters_custom_fields": null
},
{
"key": "COL$C",
"label": "My third column",
"prefill": null,
"custom_field": true,
"depends_on": [],
"alters_custom_fields": null
}
]
}
```
## Choices
If the need has a `choices` array, then the value being chosen should come from that list.
Often, you will see this with `boolean` fields.
For example, the `SlackAPI` API's `direct_message` action contains the following need (irrelevant fields removed):
```json
{
"key": "as_bot",
"type": "boolean",
"label": "Send as a bot?",
"choices": [
{
"key": "no",
"label": "No"
},
{
"key": "yes",
"label": "Yes"
}
],
"default": "yes",
"format": "SELECT"
}
```
## Other field types
See [Field definitions and types](/platform/build/field-definitions) in the Zapier Platform documentation for details on the other types of fields that a need can have.
# Choose authentication
get /api/v2/apps/{app}/auths/
Get list of Zapier authentications associated with the requesting user and account.
Authentications for each app can be managed [in your Zapier account](https://zapier.com/app/connections).
# Find an action
get /api/v2/apps/{app}/actions/
Search for Zapier actions by app, ordered by relevancy.
# Find an app
get /api/v2/apps/search/
# Introduction
When using AI Actions, you will first have to build up information about the action that you want to run.
## When do I need to gather action information?
See [Stored vs. Stateless Actions](/ai-actions/how-tos/stateless-vs-stored) for more information.
You do **not** need to gather this information if:
* You're running a **stored action** that is configured in the AI Actions UI
You **do** need to gather this information if:
* You're running a **stateless action**
* You're setting up a new stored action using the stored action management API
## Gathering action information
Each action lives under an app, for example Google Sheets or Slack.
You first need to choose which app you will want to use to run an action.
See [Find an app](/ai-actions/how-tos/action-info/find-app) for more details.
Once you know the app you want to use, you can choose which action you want to run for that app.
See [Find an action](/ai-actions/how-tos/action-info/find-action) for more details.
Every app has authentications that are configured in Zapier for each user.
To run an action, you need to know which Zapier account and app authentication that you want to run the action with.
See [Choose authentication](/ai-actions/how-tos/action-info/choose-auth) for more details.
Each action has a number of "needs" that can be used to decide what values to run the action with.
See [Get action details and needs](/ai-actions/how-tos/action-info/action-details) for more details.
Some of these needs are have a `prefill` key, which means that they provide a list of values that can be chosen from.
See [Get prefill choices](/ai-actions/how-tos/action-info/prefill-choices) for more details.
**You don't have to get a value for every need!**
When running an action, you can have the AI guess values based on instructions.
Once you have all of the details you need, you can either run the action as a stateless action, or save the details for later.
See [Stored vs Stateless Actions](/ai-actions/how-tos/stateless-vs-stored) for more information.
# Get prefill choices
post /api/v2/apps/{app}/choices/{prefill}/
Get prefill choices for an app's prefill.
If a need that comes back from the [Get Action Details](/ai-actions/how-tos/action-info/action-details) endpoint has a `prefill` key, then this endpoint can be used to get the choices for that prefill.
## Dependent needs
If a need has any values in its `depends_on` array, the values for each of the needs in `depends_on` *must* be included in the `params` in the body of the `POST` to get prefill choices.
As an example, the `action_needs` list for the `add_row` action for the `GoogleSheetsV2API` app from [Get action details and needs](/ai-actions/how-tos/action-info/action-details) includes a `spreadsheet` and a `worksheet` need:
```json
{
"action_needs": [
{
"key": "spreadsheet",
"label": "Spreadsheet",
"prefill": "spreadsheet.id.title",
"depends_on": []
},
{
"key": "worksheet",
"label": "Worksheet",
"prefill": "worksheet.id.title",
"depends_on": ["spreadsheet"]
}
]
}
```
This means that, in order to get the `worksheet.id.title` prefill choices, you need to include a `spreadsheet` value in the request to this endpoint:
```json
{
"params": { "spreadsheet": "SOME_SPREADHSEET_ID" }
}
```
`SOME_SPREADHSEET_ID` would have come from a call to get prefill choices for the `spreadsheet.id.title` value.
## Caching
By default, responses from this endpoint for a given `app`, `prefill`, `params`, and user will be cached for 24 hours.
When this endpoint is first hit, a background job is started that will paginate through all of the available choices.
While this background job is running, the results from this endpoint will return with `"is_loading": true`.
If `is_loading` is `true`, then the endpoint should be polled every \~1 second until `is_loading` is `false`, at which point all of the available results are returned.
The time that the cache was last updated is returned with `last_refresh_seconds_ago`.
To force a refresh of the cache, the `force_refresh` query parameter can be set to `true`.
`force_refresh` should only be used when the choices are expected to have changed, and should only be used with a single request.
Repeated reqeusts with `force_refresh` set to `true` will cause results to continuously load.
# Authentication
get /api/v2/auth/check/
Test that the API and auth are working.
# Introduction
There are two methods that can be used to authenticate with `@zapier/ai-actions`:
* [API Key](#api-key): If you plan on only using the AI Actions client to interact with AI Actions on your behalf
* [OAuth](#oauth): If you plan on creating an OAuth app to allow users to interact with AI Actions on their behalf
If you plan on using the interactive playground available in this
documentation, then an API key must be user.
## API Key
Follow the steps below to create your API key:
1. [Log in](https://actions.zapier.com/custom/start/) or [sign up](https://zapier.com/sign-up) to Zapier.
2. [Connect](https://actions.zapier.com/custom/actions/) your Zapier account to the `custom` integration app.
3. Your API key can be retrieved from the [Credentials page](https://actions.zapier.com/credentials/).
**Treat your API key like a password.**
It can be used to run your AI Actions.
For example: if you set up a "Gmail: Find email" action, anyone with your API key can read all your email.
## OAuth
### Creating an OAuth app
Click here to create a new OAuth app to use for authenticating with AI
Actions.
In order to work with the `@zapier/ai-actions` API client, **your app must
have "Public Client" checked.**
### Authenticating with OAuth
If you're using JavaScript/TypeScript, you can use the `@zapier/ai-actions` library to handle authentication for you!
[See the `@zapier/ai-actions` documentation here](/ai-actions/libraries/nodejs/authentication#getting-an-oauth-token)
### Getting a token
Your OAuth client can be used with the [PKCE flow](https://www.oauth.com/playground/authorization-code-with-pkce.html) to authenticate with AI Actions using a `Bearer` token.
To begin this process, you first need to generate a code verifier and a code challenge.
Here is some sample code in TypeScript to do this:
```ts
/*
* https://nodejs.org/api/crypto.html#cryptogetrandomvaluestypedarray
*
* Store the verifier securely, as it will be needed later
*
* @returns Verifier to use when getting a token
*/
const generateVerifier = () => {
const array = new Uint32Array(28);
crypto.getRandomValues(array);
return Array.from(array, (item) => `0${item.toString(16)}`.slice(-2)).join(
""
);
};
/** Base64 URL encode a buffer */
const base64URLEncode = (buffer: ArrayBuffer) => {
const uint8Array = new Uint8Array(buffer);
const base64String = btoa(String.fromCharCode(...uint8Array));
return base64String
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
};
/**
* @param verifier Verifier from `generateVerifier`
* @returns Base64 URL encoded SHA-256 hash of the verifier
*/
const generateCodeChallenge = async (verifier: string) => {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
return base64URLEncode(hashBuffer);
};
```
Make sure to store the verifier securely! It will be needed when returning to
your site after authenticating with AI Actions
You can then send the user to AI Actions to see your OAuth consent screen:
```
https://actions.zapier.com/oauth/authorize?
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_REDIRECT_URI&
scope=openid%20nla%3Aexposed_actions%3Aexecute&
response_type=code&
code_challenge=YOUR_CODE_CHALLENGE&
code_challenge_method=S256
```
After the user has authorized your app, they will be redirected to the `redirect_uri` you provided with a `code` query parameter.
You can then exchange this code for a token, using the `verifier` that you generated previously:
```sh
curl -X POST "https://actions.zapier.com/oauth/token/" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=YOUR_CLIENT_ID" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code_verifier=YOUR_VERIFIER" \
--data-urlencode "redirect_uri=YOUR_REDIRECT_URI" \
--data-urlencode "code=YOUR_CODE"
```
This will return the following JSON:
```ts
interface OAuthAccessToken {
/** JWT with user info */
id_token: string;
/** Token that can be used with the `Authorization: Bearer ...` header */
access_token: string;
/** Token that can be used to get a new access token */
refresh_token: string;
/** Number of seconds until the token expires */
expires_in: number;
token_type: "Bearer";
scope: "openid nla:exposed_actions:execute";
}
```
The `access_token` can then be used to make API calls to AI Actions:
```sh
curl -X GET "https://actions.zapier.com/api/v2/auth/check" -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
### Quick account creation
Since your users may not already have a Zapier account, we offer a quick account creation flow that allows users to create a Zapier account and connect their account to your app in one step.
To use this flow, first generate the `/oauth/authorize` URL as outlined above.
Then, get the account creation URL for your OAuth client:
```sh
https://actions.zapier.com/api/v2/auth/login-link/?
redirect_uri=OAUTH_AUTHORIZE_URL&
sign_up_first_name=USER_FIRST_NAME&
sign_up_last_name=USER_LAST_NAME&
sign_up_email=USER_EMAIL
```
This will return the following JSON:
```ts
{
"login_link": "https://zapier.com/....."
}
```
When you send a user to the provided `login_link`, they will go through a quick Zapier account creation flow. If the provided email address is already associated with a Zapier account, they will be asked to log in.
Users will receive a follow-up email from Zapier to confirm their email address and to let them set a password for the account.
The user will then see the AI Actions OAuth consent screen and be redirected back to your `redirect_uri` with a `code` query parameter, which can be exchanged for an access token as outlined above.
### Refreshing tokens
After `expires_in` seconds, the `access_token` will expire. To get a new token, you can use the `refresh_token` that was returned when you got the original token:
```sh
curl -X POST "https://actions.zapier.com/oauth/token/" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=YOUR_CLIENT_ID" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=YOUR_REFRESH_TOKEN"
```
This will return a new OAuth token that can be used in an `Authorization: Bearer ...` header.
# Stored Vs. Stateless Actions
When using the AI Actions API, there are two ways that actions can be run: stored and stateless.
## Stored Actions
With stored actions, you let us handle storing all of the details about an action.
You will only need to provide the user instructions and parameter hints when running the action.
Stored actions are configured ahead of time, either using the AI Actions UI or
the provided management endpoints.
Only user instructions and parameter hints are needed when running them.
A `@zapier/ai-actions-react` package is available to make it easy to interact
with stored actions from a React application.
## Stateless Actions
With stateless actions, you provide all of the necessary parameters at runtime.
This means you will have to store action details yourself or generate the action every time you want to run it.
Stateless actions are run by providing all the necessary parameters at
runtime.
This gives you greater control over how actions are run.
# Stateless actions introduction
When running a stateless action, all of the parameters that are needed for it to run are provided at runtime.
See [Gathering Action Information](/ai-actions/how-tos/action-info/introduction) for details on how to build up the needed values that are required
See [Run a Stateless Action](/ai-actions/how-tos/stateless/run-stateless-action) for information on actually running the action.
# Run a Stateless Action
post /api/v2/execute/
See `params_constraints` in the documentation below for details on how to tell the AI which values to use and which to guess when running the action.
See [Get action details and needs](/ai-actions/how-tos/action-info/action-details) for how to discover the needs for an action.
# Configure stored actions in the AI Actions UI
*Planned improvements to stored actions*
While stored actions can only currently be configured on the AI Actions domain, work is planned to provide APIs and components that allow you to embed action configuration in your own UI.
Actions can be configured by you at a specific URL or with our quick configuration popup.
The setup URL is the same for everyone: `https://actions.zapier.com//start/`
You'd typically include a button or link somewhere to open the setup window. Here's an example of how you might do that:
```javascript
var actions = window.open('https://actions.zapier.com//start', 'actions', 'width=650,height=700');
```
# Stored actions introduction
Running a stored action involves setting up the action in the AI Actions UI and then running it by its ID.
This is done in the following steps:
Actions need to be configured by a user before they can be used.
There are two ways to set up stored actions:
1. Using the AI Actions website- see [Configure stored actions](/ai-actions/how-tos/stored/action-configuration) for more details.
2. Using the AI Action management API provided under [Manage stored actions](/ai-actions/how-tos/stored/manage/create-action).
After actions are set up, they can be provided as tools to an LLM.
See [List stored Actions](/ai-actions/how-tos/stored/list-actions) for more details.
The LLM can then decide which action it wants to run.
See [Run a stored action](/ai-actions/how-tos/stored/run-stored-action) for more details.
# List stored Actions
get /api/v2/ai-actions/
List all the current actions for the current user.
These actions can then be provided as tools to an LLM, which can decide what parameters to use when running the action.
See [Run a stored action](/ai-actions/how-tos/stored/run-stored-action) for details on running an action.
# Create stored action
post /api/v2/ai-actions/
Create a new AI Action that can be executed by the user later.
# Delete stored action
delete /api/v2/ai-actions/{ai_action_id}/
Delete an AI Action.
Returns `true` if the action was deleted, `false` if it was not found.
# Get stored action
get /api/v2/ai-actions/{ai_action_id}/
Get the details of a specific AI Action.
See [Run a stored action](/ai-actions/how-tos/stored/run-stored-action) for details on running an action.
# Update stored action
put /api/v2/ai-actions/{ai_action_id}/
Update an existing AI Action.
Partial updates are not currently supported.
Use the [Get AI Action](/ai-actions/how-tos/stored/manage/get-action) endpoint to get the action details first.
# Run a stored action
post /api/v2/ai-actions/{ai_action_id}/execute/
Given an action ID and instructions, this will execute the action and return the results.
# Introduction
AI Actions exposes Zapier's 7,000+ apps and 30,000+ actions, for selected partners, to build your own custom AI assistants.
You can use the API to:
* Send messages in Slack
* Add a row to a Google Sheet
* Draft a new email in Gmail
* And thousands of other app actions, all with one universal natural language API.
AI Actions is optimized for receiving user input in natural language like chat, assistant, or other large language model based experience. Think of AI Actions as a more human-friendly integrations API.
## Integrations made easy
AI Actions contains a decade of Zapier's experience in solving API shenanigans, so you don't have to.
Here are some common API complexities, automatically handled by AI Actions.
* **Support for every type of auth**: Zapier securely handles and signs requests for you including Basic, Session, API Key, OAuth v1, Oauth v2, and Digest.
* **Support for create, update, and search actions**: With endpoints optimized for natural language usage.
* **Support for custom fields**: Spreadsheet, CRM, and mailing list friendly.
* **Reference by name, not ID**: Humans use natural language names, not IDs, to reference things in their apps, so AI Actions does too.
* **Smart, human defaults**: APIs sometimes have 100 options. Zapier's platform data helps us make AI Actions simpler for users out of the box.
## API design notes
Here are a few design choices we've made with this API.
* Let individual users control access to their actions and accounts.
* Minimize the amount of work users must do to set up and run actions.
* Let users override the AI when desired.
# Authentication
# Introduction
There are two methods that can be used to authenticate with `@zapier/ai-actions`:
* [API Key](#api-key): If you plan on only using the AI Actions client to interact with AI Actions on your behalf
* [OAuth](#oauth): If you plan on creating an OAuth app to allow users to interact with AI Actions on their behalf
## API Key
Your API key can be retrieved from the [Credentials page](https://actions.zapier.com/credentials/).
**Treat your API key like a password.**
It can be used to run your AI Actions.
For example: if you set up a "Gmail: Find email" action, anyone with your API key can read all your email.
Create an `AiActions` client with the API key:
```ts
import { AiActions } from "@zapier/ai-actions";
const client = new AiActions({
auth: {
apiKey: "YOUR_API_KEY",
},
});
```
## OAuth
### Creating an OAuth app
Click here to create a new OAuth app to use for authenticating with AI
Actions.
In order to work with the API client, **your app must have "Public Client"
checked.**
### Getting an OAuth token
#### Creating the `AiActionsAuth` object
`@zapier/ai-actions` provides a helper class to handle the auth process.
This is done with the `AiActionsAuth` class:
```ts
import { AiActionsAuth } from "@zapier/ai-actions";
const aiActionsAuth = new AiActionsAuth({
clientId: "YOU_APP_CLIENT_ID",
redirectUri: "YOUR_APP_REDIRECT_URI",
});
```
#### Storing the code verifier securely
`@zapier/ai-actions` uses the [OAuth 2.0 PKCE flow](https://www.oauth.com/playground/authorization-code-with-pkce.html) to facilitate the OAuth process. As part of this, a "code verifier" is generated.
By default, `AiActionsAuth` will store the verifier in the browser's
`localStorage`.
**This is not recommended for production use** since it can be easily accessed
by malicious scripts, and it will not work server-side.
To facilitate storing the code verifier securely, the constructor for `AiActionsAuth` has options that can be used.
```ts
import { AiActionsAuth } from "@zapier/ai-actions";
const aiActionsAuth = new AiActionsAuth({
clientId: "YOU_APP_CLIENT_ID",
redirectUri: "YOUR_APP_REDIRECT_URI",
async storeVerifier(verifier) {
// store the verifier somewhere secure for your user (i.e. database or KV store)
},
async getVerifier(): Promise {
// this function MUST return the verifier stored for the user by `storeVerifier`
// each verifier can only be used once, and it must be consistend throughout the OAuth flow
},
});
```
#### Generating the authorization OR quick account creation URL
Once you have an `AiActionsAuth` object, you can use it to generate the authorization URL:
```tsx
const authUrl = await aiActionsAuth.generateAuthUrl();
// ...
Authenticate with AI Actions;
```
This URL can be used to redirect the user to the authorization page. Once the user has authorized your app, they will be redirected to the `redirectUri` you provided.
***
You can also generate a **quick account creation** URL. This will ensure that the user has a Zapier account before authorizing your app.
If they do not have a Zapier account, one will be created for them. If they already have a Zapier account, they will be asked to log in on zapier.com.
```tsx
const quickAccountCreationUrl = await aiActionsAuth.generateCreateAccountUrl(
"Firstname",
"Lastname",
"user@example.com"
);
// ...
Authenticate with AI Actions;
```
After going through this flow, they will end up back on the `redirectUri` that you provided.
#### Getting an access token
After authorizing your app, the user will be redirected to the `redirectUri` you provided along with a `code` query parameter.
This `code` can now be used to get an access token
```ts
import { AiActions } from "@zapier/ai-actions";
// `code` will be in the query params; get it however you like (most likely server-side)
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code");
// this access token can now be used to generate an AiActions client
const oauthToken = await aiActionsAuth.getToken(code);
const aiActionsClient = new AiActions({
auth: {
token: oauthToken.access_token,
},
});
try {
// this will pass if auth succeeds, or throw an error if it fails
await aiActionsClient.checkAuth();
} catch (e) {
console.error("Error checking AI Actions auth", e);
}
```
#### Refreshing the access token
Access tokens expire after a certain amount of time. To refresh the token, you can use the `refreshToken` method along with the `refresh_token`:
```ts
import {
OAuthAccessToken,
refreshOAuthToken,
tokenNeedsRefresh,
} from "@zapier/ai-actions";
// get the stored token for the current user; `getAiActionsToken` is a placeholder for your own function
const oauthToken: OAuthAccessToken = await getAiActionsToken();
if (tokenNeedsRefresh(oauthToken)) {
const refreshed = await refreshOAuthToken({
clientId: "YOUR_CLIENT_ID",
refreshToken: oauthToken.refresh_token,
});
if (refreshed) {
// `setAiActionsToken` is a placeholder for your own function
await setAiActionsToken(refreshed);
}
}
```
# Getting started
### **The `@zapier/ai-actions` package is still in development!**
If you want to check it out, please [contact us](https://nla.zapier.app/_z/embed/page/clh5sdteo0001ml0pdz8a2aqr?&)
The `@zapier/ai-actions` library makes it easy to interact with AI Actions from the client or the server.
The library includes an API client, Typescript types, and utilities to make authentication easy.
## Installation
To install the library, run:
```bash
# npm
npm install @zapier/ai-actions
# pnpm
pnpm add @zapier/ai-actions
# yarn
yarn add @zapier/ai-actions
```
## Quick start
[Get your API Key here](https://actions.zapier.com/credentials/)
Create a new `AiActions` client with your API key:
```ts
import { AiActions } from "@zapier/ai-actions";
const aiActionsClient = new AiActions({
auth: {
apiKey: "sk-YOUR_API_KEY",
},
});
```
Use the client:
```ts
const actionList = await aiActionsClient.getActionList();
actionList.results.forEach((action) => {
console.log(action.id, action.description);
});
```
## Next steps
Set up OAuth to allow users to interact with AI Actions on their behalf
Check out some examples of how to use the library
Set up the library with Next.js
See how to use the React component library
# Next.js
This page details recommended ways to use the `@zapier/ai-actions` library with [Next.js](https://nextjs.org/).
The examples here are using the [Next.js app router](https://nextjs.org/docs/app).
For storage, these examples are using [Vercel KV](https://vercel.com/docs/storage/vercel-kv) but any other form of server-side storage can be used.
It's assumed that your Next.js app also has authentication set up, using a library such as [NextAuth.js](https://next-auth.js.org/).
# Set up environment variables
After [generating your OAuth client](/ai-actions/libraries/nodejs/authentication#getting-an-oauth-token), you can set your client ID and redirect URL inside of your `.env` file:
```sh .env
NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_CLIENT_ID=nla-YOUR_CLIENT_ID
NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_REDIRECT_URI=https://example.com/ai-actions-auth-callback
```
The `NEXT_PUBLIC_` prefix ensures that they can be referenced client-side.
# Handle authentication server-side
Authentication with AI Actions can be handled fully server-side.
```ts @/lib/ai-actions/server.ts
import "server-only"; // make sure this code only ever runs on the server
// using `@vercel/kv` as an example, but you can use any server-side storage
import { kv } from "@vercel/kv";
import {
AiActionsAuth,
OAuthAccessToken,
refreshOAuthToken,
tokenNeedsRefresh,
} from "@zapier/ai-actions";
/**
* @param email User's email
* @returns OAuth token to use for AI Actions API calls
*/
export async function getAiActionsToken(
email: string
): Promise {
// NOTE: You don't have to use email here; it can be any sort of user identifier
const token = await kv.hgetall<{ token: string }>(
`ai-actions-token:${email}`
);
if (!token?.token) {
return null;
}
const oauthToken = token.token as unknown as OAuthAccessToken;
if (tokenNeedsRefresh(oauthToken)) {
console.log(`Refreshing AI Actions token for ${email}`);
const refreshed = await refreshOAuthToken({
clientId: process.env.NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_CLIENT_ID!,
refreshToken: oauthToken.refresh_token,
});
if (refreshed) {
// see below; this just stores the token in the KV store
await setAiActionsToken(email, refreshed);
return refreshed;
}
}
// typescript thinks this is a string, but it's actually an object
return token.token as unknown as OAuthAccessToken;
}
/**
* @param email User's email
* @param token AI Actions OAuth token to set for the user
*/
export async function setAiActionsToken(
email: string,
token: OAuthAccessToken | null
) {
await kv.hset(`ai-actions-token:${email}`, { token: JSON.stringify(token) });
}
/**
* @param session Current user's session
* @returns AI Actions auth object to use for OAuth
*/
export const getAiActionsAuth = (email: string) =>
new AiActionsAuth({
clientId: process.env.NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_CLIENT_ID!,
redirectUri: process.env.NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_REDIRECT_URI!,
async storeVerifier(verifier) {
await kv.hset(`ai-actions-verifier:${email}`, { verifier });
},
async getVerifier(): Promise {
const key = `ai-actions-verifier:${email}`;
const verifier = await kv.hgetall<{ verifier: string }>(key);
// this can only be used once, so we delete it after getting it
await kv.hdel(key, "verifier");
return verifier?.verifier || "";
},
});
```
# Generating the authorization URL
Here is an example of a button that will initiate authentication with AI Actions, using the server-side code from above.
```tsx initiate-ai-actions-auth.tsx
"use server";
import { getAiActionsAuth } from "@/lib/ai-actions/server";
import { auth } from "@/auth";
import { Session } from "@/lib/types";
import Link from "next/link";
export const InitiateAiActionsAuth = async () => {
const session = (await auth()) as Session;
const aiActionsAuth = getAiActionsAuth(session.user.email);
const url = await aiActionsAuth.generateAuthUrl();
return (
Please authenticate with AI Actions to proceed
Authenticate with AI Actions
);
};
```
# Getting and storing the access token
To get the access token after the user has authenticated, a page needs to exist at `NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_REDIRECT_URI` that will handle the OAuth flow.
```tsx @/app/NEXT_PUBLIC_ZAPIER_AI_ACTIONS_OAUTH_REDIRECT_URI/page.tsx
import { auth } from "@/auth";
import { Session } from "@/lib/types";
import { getAiActionsAuth, setAiActionsToken } from "@/lib/ai-actions/server";
import { redirect } from "next/navigation";
/**
* The user will get redirected here after they login to Zapier/AI Actions.
*
* This will store the OAuth access token for the user.
*/
export default async function LoginSuccess({
searchParams,
}: {
searchParams: { code: string };
}) {
const { code } = searchParams;
const session = (await auth()) as Session;
const aiActionsAuth = getAiActionsAuth(session.user.email);
const response = await aiActionsAuth.getToken(code);
if (response) {
await setAiActionsToken(session.user.email, response);
} else {
console.error("Error getting AI Actions token", response);
return redirect("/error");
}
return redirect("/");
}
```
# Using the access token to create an `AiActions` client
Once the token is stored, it can be used to get an `AiActions` client that can be used to make API calls.
It can also be passed to the `AiActionsProvider` if you're using the `@zapier/ai-actions-react` library.
Make sure that the `refresh_token` for the OAuth token doesn't leave the
server
```tsx @/app/example-route/page.tsx
import { AiActions } from "@zapier/ai-actions";
import { Session } from "@/lib/types";
import { getAiActionsToken, setAiActionsToken } from "@/lib/ai-actions/server";
import { ExampleClientComponent } from "./example-component";
export default async function ExamplePage() {
const session = (await auth()) as Session;
let aiActionsToken = session?.user
? await getAiActionsToken(session.user.email)
: null;
if (aiActionsToken) {
const aiActionsClient = new AiActions({
auth: {
token: aiActionsToken.access_token,
},
});
try {
await aiActionsClient.checkAuth();
} catch (e) {
console.error("Error checking AI Actions auth", e);
aiActionsToken = null;
setAiActionsToken(session.user.email, null);
}
}
return ;
}
```
```tsx @/app/example-route/example-component.tsx
"use client";
import { AiActionsProvider, ActionList } from "@zapier/ai-actions-react";
export const ExampleComponent = ({
aiActionsToken,
}: {
aiActionsToken: string;
}) => {
return (
);
};
```
# Usage examples
# Introduction
The AI Actions API client can be used to interact with any of the API endpoints that AI Actions supports.
Below are a few examples of common uses for the client.
# Creating a client
See the [authentication guide](/ai-actions/libraries/nodejs/authentication) for more information on how to authenticate.
```ts
import { AiActions } from "@zapier/ai-actions";
const aiActionsClient = new AiActions({
auth: {
apiKey: "YOUR-API-KEY",
// or...
token: "OAUTH-TOKEN",
},
});
```
# Checking a user's authentication
This is useful if you want to check if a user's AI Actions OAuth token is valid.
```ts
try {
await aiActionsClient.checkAuth();
} catch (error) {
console.error("User is not authenticated", error);
}
```
# Error handling
The `@zapier/ai-actions` package exports an `AiActionsHttpError` type that can be used to get JSON from errors.
```ts
import { AiActionsHttpError } from "@zapier/ai-actions";
try {
await aiActionsClient
.executeAction
// ...
();
} catch (error) {
if (error instanceof AiActionsHttpError) {
const parsedError = await error.response.json();
console.error("Error executing action", parsedError);
}
}
```
# Listing a user's actions
Actions can be created or edited either on the [AI Actions website](https://actions.zapier.com/providers/) or using the [`@zapier/ai-actions-react` library](/ai-actions/libraries/react/getting-started)
```ts
const actionList = await aiActionsClient.getActionList();
actionList?.results.forEach((action) => {
// Each action has an ID that can be used to execute it, and it includes information about how the user
// has set it up, such as the description and the app it's associated with.
console.log(action.id, action.description); // "01EXAMPLE_ID Gmail: Find Email"
});
```
# Executig an action
See the [Run a stored action guide](/ai-actions/how-tos/stored/run-stored-action) for more information on the available parameters.
```ts
const result = await aiActionsClient.executeAction(
{
ai_action_id: "YOUR_ACTION_ID",
},
{
instructions: "Create a row with the following data....",
params: {
spreadsheet: {
mode: "guess",
// AI Actions will figure out the proper ID to use based on the hint,
// that way you don't need to worry about knowing IDs!
value: "My Spreadsheet",
},
},
preview_only: false,
}
);
console.log(result?.resolved_params, result?.full_results);
```
# Search for Zapier apps and actions
```ts
const appSearch = await aiActionsClient.searchApps({ query: "Google Sheets" });
appSearch?.results.forEach(async ({ name, app }) => {
console.log(name);
const actionSearch = await aiActionsClient.searchActions({
app,
// omit `query` to get all actions for the app
query: "add spreadsheet row",
});
});
```
# Give feedback on an action run
If an execution doesn't work properly, you can provide feedback on it to help us improve the AI.
```ts
const result = await aiActionsClient.executeAction({
// ...
});
if (result) {
await aiActionsClient.rateExecution({
execution_log_id: result.execution_log_id,
// -1: Negative feedback
// 0: Neutral feedback
// 1: Positive feedback
rating: -1,
feedback: "It didn't work for me!",
});
}
```
# ActionList
The `ActionList` component can be used to list all of the current [stored actions](/ai-actions/how-tos/stored/intro) that are available for a user.
If you are using an API key for authentication, then the stored actions listed will be only for that API Key.
If you are using OAuth for authentication, then the stored actions listed will only be the actions that a user has set up for your OAuth provider.
## Props
### `onActionSelected`
This is called when the user clicks on an action in the list. It is passed the `AiAction` object that was clicked.
See the [List stored actions](/ai-actions/how-tos/stored/list-actions) documentation for more information about what this object contains.
## Usage
Make sure that this component is rendered unerneath an
[`AiActionsProvider`](/ai-actions/libraries/react/ai-actions-provider)!
See [EditAction and CreateAction](/ai-actions/libraries/react/edit-action) for a more complete example of usage.
```tsx ai-action-list.tsx
import { AiAction } from "@zapier/ai-actions";
import { ActionList } from "@zapier/ai-actions-react";
export const AiActionList = () => {
return console.log(action)} />;
};
```
## Example
On the left is the `` component. Click on an action to see the details on the right.
```tsx action-list-example.tsx
import { AiAction } from "@zapier/ai-actions";
import { ActionList, AiActionsProvider } from "@zapier/ai-actions-react";
import { FunctionComponent, useState } from "react";
import ReactJson from "react-json-view";
interface ActionListExampleProps {
apiKey: string;
}
export const ActionListExample: FunctionComponent = ({
apiKey,
}) => {
const [selectedAction, setSelectedAction] = useState(null);
return (
{
setSelectedAction(action);
}}
/>
{selectedAction ? (
) : (
"Select an action to see its details"
)}
);
};
```
# AiActionsProvider
All `@zapier/ai-actions-react` components **must** be rendered underneath an `AiActionsProvider`.
## Props
### `auth`
The `auth` prop is used to handle authenticating with AI Actions.
It takes an object that can have one of two properties:
* `apiKey`: A string containing your API key.
* `token`: A string containing your OAuth token.
See the [Authentication guide](/ai-actions/libraries/nodejs/authentication) for more information on how to get an API key or an OAuth token.
If you plan on using the AI Actions components to allow users to create and
manage their own actions, then you will have to use the OAuth `token`
authentication method.
## Usage
If you're using a framework like Next.js, the `AiActionsProvider` and the
components rendered as children of it need to be rendered client-side.
This can be done by putting `"use client";` at the top of the file that uses `AiActionsProvider`.
```tsx
import { AiActionsProvider } from "@zapier/ai-actions-react";
export const MyAiActionsProvider = ({ children }) => {
return (
{children}
);
};
```
# EditAction and CreateAction
There are two separate components for managing a specific stored action with `@zapier/ai-actions-react`: `EditAction` and `CreateAction`.
`CreateAction` should be used when you do not have an existing action that you want to edit, and `EditAction` should be used when you know the ID of the action you want to edit.
## Props
### `onActionSaved`
This callback will be called with the updated action when the user clicks the "Save" button.
See the [Get stored action](/ai-actions/how-tos/stored/manage/get-action) documentation for more information about what the object contains.
### `actionId`
**Only for `EditAction`**
This is the ID of the action that you want to edit. When the component renders, it will fetch the latest version of the action to edit from the API.
### `onActionDeleted`
**Only for `EditAction`**
This callback wall be called with the ID of the action that was deleted when the user clicks the "Delete" button.
## Usage
Make sure that this component is rendered unerneath an
[`AiActionsProvider`](/ai-actions/libraries/react/ai-actions-provider)!
```tsx create-action.tsx
import { FunctionComponent } from 'react'
import { AiAction } from "@zapier/ai-actions";
import { CreateAction } from "@zapier/ai-actions-react";
interface CreateAiActionsProps {
onActionSaved: (action: AiAction) => void;
}
export const CreateAiAction: FunctionComponent = ({
onActionSaved,
}) => {
return ;
};
```
```tsx edit-action.tsx
import { FunctionComponent } from "react";
import { AiAction } from "@zapier/ai-actions";
import { EditAction } from "@zapier/ai-actions-react";
interface EditAiActionsProps {
actionId: string;
onActionSaved: (action: AiAction) => void;
onActionDeleted: (actionId: string) => void;
}
export const EditAiAction: FunctionComponent = ({
actionId,
onActionSaved,
onActionDeleted,
}) => {
return (
);
};
```
## Example
This example uses the [`ActionList` component](/ai-actions/libraries/react/action-list) to show a list of action and lets you select an action to edit. It also lets you create new actions.
```tsx edit-action-example.tsx
import { AiAction } from "@zapier/ai-actions";
import {
ActionList,
AiActionsProvider,
CreateAction,
EditAction,
} from "@zapier/ai-actions-react";
import { FunctionComponent, useState } from "react";
export const EditActionExample: FunctionComponent<{ apiKey: string }> = ({
apiKey,
}) => {
return (
);
};
```
# Getting started
### **The `@zapier/ai-actions-react` package is still in development!**
If you want to check it out, please [contact us](https://nla.zapier.app/_z/embed/page/clh5sdteo0001ml0pdz8a2aqr?&)
The `@zapier/ai-actions-react` library provides utilities to make it easy to interact with AI Actions from a React application.
## Installation
To install the library, run:
```bash
# npm
npm install @zapier/ai-actions @zapier/ai-actions-react
# pnpm
pnpm add @zapier/ai-actions @zapier/ai-actions-react
# yarn
yarn add @zapier/ai-actions @zapier/ai-actions-react
```
## Quick start
[Get your API Key here](https://actions.zapier.com/credentials/)
Render the `AiActionsProvider` at the root of your application, or around any components that need to interact with AI Actions.
You can pass the API key as a prop to the provider component, or you can get an OAuth token. See the [Authentication guide](/ai-actions/libraries/nodejs/authentication) for more information.
```tsx
import { AiActionsProvider } from "@zapier/ai-actions-react";
export const MyAiActionsProvider = ({ children }) => {
return (
{children}
);
};
```
If you're using a framework like Next.js, the `AiActionsProvider` and the
components rendered as children of it need to be rendered client-side.
This can be done by putting `"use client";` at the top of the file that uses `AiActionsProvider`.
Then, you can use the `ActionList` component to list the actions that are available to the user:
```tsx
import { AiAction } from "@zapier/ai-actions";
import { ActionList } from "@zapier/ai-actions-react";
export const AiActionList = () => {
// When the user clicks on an action in the list, the `onActionSelected` callback will be called.
// This can be used to open a modal to edit the action, or to execute it using the `AiActions` client.
return console.log(action)} />;
};
```
# StatelessActionCreator
The `StatelessActionCreator` component can be used to create a new stateless action that you can use to run a stateless action.
## Props
### `onActionGenerated`
This callback will contain JSON that can be passed, along with instructions, to the [execute statless action](/ai-actions/how-tos/stateless/run-stateless-action) endpoint.
## Usage
Make sure that this component is rendered unerneath an
[`AiActionsProvider`](/ai-actions/libraries/react/ai-actions-provider)!
```tsx stateless-action-creator.tsx
import { StatelessAction } from "@zapier/ai-actions";
import { StatelessActionCreator } from "@zapier/ai-actions-react";
export const StatelessActionCreatorExample = () => {
return (
console.log(action)}
/>
);
};
```
This JSON can then be used with [the Node.js client](/ai-actions/libraries/nodejs/getting-started) to execute a stateless action:
```ts execute-stateless-action.ts
import { AiActions, StatelessAction } from "@zapier/ai-actions";
const aiActionsClient = new AiActions({
auth: {
apiKey: "sk-YOUR_API_KEY",
},
});
const action: StatelessAction = {
// JSON from the StatelessActionCreator
};
action.instructions =
"Set the instructions you want to use when running the action";
const result = await aiActionsClient.executeStatelessAction(action, {
// set this to `false` to actually run the action
// otherwise, you can check the parameters that AI Actions will guess
preview_only: true,
});
console.log(result);
```
## Example
[See the "Stateless action creator/runner" tool for a full example](/ai-actions/tools/stateless-action)
# Quickstart
Learn how to kick-start AI Actions by obtaining a partner key, setting up allowed actions, and executing an action through our API.
## Step 1: Get a Partner Key for Authentication
Follow the steps below to create your API key:
1. [Log in](https://actions.zapier.com/custom/start/) or [sign up](https://zapier.com/sign-up) to Zapier.
2. [Connect](https://actions.zapier.com/custom/actions/) your Zapier account to the `custom` integration app.
3. Your API key will be available on the [credentials page](https://actions.zapier.com/credentials/).
Test that the API key is working:
````bash
curl -v \
-H "Content-Type: application/json" \
-H "x-api-key: " \```
'https://actions.zapier.com/api/v2/check/'
````
## Step 2: Set Up Allowed Actions
Now that you're authenticated, let's set up the actions you're allowed to execute.
Actions can be configured by you at a specific URL or with our quick configuration popup.
The setup URL is the same for everyone: `https://actions.zapier.com//start/`.
## Step 3: Execute an Action via the API
With your partner key and actions set up, you're ready for action.
To run an action, you need to execute a command like the one below, filling in the `instructions` and any optional hint parameters.
```bash run_action1.sh
curl -v \
-d '{"instructions": "grab the latest email from bryan helmig"}' \
-H "Content-Type: application/json" \
-X POST 'https://actions.zapier.com/api/v1/dynamic/exposed/01GTA3G1WD49GN1XXXXXXXXX/execute/'
One more example, this time requesting a preview of the action:
```
```bash run_action2.sh
curl -v \
-d '{"instructions": "send a short poem about automation and robots to slack", "Channel_Name": "#fun-zapier"}' \
-H "Content-Type: application/json" \
-X POST 'https://actions.zapier.com/api/v1/dynamic/exposed/01GTB1KMX72QTJEXXXXXXXXXX/execute/'
Another example, this time an action to retrieve data:
```
```bash preview_action_run.sh
curl -v \
-d '{"instructions": "say Hello World to #fun-zapier", "preview_only": true}' \
-H "Content-Type: application/json" \
-X POST 'https://actions.zapier.com/api/v1/dynamic/exposed/01GTB1KMX72QTJEXXXXXXXXXX/execute/'
```
```json error_response.json
{
"action_used": "Gmail: Send Email",
"result": null,
"status": "error",
"error": "Error from app: Required field 'subject' (subject) is missing. Required field 'Body' (body) is missing."
}
```
```json preview_response.json
{
"action_used": "Slack: Send Channel Message",
"input_params": {
"Channel": "fun-zapier",
"Message_Text": "Hello World"
},
"review_url": "https://actions.zapier.com/execution/01GW2E2ZNE5W07D32E41HFT5GJ/?needs_confirmation=true",
"status": "preview",
}
```
```json trimmed_response.json
{
"result": {
"from__email": "mike@zapier.com",
"from__name": "Mike Knoop",
"subject": "Re: Getting setup",
"body_plain": "Hi Karla, thanks for following up. I can confirm I got access to everything! ... Thanks! Mike",
"cc__emails": "bryan@zapier.com, wade@zapier.com",
"to__email": "Karla"
}
}
```
# Stateless action creator/runner
[See the "Stored Vs. Stateless Actions" documentation for more information
about stored vs. stateless actions](/ai-actions/how-tos/stateless-vs-stored)
Use the tool below to generate and run a stateless action.
If you have stateless action JSON available already, you can input it on the "Run stateless action" tab.
# Create Account
get /v2/authorize
Create a new user and obtain an access token. See [here](/guides/quac/intro-quac#add-support-to-the-workflow-api) to get started.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
# User Profile
get /v1/profiles/me
This endpoint returns the authenticated user information
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `profile` OAuth scope.
# Get Actions
get /v2/actions
Fetch the available actions for the provided App. It's typical to filter by type so that only actions that make sense for a particular step are shown. Action IDs may not be reused, see our documentation [here](https://docs.zapier.com/partner-solutions/workflow-api/Hardcoding-an-Action) for how to hardcode a particular action.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap` OAuth scope.
# Get Choices
post /v2/actions/{action_id}/inputs/{input_id}/choices
Get the possible values for an Input Field that is marked as `SELECT`.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap` OAuth scope.
# Get Input Fields
post /v2/actions/{action_id}/inputs
Get the Input Fields for a particular Action, using the provided authentication and inputs. See [here](/guides/getting-started/Fields-and-Fieldsets) for more information.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap:write` OAuth scope.
# Get Output Fields
post /v2/actions/{action_id}/outputs
Get the Output Fields for a particular Action, using the provided authentication and inputs.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap:write` OAuth scope.
# Step Test
post /v2/actions/{action_id}/test
Tests the action (step) in the third party api, using the provided authentication and inputs.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap:write` OAuth scope.
# Get Apps [v1]
get /v1/apps
This endpoint returns a list of apps sorted popularity. See [here](/guides/list-apps) to get started.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
# Get Apps [v2]
get /v2/apps
This endpoint returns a list of apps sorted popularity. See [here](/guides/list-apps) to get started.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap` OAuth scope.
# Zapier Authentication
Authenticate with The Zapier Workflow API
### Prerequisites
* Your app needs to be published as a [public integration](https://platform.zapier.com/quickstart/private-vs-public-integrations) in Zapier's App Directory.
### Retrieving an access Token
The Workflow API uses [OAuth 2.0 authentication with the authorization code grant type](https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type). At the end of the Oauth authentication code flow, you'll get a user access token that you'll pass in a header with each API request.
You can configure one (or multiple) redirect URIs in the [Zapier Developer Platform](https://developer.zapier.com/) under `Embed` → `Settings` → `Redirect URIs`
![Configure redirect URIs](https://cdn.zappy.app/f8fbb9cf76864f457a0132b7eb8db196.png)
You will only be able to configure redirect URIs after you've published your app as a [public integration in Zapier's App Directory](https://platform.zapier.com/quickstart/private-vs-public-integrations).
You can find your Client ID and Client Secret in the [Zapier Developer Platform](https://developer.zapier.com/) under `Embed` → `Settings` → `Credentials`
![Client ID and Secret](https://cdn.zappy.app/cb3660c17a3d26b36f438ab80c0860d5.png)
Your application's **Client ID** and **Client Secret** are only available after you've published your app as a [public integration in Zapier's App Directory](https://platform.zapier.com/quickstart/private-vs-public-integrations).
Regenerating your client secret will invalidate any previous secret.
The various endpoints of the Zapier Workflow API require different OAuth scopes. Information on specific scopes required is included within the API reference for each endpoint.
Construct a URL like the one below (with your own redirect url, client id, OAuth scopes, etc.) and open a browser to that URL.
```js
https://api.zapier.com/v2/authorize
?response_type=code
&client_id={YOUR_CLIENT_ID}
&redirect_uri={YOUR_REDIRECT_URI}
&scope={YOUR_OAUTH_SCOPES}
&response_mode=query
&state={RANDOM_STRING}
```
Here's an explaination for each query parameter:
| Parameter | Meaning |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `response_type=code` | This tells the authorization server that the application is initiating the authorization code flow. |
| `client_id` | The public identifier for your application. This will be the same client id that you retrieved in step #2. |
| `redirect_uri` | Tells Zapier's authorization server where to send the user back to after they approve the request. This should be the redirect URI that you configured in step #1. |
| `scope` | One or more space-separated strings indicating which permissions your application is requesting. Information on specific scopes required is included within the API reference for each endpoint. |
| `state` | Your application generates a random string and includes it in the request. It should then check that the same value is returned after the user authorizes the app. This is used to prevent CSRF attacks. |
A full example would be:
```js
https://api.zapier.com/v2/authorize
?response_type=code
&client_id=5672067294567789354752
&redirect_uri=https%3A%2F%2Fyour-app.com%2Fcallback
&scope=zap%20zap:write%20authentication
&response_mode=query
&state=tney4952
```
When the user visits this URL, Zapier's authorization server will present them with a prompt asking if they would like to authorize your application's request.
![OAuth prompt example](https://cdn.zappy.app/a58205b77ec7e6aa52226354af296125.png)
If the user approves the above request, then Zapier's authorization server will redirect the browser back to the `redirect_uri` that you specified and configured earlier. Two query string parameters, `code` and `state` will also be included.
For example, the browser would be redirected to:
```js
https://your-app.com/redirect
?code=dfJ2KuL0vKLRCwQIOL5NDGKQ&H9mlc
&state=tney4952
```
| Parameter | Meaning |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code` | This value is the **authorization code** generated by Zapier's authorization server. In the next step, you'll exchange this code for an access token. Keep in mind that authorization codes are only valid for 2 minutes, and you'll need to do the exchange within that window of time to avoid errors. |
| `state` | This value should match the state query parameter that you used in step 2. Your application should verify that these values match. |
The final step is to exchange the **authorization code** that you just recieved for an **access token** that can be used to make authorized requests to the Zapier Workflow API. You make the exchange with a `POST` request to Zapier's token endpoint `https://zapier.com/oauth/token/`.
Below is an example of a request that can be used to do the exchange.
```sh cURL
curl -v -u {CLIENT_ID}:{CLIENT_SECRET} \
-H "Content-Type: multipart/form-data" \
-F grant_type=authorization_code \
-F code={AUTHORIZATION_CODE} \
-F redirect_uri="{REDIRECT_URI}" \
https://zapier.com/oauth/token/
```
```python Python
import requests
data = 'grant_type=authorization_code&code={AUTHORIZATION_CODE}&redirect_uri={REDIRECT_URI}'
response = requests.post('https://zapier.com/oauth/token/', headers=headers, data=data, auth=('{CLIENT_ID}', '{CLIENT_SECRET}'))
```
```js Javascript
fetch('https://zapier.com/oauth/token/', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': 'Basic ' + btoa('{CLIENT_ID}:{CLIENT_SECRET}')
},
body: 'grant_type=authorization_code&code={AUTHORIZATION_CODE}&redirect_uri={REDIRECT_URI}'
});
```
```php PHP
| Parameter | Meaning |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLIENT_ID` | This will be the same client id that you retrieved in step #2. |
| `CLIENT_SECRET` | This is a secret known only to your application and the authorization server. It will be the same client secret that you retrieved in step #2. |
| `AUTHORIZATION_CODE` | This is the authorization code you received in the above step #5. |
| `REDIRECT_URI` | This should be the redirect URI that you configured in step #1. |
Note that, in addition to client id and secret being passed as a Basic Authentication header as above, they can be
passed as part of the body, using the keys `client_id` and `client_secret`.
You'll recieve a response that looks like this:
```js
HTTP/1.1 200 OK
Content-Type: multipart/form-data
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "jk8s9dGJK39JKD93jkd03JD",
"expires_in": 36000,
"token_type": "Bearer",
"scope": "zap zap:write authentication",
"refresh_token": "9D9oz2ZzaouT12LpvklQwNBf6s4vva"
}
```
This response contains the `access_token` that you'll use to make API request on the user's behalf, as well as a refresh token.
Both tokens should be stored securely, to protect your users' privacy.
The refresh token in particularly important, since it would allow a nefarious entity to generate access tokens indefinitely:
* The refresh token **MAY NOT** be stored in localStorage
* The refresh token **MAY NOT** be stored in sessionStorage
* The refresh token **MAY NOT** be stored in indexedDB
* The refresh token **MAY NOT** be stored in a regular cookie
* The refresh token **MAY** be stored in a Secure; HTTPOnly cookie
* The refresh token **MAY** be stored in a server side database, only accessible to the current user
The access token should be passed with requests as an `Authorization` header. For example:
```
Authorization: Bearer jk8s9dGJK39JKD93jkd03JD
```
All access tokens will expire after 10 hours (the number of seconds in `expires_in`), for security purposes. After that point, any request using that access token will return a 401 status code. To proceed, the refresh token should be exchanged for a new access token *and* a new refresh token. This will not require any interaction by the user.
Below is an example request that can be used:
```sh cURL
curl -v -u {CLIENT_ID}:{CLIENT_SECRET} \
-H "Content-Type: multipart/form-data" \
-d "grant_type=refresh_token&refresh_token={REFRESH_TOKEN}" \
https://zapier.com/oauth/token/
```
```python Python
import requests
data = 'grant_type=refresh_token&refresh_token={REFRESH_TOKEN}'
response = requests.post('https://zapier.com/oauth/token/', headers=headers, data=data, auth=('{CLIENT_ID}', '{CLIENT_SECRET}'))
```
```js Javascript
fetch('https://zapier.com/oauth/token/', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': 'Basic ' + btoa('{CLIENT_ID}:{CLIENT_SECRET}')
},
body: 'grant_type=refresh_token&refresh_token={REFRESH_TOKEN}'
});
```
```php PHP
| Parameter | Meaning |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLIENT_ID` | This will be the same client id that you retrieved earlier. |
| `CLIENT_SECRET` | This is a secret known only to your application and the authorization server. It will be the same client secret that you retrieved earlier. |
| `REFRESH_TOKEN` | This is the refresh token code you received with the access token. |
You'll receive a response that looks like this:
```js
HTTP/1.1 200 OK
Content-Type: multipart/form-data
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "NEfSRKpUjVd3Nj9yyaXKs15BrM7SVA",
"expires_in": 36000,
"token_type": "Bearer",
"scope": "zap zap:write authentication",
"refresh_token": "zzdumGAW2TmayeKjzu0z9oHJiziKdn"
}
```
Note that you will receive a *new* refresh token - the old refresh token can no longer be used again, and so the new refresh token should be stored securely for future use. Refresh tokens don't have an expiration date - they only expire when they are used to get a new access token.
### Retrieving your client id
The previous version of the Workflow API (the PartnerAPI) supports passing the `client_id` to make an authenticated request.
You can find your client id in the [Zapier Developer Platform](https://developer.zapier.com/) under `Embed` → `Settings` → `Credentials`
![Client ID and Secret](https://cdn.zappy.app/cb3660c17a3d26b36f438ab80c0860d5.png)
Your application's **Client ID** and **Client Secret** are only available after you've published your app as a [public integration in Zapier's App Directory](https://platform.zapier.com/quickstart/private-vs-public-integrations).
Regenerating your client secret will invalidate any previous secret.
From there, simply pass your `client_id` as a query param to any V1 endpoints that require it.
```js Example for Zap-templates
https://api.zapier.com/v1/zap-templates?client_id={YOUR_CLIENT_ID}
```
# Create Authentication
post /v2/authentications
Creates a new Authentication for the provided App. See [here](/guides/custom-flows/adding-an-authentication) to get started.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `authentication:write` OAuth scope.
# Get Authentications
get /v2/authentications
Fetch the available Authentications for the provided App. This will only return Authentications that are owned by the user and not those that are shared with them, since it's not possible to create Zaps with Authentications you don't own.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `authentication` OAuth scope.
# Get Categories
get /v1/categories
List of Zap categories
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
# null
# null
# null
# null
# Errors
Errors in the API follow the recommendation from the JSON API spec.
An example error object returned in a response body looks like this:
```json
{
"errors": [
{
"status": "400",
"title": "Bad Request",
"detail": "The request could not be understood by the server due to malformed syntax.",
"source": {
"pointer": "/data/attributes/first-name"
}
}
]
}
```
Other potential error codes that may be returned are:
In all cases the response will be a JSON object with a single key `errors` which will be an array of error objects.
For more information, check out the [responses](https://jsonapi.org/format/#error-objects) in the API reference.
# null
# null
# null
# null
# Pagination
Section to be completed.
# Requests
Requests follow the [JSON API spec](https://jsonapi.org/format/#fetching) and should send the header Accept: application/vnd.api+json.
Typical arguments for requests are sent via query params, for more information check the specific requests for each endpoint in the API Reference section.
Example:
```bash
curl --request GET \
--url https://stoplight.io/mocks/zapier/public-api/181772442/zaps \
--header 'Accept: application/vnd.api+json'
```
# Responses
Responses for the API follow the JSON API spec for fetching resources.
A successful response will always have a data key, which will be an array of objects depending on the request.
```json
{
"data": [
{
"id": "00000000-0000-c000-8000-000000012345",
"type": "zap",
"links": {
"html_editor": "https://zapier.com/webintent/edit-zap/00000000-0000-c000-8000-000000012345"
},
"steps": [
{
"action": "uag:1f188536-6dd0-4172-8414-2b90914ddee9",
"inputs": {
"deal_stage": "CLOSED_WON"
},
"authentication": "b3eYnwl"
},
{
"action": "uag:1f188536-6dd0-4172-8414-2b90914ddaa7",
"inputs": {
"full_name": "{{customer__full_name}}"
},
"authentication": "BRn9rRg"
}
],
"title": "My Zap",
"is_enabled": false,
"updated_at": "2019-08-24T14:15:22Z",
"last_successful_run_date": "2019-08-24T14:15:22Z"
}
],
"meta": {
"count": 133,
"limit": 10,
"offset": 10
},
"links": {
"next": "https://api.zapier.com/v2/zaps?offset=20&limit=10",
"prev": "https://api.zapier.com/v2/zaps?offset=0&limit=10"
}
}
```
Every response should also include a `links` key that provides information for pagination and a `meta` key that provides information about the response.
# null
# Create a Workflow step
post /v2/workflow-steps
Creates a new Workflow Step based on a single provided step and returns a webhook URL that can be used to invoke the Workflow Step and retrieve a response. When creating a Workflow write action step, field values can be hardcoded, or they can contain mapped values. The mapped values should be surrounded with double curly braces.
In the example request body the Workflow Step contains the field values `email`, `name`, `phone`, and `address` in double curly braces. Then the webhook URL returned can be called with a `POST` request containing a JSON body in the following format:
```
{
"email": "user@example.com",
"name": "John Doe",
"phone": "1234567890",
"address": "123 Maple Lane"
}
```
Note that authentications may become invalid over time, and if this occurs, users will be notified by email to fix their authentication. If the authentication is not fixed, trying to run the Workflow Step will time out. Workflow Steps are also (for now) visible and editable as Zaps at https://zapier.com/app/zaps. If a user turns off a Workflow Step Zap, the request will similarly time out. Finally, it is possible that a user can edit the Workflow Step Zap such that it returns unexpected outputs.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap:write` OAuth scope.
# Rate Limiting
Rate limits when accessing the Workflow API
Currently all endpoints are rate limited by the following, whichever occurs first:
* IP address, and is limited to **60 requests per minute.**
* Partner, and is limited to **150 requests per minute.**
## Rate Limit Response
If you hit the rate limit, you will receive a `429 Too Many Requests`. There will be a cooldown of 60 seconds before you can make any more requests.
# Get Zap Templates
get /v1/zap-templates
List popular Zap Templates using your app. See [here](/guides/list-zap-templates) to get started.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
# Create a Zap
post /v2/zaps
This URL creates a new Zap based on a series of steps and a given title.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap:write` OAuth scope.
# Get Zaps [v1]
get /v1/zaps
This endpoint returns a list of Zaps for the authenticated Zapier user.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap` OAuth scope.
# Get Zaps [v2]
get /v2/zaps
This endpoint returns a list of Zaps for the authenticated Zapier user.
The `expand` array can be used to expand selected fields into full objects in the response. Inputs with keys can
also be passed to filter Zaps by certain criteria.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
#### OAuth
This endpoint requires the `zap`, or `zap:all` OAuth scope.
# Guess a Zap [Beta]
post /v2/guess
This endpoint returns a suggested Zap and pre-filled URL to Zapier from a given prompt.
This API is rate limited. See [Rate Limiting](https://docs.zapier.com/partner-solutions/api-reference/rate-limiting) for more details.
# Element Security
Keeping our elements secure and usable is critical at Zapier
User security is paramount. By default, Zapier denies any embedding of our product unless you provide us with a list of domains that you expect to embed Zapier in.
This protects the user from malicious activities like [Clickjacking](https://www.owasp.org/index.php/Clickjacking). :
![If you were to attempt to embed Zapier and the embedding domain was not registered, we would present an error](https://cdn.zappy.app/4fb49db62ac6d5c41df46db7ccf3aab7.png)
## Provide a list of domains
If you've already embedded our Product, this would have already been captured and your product domains are permitted.
* To add domains, navigate to the Settings tab under the Embed section in the sidebar of your integration's [Platform UI](https://developer.zapier.com/), and add the missing domains under the 'Embedding Domains' section.
![](https://cdn.zappy.app/da56277eb07303d8ce8ef42cafc8511e.png)
These specific domains are then permitted to embed Zapier. The domains provided by you should be registered with your company with a public registrar. That is to say a `randomcnamedomain.com` is not valid for the same reason that a user or bad actor could register that domain.
## Troubleshooting
* `localhost`, `yourcomp.local` and `127.0.0.1` are not valid supported domains. An option during your embed development would be to use a tunnel service like [ngrok](https://ngrok.com/) and to register that ngrok tunnel with us. Be advised, that we will ask for a static domain from ngrok.com or similar tunneling service.
* If the domain you're embedding on is added to the allowlist within *Manage Domains*, but you're seeing the `This embed is blocked` error, the [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) may be too restrictive/overly strict. You'll want to check Console/Network for the appropriate request to see the [referrer-policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) header. Using `strict-origin-when-cross-origin` as the referrer-policy is recommended.
* For local development, use [ngrok](https://ngrok.com/) to make `https` test URLs when needed, as using `http` would be blocked, even if the domain has been added to the allowlist.
# Embed Insights
Insights are available to review the performance of your embed, and track usage growth.
## Embed insights definitions
Embed features are available for public integrations. All metrics can be filtered by All Embeds or individual embeds, and the last 7, 30, and 90 days, hover over the metric to see the percentage and count figures. To view insights from your embed, visit the [Zapier Developer Platform](https://developer.zapier.com/).
Definitions for each of the metrics provided in the integration dashboard below:
| Metric | Definition |
| ---------------------------------- | ---------------------------------------------------------------------------- |
| Total conversion | Percentage of users who clicked on Zap Templates and activated a Zap. |
| Users who clicked on Zap Templates | Percentage and count of users who clicked on a Zap Template in an embed. |
| Users who signed up for Zapier | Percentage and count of users who signed up for a new account on Zapier. |
| Users who created Zaps | Percentage and count of users who started a Zap in the editor. |
| Users who enabled Zaps | Percentage and count of users who created and successfully published a Zap. |
| Users who activated Zaps | Percentage and count of users who created a Zap that successfully ran tasks. |
## Best practices
* Test different iterations of embeds and compare rates to see which ones resonate most with your users. Do certain sets of Zap Templates garner more clicks and activations than others? Does enabling “App search” on the Workflow Element, so users can see all the apps they can integrate with, increase activations?
* Improve your Zap Templates with short, descriptive titles, and as much [prefilled field](/partner-solutions/pre-filled-zaps) mapping as possible, so users are immediately aware of the use case and can activate the Zap with the least amount of clicks.
* Provide context with your embed through help docs or an onboarding flow guiding users on how to connect your app with others using Zapier to improve the Users who signed up for Zapier rate.
# Zapier Partner Solutions
Add automation to your product with Zapier.
Our partner solutions offer the most efficient and comprehensive ways to add automation and surface integrations directly within your product. Whether you aim to build native automated workflows for your users or simply display the most popular integration use cases, our [Workflow API](/partner-solutions/workflow-api/intro) and [Workflow Element](/partner-solutions/workflow-element/intro) provide robust solutions to meet your needs. These tools replace the need to develop your own automation capabilities, ensuring you can deliver powerful automation experiences to your users with ease.
## Why us?
Partners who embed Zapier see higher retention rates, more customer upgrades, and improved lifetime value from their users. For example, [Jotform](https://zapier.com/blog/how-jotform-retains-users-with-zapier/) found that users who used their Workflow API implementation were **25x more likely to upgrade to a paid plan, and had a 50% higher retention rate!**
Check out our collection of [partner case studies](https://zapier.com/blog/all-articles/partner-case-studies/) for more testimonials on how embedding Zapier has boosted growth for our partners
## Pricing
Embedding Zapier is free for all partners.
## What are the different partner solutions?
We have 2 main products for you to add automation to your product:
1. Our [Workflow API](/partner-solutions/workflow-api/intro) is a powerful tool for building native integrations and workflows powered by Zapier for your end users in your product
2. Our [Workflow Element](/partner-solutions/workflow-element/intro) is a prebuilt UI component that offers the quickest and easiest way to integrate Zapier directly into your product. With just a few lines of code from our code generator, it provides a complete end-to-end automation solution, allowing users to discover, create, and maintain automated workflows.
Along with our main products, we have a suite of product enhancements, meant to help create an even more frictionless experience for your users
1. [Pre-filled Zaps](/partner-solutions/pre-filled-zaps) allow you to define the input fields on behalf of the user, simplying the experience of setting up their Zap.
2. [Quick Account Creation](/partner-solutions/quick-account-creation) is a seamless, accelerated sign-up feature allowing first time Zapier users to skip the standard sign-up procedure and onboarding survey and have an account created on their behalf.
3. [Guess a Zap (New! ✨)](/partner-solutions/api-reference/zaps/guess-a-zap) Simplifies the automation setup process by using natural language to help users describe what they want to automate. Guess a Zap analyzes their description and suggests the most relevant workflows, minimizing the need for manual setup and helping users quickly find the right automation.
4. [Workflow Steps (New! ✨)](/partner-solutions/experimental/create-a-workflow-step) enables you to augment your native workflow tools with Zapier’s apps and actions.
To see our partner solutions in action, check out our [Embed Gallery](https://zapier.com/developer-platform/partner-embeds)Zapier's partner solutions are available for public integrations. To take advantage, first ensure your integration has been published to [Zapier's App Directory](https://zapier.com/apps). If your integration isn't yet published, learn more about [submitting your integration for review](https://platform.zapier.com/publish/public-integration#4-submit-your-integration-for-app-review).
# Pre-filled Zaps
Prefills allow you to define the input fields on behalf of the user, simplying the experience of setting up their Zap.
Simplifying zap setup is one of the most effective ways to make sure the zaps created work as desired. As integration owners, you can likely pre-fill some of the zap fields on behalf of your users; making zap setup faster, easier and more reliable.
### What are they?
Pre-filled Zaps are simply URLs with field values added as parameters, which you can use to direct users to the Zap editor with some input fields already filled. Place these URLs inside your product or within an embedded Zap editor to facilitate users creating and publishing Zaps.
The following is an example of a simple pre-filled zap that sends the weather to a slack dm every day. The **latitude** and **longitude** inputs have been pre-filled in this case.
`https://api.zapier.com/v1/embed/space-by-zapier/create?steps[0][app]=WeatherCLIAPI@latest&steps[0][action]=today_forecast&steps[0][params][latitude]=40.7127&steps[0][params][longitude]=-74.0059&steps[1][app]=SlackAPI&steps[1][action]=direct_message`
## Creating pre-filled Zaps using the Zap Generator
The UI-based generator within the [Developer Platform](https://developer.zapier.com/) facilitates constructing pre-filled Zaps. Start by selecting your app and navigating to `Embed` → `Pre-Filled Zaps` and scroll down to the generator.
Start by selecting an app and event for both the trigger and action to see the fields you can pre-fill. If no fields appear, the event has no input fields.
* If you don't want to pre-fill the field, leave the box unchecked. In the Zap, the field will be empty or set to a default value, if there is one.
* If there's a static value for a field that applies to every user's Zap (like the title of an email), check the field and provide the value in the text field.
* If the values are dynamic or you don't know them yet, replace the placeholders represented in curly brackets (i.e \{TRIGGER\_LIST\_ID}) in the generated URL from Step 2 before using it in your app. This could be something like an account or list ID field. The placeholder serves as a reminder to replace the value at runtime.
* If the field is greyed out, it requires a complex field value and cannot be pre-filled.
Fields denoting (required) are required for turning the Zap on, not required to be pre-filled.
* Use the handy test button to make sure the resulting Zap is what you intended. You'll need to connect app accounts on both steps to see the pre-filled fields.
* Copy the code and embed it inside your app to make setting up a Zap easier and faster for users.
The generator only supports creating 2-step Zaps, but you can construct multi-step Zaps by building upon the generated URL and adding `steps` parameters with increased indeces.
## Creating pre-filled Zaps from Zap Templates
### Prerequisites
* You will need to know the required input fields per trigger or action step. You can find the fields as defined in your [Zapier integration](https://developer.zapier.com/).
Get the appropriate `create_url` property from the specific Zap Template desired. This can be retrieved from the [zap-templates](/partner-solutions/api-reference/zap-templates/get-zap-templates) endpoint. It follows the following format: `https://api.zapier.com/v1/embed/{app_name}/create/{template_id}`
The url params should be defines as follows; `step[{step_index}][params][{param_name}]`- with steps being zero-indexed. As an example, Trello could prefill the name of a Trello card `name` in the second step of the Zap template using the following:
`https://api.zapier.com/v1/embed/trello/create/113?steps[1][params][name]=hello`
where the `create_url` of `https://api.zapier.com/v1/embed/trello/create/113` was retrieved from the [zap-templates](/partner-solutions/api-reference/zap-templates/get-zap-templates) endpoint, and input fields defined in their zapier integration.
Here's what it would look like in the editor:
You can prefill multiple values for the user. In this example `name` and `desc` are prefilled
```js
https://api.zapier.com/v1/embed/trello/create/113
?steps[1][params][name]=yoyoyo
&steps[1][params][desc]=yeehaw
```
* `template=113`
* `steps[1][params][name]=yoyoyo`
* `steps[1][params][desc]=yeehaw`
You can provide a label for prefill dropdowns as we won't fetch all of the pages of choices until the user opens the dropdown:
```js
https://api.zapier.com/v1/embed/trello/create/113
?steps[1][params][board]=1234
&steps[1][meta][parammap][board]=Test
```
* `template=113`
* `steps[1][params][board]=1234`
* `steps[1][meta][parammap][board]=Test`
# Quick Account Creation
Quick Account Creation is a seamless, accelerated sign-up feature allowing first time Zapier users to skip the standard sign-up procedure and onboarding survey. Enabling Quick Account Creation as part of your embed tool code helps provide a more frictionless experience for end users.
Instead of being directed to a sign-up screen, users are presented with a consent page to connect to Zapier, then proceed to the Zap editor if consented. If an existing Zapier account under the email does not exist, users will receive an email prompting them to finish setting up their account. This allows users to dive directly into the Zap editor to accomplish the task at hand efficiently and without context switching.
## Case study
Signups to use Adalo's integration jumped 40% after embedding Zapier with Quick Account Creation into their app builder. [Read more about Adalo's user experience and results.](https://zapier.com/blog/adalo-user-experience-with-zapier/)
## Example implementation
## Prerequisites
* New or existing implementation of the [Workflow Element](/partner-solutions/workflow-element/intro), or [Workflow API](/partner-solutions/workflow-api/intro).
* Access to your user's first name, last name, and email on the page supporting Quick Account Creation.
## Add support to embed elements
Go to the [generator tool](https://zapier.com/partner/embed/workflow) for the Workflow Element.
Customize the visual design and features of the embed solution of choice.
Generate the embed code in HTML, Vanilla JS, React, Angular, or Vue.js.
Replace the placeholder values set to `sign-up-email`, `sign-up-first-name`, and `sign-up-last-name` in the code Body. All three values and the client ID are required for Quick Account Creation.
Embed both the Head and Body code on your product's page.
You can still embed the Workflow Element or utilize the Workflow API without support for Quick Account Creation. If the four required fields aren't provided, the embed will use the default behaviour redirecting users to Zapier's signup page from your product's page.
If you have an existing Workflow Element embed, you can add the new required fields for Quick Account Creation to your current code implementation, instead of re-customizing and regenerating the code. Make sure the Body code includes the below four fields with the placeholder values replaced:
```js
clientId="your_integration_client_id"
signUpEmail="email_of_your_user@example.com"
signUpFirstName="first_name_of_your_user"
signUpLastName="last_name_of_your_user"
```
## Add support to the Workflow API
### Redirect users to the Zap editor
After an account is created with this implementation, a user token still needs to be procured to access specific Workflow API endpoints. Generally, since the user will already be signed in to their newly created account in an active session on Zapier, users won't have to explicitly sign in again when prompted with Zapier's OAuth flow.
Use the URL below to initiate Quick Account Creation, then redirect users into the Zap editor with the Zap template set in the parameters:
```js
https://zapier.com/webintent/create-zap
?template=
&utm_source=partner
&utm_medium=embed
&utm_campaign=partner_api
&utm_content=partner_quick_account_creation
&entry-point-location=partner_embed
&referer=
&referrer=
&sign-up-first-name=
&sign-up-last-name=
&sign-up-email=
&client-id=
```
Replace the following query parameter placeholders in the URL:
| Parameter | Requirement | Explanation |
| ------------------ | ----------- | ------------------------------------------------ |
| client\_id | Required | Your application Client ID. |
| template | Required | An ID of a Zap Template. |
| sign-up-first-name | Required | First name of the user signing up. |
| sign-up-last-name | Required | Last name of the user signing up. |
| sign-up-email | Required | Email address of the user signing up. |
| refer or referrer | Required | URL of the page where the user clicked the link. |
### Procure a token and redirect users to a custom URL
This implementation allows a new Zapier account to be created, provides an access token to Zapier's [Workflow API](/partner-solutions/workflow-api/intro), then allows you to redirect users to a custom URL of your choosing.
Use the URL below to initiate Quick Account Creation, then redirect users to a specified URL:
```js
https://api.zapier.com/v2/authorize
?redirect_uri=
&scope=
&response_type=
&client_id=
&sign_up_first_name=
&sign_up_last_name=
&sign_up_email=
```
Replace the following query parameter placeholders in the URL:
| Parameter | Requirement | Explanation |
| --------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| redirect\_uri | Required | The page the user will be redirect to after OAuth. |
| scope | Required | Space (`%20`) separated values. See the [API reference](/partner-solutions/api-reference/categories/get-categories) for more information on specific scopes required. |
| response\_type | Required | Set to `token`. |
| client\_id | Required | Your application Client ID. |
| sign\_up\_first\_name | Required | First name of the user signing up. |
| sign\_up\_last\_name | Required | Last name of the user signing up. |
| sign\_up\_email | Required | Email address of the user signing up. |
## When Quick Account Creation is enabled
* If the user is already logged into Zapier, they are redirected to the Zap editor.
* If the user's email is already associated with a Zapier account, but the user is not logged in, they are redirected to Zapier's sign-in page.
* If the user's email is not associated with an existing Zapier account, they are redirected to the consent page.
* If the user consents to the terms and selects "Continue", a Zapier account is created on their behalf and they are redirected to the Zap editor. They receive an email to finish setting up their account shortly after the account is created.
* If the user closes the consent page, no Zapier account is created.
* If there is an error creating an account, the user is redirected to an error page.
* In the event a user wants to sign up with a different email than the one supplied, they can edit that email within the consent page.
* In the event a user has an existing Zapier account, but it's not under the supplied email they also have an option to sign in to any other account from the consent page.
# How to Build a Workflow
This guide walks through the entire process of building an automated workflow for your users to use -- front picking apps, adding authentication, filling inputs and publishing.
Authenticating with Zapier is required to build alongside this guide. This is covered separately [here](/partner-solutions/api-reference/authentication).
For this guide, let's assume that we want give our users the ability to automate workflows with the (imaginary) apps SuperExampleCRM and PlatformAdManager so that when there is a new lead in SuperExampleCRM, a new engagement report is sent to PlatformAdManager. We need to build a zap in your product with SuperExampleCRM and PlatformAdManager. Zaps are comprised of a series of steps, so in this example our two steps would be:
Whenever there is a new lead in SuperExampleCRM...
...a new engagement report is sent to PlatformAdManager
### Zap Step Requirements
Each Zap step in turn, is comprised of:
An Action is an operation that can be performed against a third-party API; either a `READ` or a `WRITE`.
An Authentication is a set of user credentials for an App that is stored securely by Zapier.
Inputs are fields that are provided to an Action so that it can run. They are analogous to the arguments that a function takes.
To build a Zap, we'll need to select an ***Action***, ***Authentication***, and ***Inputs*** for each step.
## Configuring the first step
> "When there is a new lead in SuperExampleCRM ..."\*
The first step of a Zap must to have a `READ` action.
### Selecting an Action
The first thing that we need to do is select an Action for the first step of the Zap. See [Selecting an Action](/partner-solutions/workflow-api/Selecting-an-Action) for more details.
Let's say that the `id` of the SuperExampleCRM app is `4b3920d6-1d5a-4071-b837-9383dc511b80`. Given that id and the constraint that the first action of an app must have the `action_type` `READ`, we can fetch a list of available actions for the selected app by making a request to the `/actions` endpoint.
```js
GET /actions?app=4b3920d6-1d5a-4071-b837-9383dc511b80&action_type=READ
```
Our user can then select one of these Actions that they wish to use as the Trigger for their Zap. For this guide, we'll say that the user chose the "New Lead" Action:
```js
{
"id": "core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ",
"key": "new_lead"
"app": "4b3920d6-1d5a-4071-b837-9383dc511b80",
"type": "action",
"action_type": "READ",
"is_instant": true,
"title": "New Lead",
"description": "Triggers when a new lead is added to SuperExampleCRM"
}
```
### Selecting an Authentication
The next step is to select an authentication for the first step of the Zap. See [Selecting an Authentication](/partner-solutions/workflow-api/Selecting-an-Authentication) for more details.
We can make a request to `GET /authentications?app=4b3920d6-1d5a-4071-b837-9383dc511b80` and allow the user to select which one of the returned Authentications they wish to use. (See [Selecting an Authentication](/partner-solutions/workflow-api/Selecting-an-Authentication) for what to do when there are no Authentications, Authentication isn't required, or if a new Authentication should be created)
For this guide, we'll say that the user chose the authentication with `id` `"49509"`
```js
{
"type": "authentication",
"id": "49509",
"app": "4b3920d6-1d5a-4071-b837-9383dc511b80",
"title": "SuperExampleCRM (wade@zapier.com)",
"is_expired": false
}
```
### Configuring Inputs
The last thing that we need to do for step 1 of the Zap is to select input values. Please see [Fields and Fieldsets](/partner-solutions/workflow-api/Fields-and-Fieldsets) for more details on fetching available Input Fields for a given Action, and reloading Input Fields as the user provides input data.
In an above step, the user selected the "New Lead" action with id `core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ`. With the `/actions/{action_id}/inputs` endpoint, we can get a list of the input fields that our first action requires
```js Request
//POST /actions/core:core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ/inputs
{
"data": {
"authentication": "49509",
"inputs": {}
}
}
```
```js Response
//POST /actions/core:853266/inputs
{
"data": [
{
"type": "input_field",
"id": "lead_type",
"depends_on": [],
"value_type": "STRING",
"format": "SELECT",
"is_required": true,
"title": "Lead type",
"description": "The type of Lead to filter on.",
"invalidates_input_fields": false,
"default_value": "company",
"placeholder": ""
}
]
}
```
Please see [Fields and Fieldsets](/partner-solutions/workflow-api/Fields-and-Fieldsets) for more details on fetching available Input Fields for a given Action, fetching Choices for Input Fields, and reloading Input Fields as the user provides input data.
As you can see, this action only has a single input field: the `Lead type`.
This gives the user the opportunity to configure the action so that it only
returns a certain type of lead.
In this example, the `format` of this input field is `SELECT`, which means we
now need to fetch the possible values which are available:
```js Request
// POST /actions/core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ/inputs/lead_type/choices
{
"data": {
"authentication": "49509",
"inputs": {}
}
}
```
```js Response
// POST /actions/core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ/inputs/lead_type/choices
{
"data": [
{
"type": "choice",
"id": "company",
"label": "Company",
"value": "company"
},
{
"type": "choice",
"id": "person",
"label": "Person",
"value": "person"
}
]
}
```
Again, we've provided an empty `inputs` field object, as the user has not yet
provided any input. If there are multiple input fields, this object should be
populated as the user progresses through them.
In this case, there are two available choices for the `lead_type` field:
`company` and `person`. With this information, we can render a dropdown field
and allow the user to select one of them.
For this guide, we'll say that the user selected `person` from the dropdown.
## Configuring the second step
> "... a new engagement report is sent to PlatformAdManager"\*
### Selecting an Action
Selecting an Action for the second step of a Zap follows the same process and uses the same API endpoints as selecting an Action for the first step of a Zap, with the two exceptions that a different App id should be used, and that the `action_type` of the second action should be `WRITE`. In this case, let's say that the id of the PlatformAdManager App is `9c29df46-f9b9-48e2-a879-8f5479d8401d`. We can fetch a list of available actions for PlatformAdManager by making a request to the `/actions` endpoint.
```js
GET /actions?app=9c29df46-f9b9-48e2-a879-8f5479d8401d&action_type=WRITE
```
Again, our user can then select one of these Actions that they wish to use. For this guide, we'll say that the user chose the "Create Engagement Report" Action:
```js
{
"id": "core:3ZYFzZKkjbDK2AwQopVqrZWL9pK",
"key": "create_engagement_report"
"app": "9c29df46-f9b9-48e2-a879-8f5479d8401d",
"type": "action",
"action_type": "WRITE",
"is_instant": true,
"title": "Create Engagement Report",
"description": "Creates a report of as engagement"
}
```
### Selecting an Authentication
Selecting an Authentication for the second step of a Zap follows the same process and uses the same API endpoints as selecting an Authentication for the first step of a Zap, with the exception that a a different App id should be used. In this case, we would make a request to `GET /authentications?app=9c29df46-f9b9-48e2-a879-8f5479d8401d`
Again, our user can select which of the available authentications they would like to use with this Zap. For this guide, we'll say that the user chose the authentication with `id` `"857610"`
```js
{
"type": "authentication",
"id": "857610",
"title": "PlatformAdManager (wade@zapier.com)",
"app": "9c29df46-f9b9-48e2-a879-8f5479d8401d",
"is_expired": false
}
```
### Configuring Inputs
For the second step of a Zap, you can follow the same process outlined above to fetch Input Fields, Choices, and reload Input fields as a user provides input data.
For this guide, lets say that we make the following request and get the below fields in response.
```js Request
// POST /actions/core:3ZYFzZKkjbDK2AwQopVqrZWL9pK/inputs
{
"data": {
"authentication": "857610",
"inputs": {}
}
}
```
```js Response
// POST /actions/core:3ZYFzZKkjbDK2AwQopVqrZWL9pK/inputs
{
"data": [
{
"type": "input_field",
"id": "engaged_party",
"depends_on": [],
"value_type": "STRING",
"is_required": true,
"title": "Engaged party",
"description": "The name of party that had engagement",
"invalidates_input_fields": false,
"placeholder": ""
},
{
"type": "input_field",
"id": "engagement_type",
"depends_on": [],
"value_type": "STRING",
"format": "SELECT",
"is_required": true,
"title": "Engagement type",
"description": "The action that the engaged party took.",
"invalidates_input_fields": false,
"default_value": "lead_form_completed",
"placeholder": ""
}
]
}
```
Again, please see [Fields and Fieldsets](/partner-solutions/workflow-api/Fields-and-Fieldsets) for more details on fetching available Input Fields for a given Action, fetching Choices for Input Fields, and reloading Input Fields as the user provides input data.
We can go through the same process that we did in Step 1 of presenting input fields to the user and receiving input configuration. However, we also have the option of creating dynamic input by mapping the Output Fields of step 1 to the Input Fields of step 2.
### Mapping Outputs to Inputs
We get the available output fields of our first action from the outputs endpoint:
```js Request
// POST /actions/core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ/outputs
{
"data": {
"authentication": "49509",
"inputs": {
"lead_type": "person"
}
}
}
```
```js Response
// POST /actions/core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ/outputs
{
"data": [
{
"type": "output_field",
"id": "id",
"title": "Lead ID",
"sample": "a3bx91sl"
},
{
"type": "output_field",
"id": "full_name",
"title": "Full Name",
"sample": "Neve Hahn"
},
{
"type": "output_field",
"id": "lead_status",
"title": "Lead Status",
"sample": "qualified"
}
]
}
```
Now, we can use these output fields when we configure the second action of our Zap.
When we configure the inputs of step 2, we can map Step 1's Output Field `full_name` to step 2's Input Field `engaged_party` by using double curly braces `{{step1_field_id}}`, or in this case `{{full_name}}`.
## Create a Zap
Now that we have an **Action**, **Authentication**, and **Inputs** for each step of our Zap, we can use the [POST /zaps](partner-solutions/api-reference/zaps/create-a-zap) endpoint to create a fully configured Zap.
```js Request
// POST /zaps
{
"data": {
"enabled": true,
"title": "Register engagement report on lead creation",
"steps": [
{
"action": "core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ",
"inputs": {
"lead_type": "person"
},
"authentication": "49509"
},
{
"action": "core:3ZYFzZKkjbDK2AwQopVqrZWL9pK",
"inputs": {
"engaged_party": "{{full_name}}",
"engagement_type": "lead_form_completed"
},
"authentication": "857610"
}
]
}
}
```
```js Response
{
"type": "zap",
"id": "233307281",
"is_enabled": true,
"last_successful_run_date": null,
"updated_at": "2024-03-28T17:25:08+00:00",
"title": "Register engagement report on lead creation",
"links": {
"html_editor": "https://zapier.com/editor/233307281?utm_source=partner&utm_medium=embed&utm_campaign=partner_api&referer=None"
},
"steps": [
{
"action": "core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ",
"authentication": "49509",
"inputs": null,
"title": null
},
{
"action": "core:3ZYFzZKkjbDK2AwQopVqrZWL9pK",
"authentication": "857610",
"inputs": null,
"title": null
}
]
}
```
It is expected that the returned Zap doesn't include any step `inputs`
Notice that we've combined all of the configurations we collected above to construct the body of this request.
* The `action` of each step is the `id` of the Action that the user selected.
* The `authentication` of each step is the `id` of the Authentication that the user selected.
* The `inputs` of each step is an object where each key is the `id` of an Input Field
* In the case of `engaged_party` (where we mapped an Output Field from step 1 to an Input Field of step 2), the value is the `id` of the Output Field from step 1 wrapped in double curly braces `{{...}}`
* In all other cases, the value is a static value that was selected or otherwise configured by the user
# Fields and Fieldsets
There are two main types of fields to consider when dealing with the Zapier API:
input fields and output fields:
Those that are provided to an Action so that it can run. They are analogous to
the arguments that a function takes.
Those that are returned by an Action. These may then be *mapped* into the input
fields of a subsequent Action in a Zap.
## Input Fields
To fetch the input fields for an Action, make a request to the
`/actions/{action_id}/inputs` endpoint:
```js Request
// POST /actions/core:853266/inputs
{
"data": {
"authentication": "762331",
"inputs": {}
}
}
```
```js Response
// POST /actions/core:853266/inputs
{
"data": [
{
"type": "input_field",
"id": "spreadsheet",
"default_value": "",
"depends_on": [],
"description": "The spreadsheet to add a new row",
"format": "SELECT",
"invalidates_input_fields": true,
"is_required": true,
"placeholder": "",
"title": "Spreadsheet",
"value_type": "STRING"
}
]
}
```
### Invalidation
The `invalidates_input_fields` field signifies whether the input fields should
be *refetched* from `/inputs` when the value of the given field is changed. A real-world example
of when this might be required is for spreadsheet apps. When adding a new row to
a sheet, the first input field allows the user to select the relevant sheet and,
once that is selected, more input fields become available (one for each column
in the selected sheet). If the user changes the sheet, then the input fields
must be refetched.
When making a request to refetch the input fields, make sure to include all the
fields that have been populated so far:
```js Request
// POST /actions/core:5381/inputs
{
"data": {
"authentication": "928117",
"inputs": {
"spreadsheet": "my_sheet"
}
}
}
```
```js Response
// POST /actions/core:5381/inputs
{
"data": [
{
"type": "input_field",
"id": "spreadsheet",
"title": "Spreadsheet",
"description": "The spreadsheet to add a row to.",
"value_type": "string",
"format": "SELECT",
"depends_on": [],
"is_required": true,
"invalidates_input_fields": true
},
{
"type": "input_field",
"id": "worksheet",
"title": "Worksheet",
"description": "The worksheet to add a row to.",
"value_type": "string",
"format": "SELECT",
"depends_on": ["spreadsheet"],
"is_required": true,
"invalidates_input_fields": true
},
{
"type": "input_field",
"id": "column_A",
"title": "Column A",
"description": "",
"value_type": "string",
"depends_on": [],
"is_required": false,
"invalidates_input_fields": false
},
{
"type": "input_field",
"id": "column_B",
"title": "Column B",
"description": "",
"value_type": "string",
"depends_on": [],
"is_required": false,
"invalidates_input_fields": false
}
]
}
```
In this example, if the user changes the value of `spreadsheet`, we should refetch `/inputs` and pass this new value to reload all fields.
If `worksheet` is modified, once again we'd refetch `/inputs` and reload the non-dependant fields.
This is because both `spreadsheet` and `worksheet` have `invalidates_input_fields` field set to `true`.
### Dependencies
Sometimes one input field *depends* on one or more other fields, meaning `/inputs` should be refetched
if any of the dependent fields are changed. This is represented via the `depends_on` array field, which has strings of field IDs in
it. Continuing with the spreadsheet example, the `worksheet` field depends on
the `spreadsheet` field. That is, if the user changes which spreadsheet they are
working in, the value of the worksheet field must be cleared (and `/inputs` refetched)---as the set of
inputs available for the action will now be different.
### Invalidation & Dependancy Flow
![Invalidation & Dependancy
Flow](https://cdn.zappy.app/9cff01e591e7c205326aa4b6b212c89a.jpg)
### Input Field Types
[Input Field Schema](/partner-solutions/api-reference/common-types/inputField)
The `value_type` key indicates what type of user data is accepted for a given field.
Per [the schema](/partner-solutions/api-reference/common-types/inputField), the options for `value_type` are: `STRING`, `NUMBER`, `INTEGER`, `BOOLEAN`, `ARRAY`, and `OBJECT`.
The following variants warrant additional consideration:
Array fields accept JSON arrays of the underlying value type. The `items`
key will be defined for arrays, and it will contain a key `type` with the
value type of the array items.
This input field:
```js
"column_names": {
"value_type": "ARRAY",
"items": {
"type": "STRING"
}
}
```
would accept this value:
```js
"column_names": ["Word", "Definition"]
```
Object fields accept JSON objects with arbitrary keys and *string* values.
This input field:
```js
"dictionary": {
"value_type": "OBJECT"
}
```
would accept this value:
```js
"dictionary": {
"apple": "A fruit!",
"salmon": "A fish!"
}
```
While boolean fields are logically `true` or `false`, their actual values can
change depending on the specific action. For this reason boolean values are paired with a `format`
of `SELECT` and options should be fetched from `/choices`. See [choices](/partner-solutions/workflow-api/Fields-and-Fieldsets#choices) for more information.
This input field:
```js
{
"type": "input_field",
"id": "replace_all_rows",
"format": "SELECT",
"value_type": "BOOLEAN"
...
}
```
would require a request to `/choices`;
```js
// repsonse from POST https://api.zapier.com/v2/actions/core:gq7MXdsdsaaqqDBj/inputs/replace_all_rows/choices
{
"id": "False",
"type": "choice",
"label": "No",
"value": "False"
},
{
"id": "True",
"type": "choice",
"label": "Yes",
"value": "True"
}
```
and ultimately would accept this value:
```js
"False"
```
Where this is the `id` of the selection. See [choices](/partner-solutions/workflow-api/Fields-and-Fieldsets#choices) for more information.
### Input Formats
The `format` key indicates how to present a given input field to the user, per [the schema](/partner-solutions/api-reference/common-types/inputField).
Its options are: `DATETIME`, `MULTILINE`, `PASSWORD`, `CODE`, `READONLY`, `FILE`, `SELECT`. Additional information on
how these fields are processed by Zapier can be found in our [help docs](https://help.zapier.com/hc/en-us/articles/8496259603341-Different-field-types-in-Zaps).
The following variants warrant additional consideration:
These fields are to be presented as text only, and require no input. The `description` field contains the text to be presented.
```js
"format": "READONLY"
```
These fields should contain a URL to a file, which will be fetched during zap execution by the zapier platform.
```js
"format": "FILE"
```
These fields require an additional request to `/choices` to fetch the choices available for
that input. See [choices](/partner-solutions/workflow-api/Fields-and-Fieldsets#choices) for more information.
```js
"format": "SELECT"
```
### Choices
[Choice Schema](/partner-solutions/api-reference/common-types/choice)
When the `format` key is equal to `SELECT`, the `/choices` endpoint returns a list of options from which the user should select.
Continuing the spreadsheet example, the first input field is (`spreadsheet`) which is
a `string` type. The `format` is `SELECT`, which implies this field has a set of
*choices* for the user to select from (which would normally be rendered as a
dropdown menu in the UI).
To fetch these, use the `/actions/{action_id}/inputs/{input_id}/choices`
endpoint:
```js Request
// POST /actions/core:853266/inputs/spreadsheet/choices
{
"data": {
"authentication": "762331",
"inputs": {}
}
}
```
```js Response
// POST /actions/core:853266/inputs/spreadsheet/choices
{
"data": [
{
"id": "ABCD12345",
"type": "choice",
"label": "Group Standup",
"value": "ABCD12345"
},
{
"id": "ABCDE123456",
"type": "choice",
"label": "Incident Tracking",
"value": "ABCDE123456"
}
]
}
```
So, this input field has only two choices available. When making a zap pass the `id` of the selected choice for the desired input.
### Fieldsets
[Fieldset Schema](/partner-solutions/api-reference/common-types/fieldset)
Input fields may be *grouped* together into fieldsets (just like in HTML). This
gives a hint as to how the fields should be rendered in the UI.
Only one level of nesting is supported (a Fieldset cannot be contained within
another Fieldset).
### Informational Fields
[Info Field Schema](/partner-solutions/api-reference/common-types/infoField)
An informational field is one that has no input, but contains some
markdown-formatted text which should be rendered in the relevant position in the
form. This is often used to give helpful tips to the user.
## Output Fields
[Output Field Schema](/partner-solutions/api-reference/common-types/outputField)
Once the user has populated all the input fields, we can determine which output
fields are available from the given action using the
`/actions/{action_id}/outputs` endpoint:
```js Request
// POST /actions/core:853266/outputs
{
"data": {
"authentication": "762331",
"inputs": {
"worksheet": "ABC123",
"spreadsheet": "EFGH456"
}
}
}
```
```js Response
// POST /actions/core:853266/outputs
{
"data": [
{
"type": "output_field",
"id": "id",
"title": "Id",
"sample": "1"
},
{
"type": "output_field",
"id": "COL$A",
"title": "Col$A",
"sample": "Value in column A"
},
{
"type": "output_field",
"id": "COL$B",
"title": "Col$B",
"sample": "Value in column B"
},
{
"type": "output_field",
"id": "COL$C",
"title": "Col$C",
"sample": "Value in column C"
}
]
}
```
In reality, many more output fields would likely be available. For details on
how these can be mapped into the input fields of subsequent steps in a Zap, see
[Building a Zap](/partner-solutions/workflow-api/Building-a-Zap).
# Hardcoding an Action
To help focus the user experience, it can be helpful to hardcode a certain Action to guide users in selecting the most appropriate action for their use-case.
While it may be tempting to reuse the `id` field returned by the `/actions`
endpoint, this is unsupported. The `id` field should not be considered stable, and the `key` field should instead be used.
To hardcode an action, first make a request to the [`/actions` endpoint](/partner-solutions/api-reference/actions/get-actions), filtered by App using the `app` query param (e.g.
`/actions?app=dc4dfc3e-c5bd-47a4-ac85-6d5ef8a50c50`).
This ID is guaranteed to remain the same for a particular
App, even as different versions of the App are promoted in the background.
i.e.
`/api/v2/actions?app=768dedb4-54b8-4585-a166-2140216e9b13`
This is the value
provided in the Zapier Developer Platform, and remains the same for the same
action across versions. The Action ID field returned can then be used as usual to create a Zap.
# Selecting an Action
An Action is an operation that can be performed against a third-party API; either a `READ` or a `WRITE`.
[Action Schema](/partner-solutions/api-reference/common-types/action)
Once you've [selected an app](/partner-solutions/api-reference/apps/get-apps-\[v2]), we can fetch the list of available actions for a selected App by making a request to the [`/actions` endpoint](/partner-solutions/api-reference/actions/get-actions):
```js
// GET /actions?app=4b3920d6-1d5a-4071-b837-9383dc511b80&action_type=READ
{
"data": [
{
"type": "action",
"id": "core:853266",
"action_type": "READ",
"title": "New Lead",
"description": "Triggers when a new lead is added to SuperExampleCRM",
"is_instant": true
},
{
"type": "action",
"id": "uag:1f188536-6dd0-4172-8414-2b90914ddee9",
"action_type": "READ",
"title": "New Deal",
"description": "Triggers when a new deal is added to SuperExampleCRM",
"is_instant": false
}
]
}
```
`READ` and `WRITE` map to *Trigger* and *Action* respectively In the Zapier ecosystem.
Our user can then select one of these Actions that they wish to use as the Trigger for their Zap. The `id` field is accepted in several other endpoints.
Looking to focus your experience to just a few actions? Check out [Hardcoding an Action](/partner-solutions/workflow-api/Hardcoding-an-Action).
# Selecting an Authentication
Support users in selecting 3rd party authentications, either through an existing authentication or by adding new.
An Authentication is a set of user credentials for an [App](/partner-solutions/api-reference/apps/get-apps-\[v2]) that is stored securely by Zapier. When required, a user must select which of the Authentications they have for that App (they may have multiple) that they would like to use when a Zap executes.
[Authentication Schema](/partner-solutions/api-reference/common-types/authentication)
We can fetch a list of Authentications available for an App by making a request to the [`/authentications` endpoint](/partner-solutions/api-reference/authentications/get-authentications):
```js
// GET /authentications?app=4b3920d6-1d5a-4071-b837-9383dc511b80
{
"data": [
{
"type": "authentication",
"id": "49509",
"app": "4b3920d6-1d5a-4071-b837-9383dc511b80",
"title": "SuperExampleCRM (wade@zapier.com)",
"is_expired": false
},
{
"type": "authentication",
"id": "96983",
"app": "4b3920d6-1d5a-4071-b837-9383dc511b80",
"title": "SuperExampleCRM (bryan@zapier.com)",
"is_expired": false
}
]
}
```
Our user can then select one of these Authentications to use with a Zap.
## When no Authentications exist
It's possible that the user doesn't have *any* Authentications for an App they've picked, as in every case when it's a new Zapier account. In these cases the `/authentications` endpoint will return an empty list under the `data` key. In this scenario, we should direct the user to the url provided by the `/apps` endpoint under the `links.connect_new_authentication` key to add a new Authentication.
This is also the best approach to take if you want to offer the user the option to use a new Authentication with this Zap, even if they already have Authentications available. (e.g. If the user wants to use a different SuperExampleCRM account than the ones already linked to Zapier)
If `links.connect_new_authentication` is `null`, then this app doesn't require authentication, and `null` should be passed instead of a valid id. Read more about that [below](/partner-solutions/workflow-api/Selecting-an-Authentication#when-authentication-is-not-required).
### Directing the user to create a new Authentication
The best way to use this `links.connect_new_authentication` link is as follows:
In this popup, the user will be prompted to authenticate with the app, and to allow Zapier to access that app.
A message with that type will be posted when the auth flow in the popup is complete.
Afterwards, use that `authentication_id` to continue the Zap creation process
```js
// 1. Open a popup window to the `links. connect_new_authentication` url.
const authPopup = window.open(app.links. connect_new_authentication, '_blank', 'width=1280,height=1024');
if (!authPopup) {
alert("Please allow popups to continue.");
return;
}
// 2. Add an event listener to be alerted when the user has completed the auth flow.
window.addEventListener('message', function(event) {
if (event.origin === 'https://zapier.com') {
const action = event.data;
if (action?.type === "zapier.popup.close") {
if (authPopup) { // In case popup has already been closed
authPopup.close();
}
const new_authentication_id = action.authentication_id;
// 3. Use `new_authentication_id` to continue the Zap creation process.
// The `new_authentication_id` is the id of the authentication that the
// user just created.
}
}
});
```
Looking to add an authentication to your own app? You can supply auth details directly and streamline the process. Check out [Adding an Authentication](/partner-solutions/workflow-api/adding-an-authentication).
## When Authentication is not required
Some apps don't require authentication at all - like Webhooks. You'll know this is the case when fetching the app and it's not possible to add a new authentication.
```js Sample response from /apps
...
"links": {
"connect_new_authentication": null
},
...
```
When creating zaps with these apps, `null` should be passed in place of a valid authentication id;
```js Sample POST to /inputs
// POST https://api.zapier.com/v2/actions/core:8yjfgwyq03zskh3LOe9jPa5dOeW/inputs
{
"data": {
"authentication": null,
"inputs": {}
}
}
```
```js Sample POST to /zaps
// POST https://api.zapier.com/v2/zaps
...
"steps": [
...
{
"action": "core:3ZYFzZKkjbDK2AwQopVqrZWL9pK",
"inputs": {
"url": "https://greatWebhooks.com/myWebhook"
},
"authentication": null
}
]
...
```
# Adding an Authentication to your app
Reduce friction when adding an authentication to your own app.
[Adding a missing authentication](/partner-solutions/workflow-api/Selecting-an-Authentication#when-no-authentications-exist) for 3rd party apps can be straightforward, but if it's an authentication to your app it can result in sub-optimal UX.
> *"If an end user is logged into my app already, why do I need to do the oauth flow for my own app? Why can't I skip it?"*
### Adding an authentication to your own app
When you created your app on Zapier's Developer Platform, a required step was to [define the fields required for authentication](https://platform.zapier.com/build/auth). Depending on your authentication scheme the `authentication_fields` below will differ.
Leveraging the [Create Authentication](/partner-solutions/api-reference/authentications/create-authentication) endpoint, supply your app's required `authentication_fields` in the request.
```js Example with Basic Authentication
// POST /authentications
"data": {
"app": "{MY_APP_ID}",
"title": "{AUTHENTICATION_NAME}",
"authentication_fields": {
"username": "{MY_USERS_USERNAME}",
"password": "{MY_USERS_PASSWORD}"
}
}
```
```js Example with API Key Authentication
// POST /authentications
"data": {
"app": "{MY_APP_ID}",
"title": "{AUTHENTICATION_NAME}",
"authentication_fields": {
"api_key": "{MY_USERS_API_KEY}",
}
}
```
Be sure to supply whatever `authentication_fields` are required by your apps' authentication scheme.
Pick an authentication name that'll be recognizeable to a user should they find it outside of this flow, such as "Carter's LinkedIn Authentication", or "Micah's Slack Authentication"
A successful response from this request will include an `id`, which can be used in subsequent steps.
# Embedding the Zapier Editor
With an embedded Zap editor in your product, your users can create and edit their Zaps without leaving your app.
### Prerequisites
* Your app has passed the review process and is Published in the Zapier App Directory
* Any [Zap templates](/partner-solutions/api-reference/zap-templates/get-zap-templates) you want to query have been reviewed and made public
### Example implementation
Looking for even more customization? Learn about [creating Zaps directly with the Workflow API](/partner-solutions/workflow-api/Building-a-Zap).
## Option 1: Create Zaps from Zap Templates
Use the Workflow API to query the public Zap Templates featuring your integration (using the [`GET /v1/zap-templates` endpoint](/partner-solutions/api-reference/zap-templates/get-zap-templates)) and feature them in your product. When a user chooses a Zap template they'd like to try, use the `create_url` value as the source to load in an embedded frame such as:
```js
```
Where `https://api.zapier.com/v1/embed/trello/create/113` is the `create_url` value of the Zap template.
Optionally, you can add additional parameters to the `create_url` to [prefill the user's Zap](/partner-solutions/pre-filled-zaps) with custom values, simplifying the Zap configuration for the user.
## Option 2: Create Zaps without Zap Templates
You can also facilitate a user's Zap creation via URL parameters instead of an existing Zap Template. This provides flexibility to redirect users to a pre-populated Zap Editor with known context from your app without publishing a Zap Template for each specific use case.
[Use the Zap Pre-fill Generator](/partner-solutions/pre-filled-zaps#creating-pre-filled-zaps-using-the-zap-generator) to easily construct pre-filled Zaps for your embedded editor experience or as a lightweight entry point into the Zap Editor from your app.
## Editing existing Zaps
Use the Workflow API to load a user's Zaps (using the [`GET /v1/zaps` endpoint](/partner-solutions/api-reference/zaps/get-zaps-\[v1])). When the user chooses to open or edit a Zap use the the `url` value of the Zap as the source of an embedded frame like this:
```js
```
Where `https://zapier.com/editor/123456` is the `url` of the Zap to be edited.
If you prefer, you can open these URLs in a separate window, new tab, or popup from your app.
## `postMessage` events
If you decide to embed the Zap editor within your product you can listen to [message events from `postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/message_event) to help you improve the interactivity with the iframe (e.g. automatically close the iframe modal.)
The messages available include:
* `zap:unpause` = Zap turned on / published
* `zap:unpause:done` = Zap turned on / published (success)
* `zap:unpause:fail` = Zap turned on / published (failure)
* `zap:pause` = Zap turned off
* `zap:pause:done` = Zap turned off (success)
* `zap:pause:fail` = Zap turned off (failure)
## Turning off a Zap
The API does not currently have an endpoint to turn off/on a user's Zaps. If your Zapier app uses [Webhook Subscriptions](https://platform.zapier.com/build/hook-trigger), you can send a `DELETE` to the unique target URL that was provided when the subscription was created and that will then pause/turn off a Zap.
# Integrate the Workflow API
Our most powerful tool for building native workflows in your product
The Workflow API is a powerful tool for building native feeling integrations and workflows for your end users. It allows your app access to the industry's largest integration directory that spans over 7,000 apps, enabling you to easily build and manage the most impactful workflows for your users. This tool enables users to discover, craft, and manage workflows from directly within your product while being customized to your product's look and feel, offering a seamless integration experience.
Whether you want to build an end-to-end native feeling integration for your users, or surface the most popular zap templates customized to your product's branding, the Workflow API extends your product's functionality by showcasing premier Zapier-powered workflows exactly where your users need them the most—in the very heart of your tool. It allows for customization in styling, simplifies the Zap setup process for users, and provides immediate access to pertinent Zap details, ensuring a cohesive and intuitive user experience.
The Workflow API is accessible for all publicly listed integrations.
## Capabilities
Get a list of all the apps available in Zapier's app directory to power your own integration marketplace and show your users all the integration possibilities with your Zapier integration.
Have complete style control over how you present Zap templates in your product. The Workflow API gives you access to the raw Zap Template data so you can give your users access to your Zap templates with your product's style, look and feel.
Define input fields on behalf of your users, simplying the experience of setting up their Zap
Embed our Zap Editor to allow your users to create new Zaps and modify existing ones, without needing to leave your product.
Build native feeling integrations to your app powered by the Workflow APIs to enable your users to create automated workflows from within your product.
Show users the Zaps they have set up from right within your product, keeping them on your site longer and giving them complete confidence in your Zapier integration.
***
Check out the recent ZapConnect Session on the Workflow API, discussing features & popular use cases.
# Known Limitations
Creating workflows using the Zapier Workflow API is a recent addition, and there are some known limitations.
* Zaps are currently limited to two steps. eg. a `READ` action and a `WRITE` action. Zaps with more than two steps are not supported.
* Zaps can only be created with Public Apps. Private Apps are not currently supported.
* More complex Action types, such as Searches, Filters, and Paths, are not supported.
* The API does not currently have an endpoint to turn off/on a user's Zaps. If your Zapier app uses [Webhook Subscriptions](https://platform.zapier.com/build/hook-trigger), you can send a `DELETE` to the unique target URL that was provided when the subscription was created and that will then pause/turn off a Zap.
* Webhook `READ` actions are not currently supported.
If you've got a use case that you think *should* be supported, let us know!
# Retrieving Apps on Zapier
Listing apps available on Zapier is a simple way to show users all of what's possible on Zapier
### Prerequisites
* Your app needs to be published as a [public integration](https://platform.zapier.com/quickstart/private-vs-public-integrations) in Zapier's App Directory.
### Example Implementation
## Fetching a list of Apps available on Zapier
Following the [authentication guide](/partner-solutions/api-reference/authentication), receive a token from the Zapier Workflow API.
Leveraging the [`/apps` endpoint](/partner-solutions/api-reference/apps/get-apps-\[v2]), filter the list of apps to meet your needs, available query params include:
* `category` - could be useful for listing popular CRMs for example.
* `query` - a string by which to seach for for apps.
* `ids` - filter the list to specific apps, by their associated identifier.
Shape the apps returned to match the look and feel of your product, showcasing the additional apps available ✨
# Retrieving Zap Templates
Zap templates are pre-made Zaps that help users discover popular use cases for automating their work. Each template features a specific use case and the apps needed for it to work.
### Prerequisites
* Your app needs to be published as a [public integration](https://platform.zapier.com/quickstart/private-vs-public-integrations) in Zapier's App Directory.
* Only `published` Zap templates will be visible through the API.
### Example Implementation
## Fetching a list of Zap Templates
You can find your Client ID in the [Zapier Developer Platform](https://developer.zapier.com/) under `Embed` → `Settings` → `Credentials`
![Client ID and Secret](https://cdn.zappy.app/cb3660c17a3d26b36f438ab80c0860d5.png)
Your application's **Client ID** and **Client Secret** are only available after you've published your app as a [public integration in Zapier's App Directory](https://platform.zapier.com/quickstart/private-vs-public-integrations).
Regenerating your client secret will invalidate any previous secret.
Leverage the [`/zap-templates` endpoint](/partner-solutions/api-reference/zap-templates/get-zap-templates) to fetch zap templates.
* You can optionally provide an `apps` query param to only return Zap Templates that have both your app and one of the app ids provided.
By default, all zap templates paired with your app are returned.
Shape the zap templates returned to match the look and feel of your product, workflow opportinuties that are 1-click away ✨
# Retrieving a list of users Zaps
Listing a users zaps reveals existing workflows created by users.
### Prerequisites
* Your app needs to be published as a [public integration](https://platform.zapier.com/quickstart/private-vs-public-integrations) in Zapier's App Directory.
### Example Implementation
## Fetching a list of Zaps for a user
Following the [authentication guide](/partner-solutions/api-reference/authentication), receive a token from the Zapier Workflow API.
Leverage the [`/zaps` endpoint](/partner-solutions/api-reference/zaps/get-zaps-\[v2]) to fetch a users zaps.
* You can optionally filter the list by specific input values, which can be helpful for instances where a user has a zap associated to specific resources. Example might be a specific trello board or google sheet. See [the api-reference](/partner-solutions/api-reference/zaps/get-zaps-\[v2]) for more info.
* Leverage the `expand` parameter to return
additional zap detail such as step information.
* Use the `zap:all` scope to return *all* owned zaps on a users acccount regardless of paired apps.
By default, only zaps that contain the authenticating app and that are **owned** by the user are returned. Shared zaps or zaps the user may have permission to view/edit will not be returned.
Shape the zaps returned to match the look and feel of your product, showcasing any existing workflows ✨
# Testing a Step
Step testing allows for the validation of a configured step, executing any `READ` or `WRITE` actions.
After supplying the required inputs for a given action, it can be helpful to validate the configuration by testing the action, in a similar way the main editor at zapier.com requires step testing. While not required via this API, it can be helpful to test actions;
* When there are common configuration challenges specific to your use case.
* To reduce the chance of user error when creating workflows.
Following the steps to [Build a Zap](/partner-solutions/workflow-api/Building-a-Zap), retrieve all necessary inputs and authentications.
Leveraging the [/test](/partner-solutions/api-reference/actions/step-test) endpoint, make a request supplying all of the necessary `inputs` along with an `authentication` - or `null` if none is required.
For example for an authenticated action with id `core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ`, we might make the following request:
```js Request
//POST /actions/core:wJ3PxHpNArZ8MqvloW3L1ZyMDQ4nJ/test
"data": {
"authentication": "KrG4mmZs",
"inputs" : {
"channel": "G45J5309P",
"text": ":rotating_light: THIS IS A TEST :rotating_light:",
"as_bot": "yes",
"username": "Step Test",
"icon": ":robot:"
}
}
```
```js Successful Test Response
{
"links": {
"next": null,
"prev": null
},
"meta": {
"count": 1,
"limit": null,
"offset": null
},
//Note: the data returned along with it's schema is unique to the action being performed.
"data": [
{
"channel": "G45J5309P",
"ts": "1704292753.233659",
"message": {
"type": "message",
"subtype": "bot_message",
"text": ":rotating_light: THIS IS A TEST :rotating_light:",
"ts": "1704292753.233659",
"username": "Step Test",
"icons": {
"emoji": ":robot:"
}
}
...
}
]
}
```
```js Successful Test Response (no results)
// Note: Tests that are configured correctly but return 0 results are returned with a 200 OK status code.
{
"links": {
"next": null,
"prev": null
},
"meta": {
"count": 0,
"limit": null,
"offset": null
},
"data": []
}
```
```js Failed Test Response
{
"errors": [
{
"status": 400,
"code": "test_failed",
"title": "ActionTestError",
"detail": "Error during execution: Error from Slack: channel_not_found",
"source": null,
"meta": {
"source": "ZAPIER",
"full_details": {
"message": "Error during execution: Error from Slack: channel_not_found",
"code": "test_failed"
}
}
}
]
}
```
Note that in the event of a successful test, the `data` object returned is directly from the 3rd party app, and therefore has no defined schema.
If possible, clean up any created resources as part of a successful test. Remember the test executes the action, so these assets could cause confusion for users should they discover them after a successful test.
# Embed the Workflow Element
The Workflow Element is a prebuilt UI component that offers the quickest—and easiest—way to surface your Zapier integration directly within your own product.
As of 6/25/2024, the Workflow Element has fully replaced our previous suite of embedded UI Elements: the Full Zapier Experience, the Zap Templates Element, the App Directory Element, and the Zap Manager Element.
With the Workflow Element, you can create a seamless experience for your users that allows them to automate and manage their workflows right from your app.
Whether you want to surface pre-built zap templates in your product, allow your users to manage their zaps from your product, enable thousands of integrations to your app, or allow your users to build workflows without ever leaving your product, the Workflow Element can be customized to your particular use case, and plugged into your product in minutes, no engineering resources required!
[Get started with the Workflow Element](https://zapier.com/partner/embed/workflow)
## Capabilities
The Workflow Element is the easiest and best way to bring automation to your users, with customizable features that allow your users to discover, use, create, and manage automations without ever leaving your product.
### Discover
Take advantage of Zapier's extensive and industry-leading app directory by embedding a customized version of Zapier's App Directory. Users can search through thousands of available apps to pair with yours and see popular workflows at a glance. Our generator allows you to customize the number of apps that are displayed and allows you to exclude apps by name or by app category.
### Use
Easily surface pre-built zap templates in your product for your users to use. You can surface the most popular zap templates for your app, or choose specific zap templates to showcase.
### Create
When clicking on a template or app listing, a modal opens up within your site, prompting users to log in or sign up if they don't have a Zapier account. Enable Quick Account Creation to make this even simpler for users. Once done, the Zap Editor will open on this same modal, and users can create Zaps.
### Manage
Once users have created Zaps, they're able to see them directly inside your product. They can view their Zaps and see their status at a glance without ever having to leave your product
See more ways our partners have used the Workflow Element in our [Embed Gallery](https://zapier.com/developer-platform/partner-embeds)
### Prerequisites
In order to embed the Workflow Element:
* Your app has to have passed the review process and is Published in the Zapier App Directory, i.e. your app is in the Beta or Public stage.
* You have created Zap templates for your app, and they have been reviewed and made public
## How to Embed the Workflow Element
The Workflow Element uses iframes, which are blocked by default for security purposes. Provide us with a list of domains you intend to embed it in and we'll permit these specific domains. Head to the [Zapier Developer Platform](https://developer.zapier.com/), find your app and select settings to get started.
![Customize your embed](https://cdn.zappy.app/da56277eb07303d8ce8ef42cafc8511e.png)
You can [configure your embed](https://zapier.com/partner/embed/workflow) and select your app.
Using the sidebar panel in the tool, customize your embed. When it's ready, click Generate Code.
![Customize your embed](https://cdn.zappy.app/d6113da7c1934b6e0402453b77f2fb8d.png)
The preview shows what your embed looks like for a user logged in to Zapier. The Intro to Zapier is only shown to Zapier users who are not logged in. This is why if you toggle to show the Intro to Zapier, you will not see it appear in the preview, but your users who are not logged in to Zapier will see a short section explaining Zapier at the top of the embed.
![Code generator](https://cdn.zappy.app/72ecc13062e1b04b931ebac5483e4962.png)
To ensure a consistent experience for your users and avoid common challenges, it's worth reading over our [Security Practices.](/partner-solutions/elements-security)
Consider augmenting with [Quick Account Creation](/partner-solutions/quick-account-creation) - it makes signing up to Zapier more seamless. Users are able to bypass signup and onboarding and and jump right into creating a Zap with your integration with the click of a button. Note, Quick Account Creation only works if your embed is behind a login wall.We do not support iframes inside of iframes. If you try to embed the Workflow Element in an iframe, links will open in pop ups rather than in an iframe.
# Dynamic Dropdowns
Sometimes, API endpoints require clients to specify a parent object in order to create or access the child resources. For instance, specifying a spreadsheet id in order to retrieve its worksheets. Since people don't speak in auto-incremented ID's, it is necessary that Zapier offer a simple way to select that parent using human readable handles.
Our solution is to present users a dropdown that is populated by making a live API call to fetch a list of parent objects. We call these special dropdowns "dynamic dropdowns."
## Definition
To define one you include the `dynamic` property on the `inputFields` object. The value for the property is a dot-separated *string* concatenation.
```js
//...
issue: {
key: 'issue',
//...
create: {
//...
operation: {
inputFields: [
{
key: 'project_id',
required: true,
label: 'This is a dynamic dropdown',
dynamic: 'project.id.name'
}, // will call the trigger with a key of project
{
key: 'title',
required: true,
label: 'Title',
helpText: 'What is the name of the issue?'
}
]
}
}
}
```
The dot-separated string concatenation follows this pattern:
* The key of the trigger you want to use to power the dropdown. *required*
* The value to be made available in bundle.inputData. *required*
* The human friendly value to be shown on the left of the dropdown in bold. *optional*
In the above code example the dynamic property makes reference to a trigger with a key of project. Assuming the project trigger returns an array of objects and each object contains an id and name key, i.e.
```js
[
{ id: "1", name: "First Option", dateCreated: "01/01/2000" },
{ id: "2", name: "Second Option", dateCreated: "01/01/2000" },
{ id: "3", name: "Third Option", dateCreated: "01/01/2000" },
{ id: "4", name: "Fourth Option", dateCreated: "01/01/2000" },
];
```
The dynamic dropdown would look something like this.
![screenshot of dynamic dropdown in Zap Editor](https://cdn.zappy.app/6a90fcc532704f6c14b91586f5cd1d5b.png)
## Use a resource
In the first code example the dynamic dropdown is powered by a trigger. You can also use a resource to power a dynamic dropdown. To do this combine the resource key and the resource method using camel case.
```js index.js
const App = {
// ...
resources: {
project: {
key: "project",
// ...
list: {
// ...
operation: {
perform: () => {
return [{ id: 123, name: "Project 1" }];
}, // called for project_id dropdown
},
},
},
issue: {
key: "issue",
// ...
create: {
// ...
operation: {
inputFields: [
{
key: "project_id",
required: true,
label: "Project",
dynamic: "projectList.id.name",
}, // calls project.list
{
key: "title",
required: true,
label: "Title",
helpText: "What is the name of the issue?",
},
],
},
},
},
},
};
```
## Hide the trigger
In some cases you will need to power a dynamic dropdown but do not want to make the Trigger available to the end user. Here it is best practice to create the trigger and set `hidden: true` on it's display object.
```js
const App = {
// ...
triggers: {
new_project: {
key: "project",
noun: "Project",
// `display` controls the presentation in the Zapier Editor
display: {
label: "New Project",
description: "Triggers when a new project is added.",
hidden: true,
},
operation: {
perform: projectListRequest,
},
},
another_trigger: {
// Another trigger definition...
},
},
};
```
## Dependencies between dropdowns
You can have multiple dynamic dropdowns in a single Trigger or Action. And a dynamic dropdown can depend on the value chosen in another dynamic dropdown when making it's API call. Such as a Spreadsheet and Worksheet dynamic dropdown in a trigger or action. This means you must make sure that the key of the first dynamic dropdown is the same as referenced in the trigger powering the second.
Let's say you have a Worksheet trigger with a `perform` method similar to this.
```js
perform: async (z, bundle) => {
const response = await z.request("https://example.com/api/v2/projects.json", {
params: {
spreadsheet_id: bundle.inputData.spreadsheet_id,
},
});
// response.throwForStatus() if you're using core v9 or older
return response.data; // or response.json if you're using core v9 or older
};
```
And your New Records trigger has a Spreadsheet and a Worksheet dynamic dropdown. The Spreadsheet dynamic dropdown must have a key of `spreadsheet_id`. When the user selects a spreadsheet via the dynamic dropdown the value chosen is made available in `bundle.inputData`. It will then be passed to the Worksheet trigger when the user clicks on the Worksheet dynamic dropdown.
```js
const App = {
// ...
triggers: {
// ...
issue: {
key: "new_records",
// ...
operation: {
inputFields: [
{
key: "spreadsheet_id",
required: true,
label: "Spreadsheet",
dynamic: "spreadsheet.id.name",
},
{
key: "worksheet_id",
required: true,
label: "Worksheet",
dynamic: "worksheet.id.name",
},
],
},
},
},
};
```
The [Google Sheets](https://zapier.com/apps/google-sheets/integrations#triggers-and-actions) integration is an example of this pattern.
> Note: Even if a trigger to power a dynamic dropdown is hidden for direct use, you still need to define the input fields, and whether these are required. In the above example, when the Worksheet trigger defines a required `spreadsheet_id` field (which also needs to be a dynamic dropdown), products such as the editor will use this in two ways. Firstly, the worksheet field will be disabled until the spreadsheet field has been set. Secondly, when the user changes the spreadsheet field, the worksheet field gets cleared.
## Detect when a trigger is used for a dynamic dropdown
If you want your trigger to perform specific scripting for a dynamic dropdown you will need to make use of `bundle.meta.isFillingDynamicDropdown`. This can be useful if need to make use of [pagination](/platform/build-cli/faqs#whats-the-deal-with-pagination-when-is-it-used-and-how-does-it-work) in the dynamic dropdown to load more options.
```js
const App = {
// ...
resources: {
project: {
key: "project",
// ...
list: {
// ...
operation: {
canPaginate: true,
perform: () => {
if (bundle.meta.isFillingDynamicDropdown) {
// perform pagination request here
} else {
return [{ id: 123, name: "Project 1" }];
}
},
},
},
},
issue: {
key: "issue",
// ...
create: {
// ...
operation: {
inputFields: [
{
key: "project_id",
required: true,
label: "Project",
dynamic: "projectList.id.name",
}, // calls project.list
{
key: "title",
required: true,
label: "Title",
helpText: "What is the name of the issue?",
},
],
},
},
},
},
};
```
## Link a search action
This feature makes it easier for users to handle the following scenario in a workflow that has multiple steps:
* The value for the input field depends on an output field from an earlier step.
* The value of that output field cannot be used directly.
* They need an additional search step that takes the output they *cannot* use directly, and translate it into something they *can*.
**Example:** Let's say the input field takes the ID of a lead. The user could select a lead from the dynamic dropdown, but then the workflow would act on the same lead every time it runs. An earlier step returns the email address of the lead, but not their ID. The user will need to prepend a search-step that takes the email address and returns the ID.
Users can do this themselves, but by using this feature, Zapier products can make this task easier.
### How it works for the user
In the Zap editor for example, dynamic dropdowns that use this feature will display a
button next to the dynamic dropdown. When the user clicks the button, the right search step is automatically prepended, and correct output field mapped into the dynamic dropdown.
![](https://cdn.zappy.app/c6bd53c4bf3efe9870493dc7c3c2dafc.gif)
### How to configure it
In the definition of the input field, configure `search` with a value of `.`.
* Replace `` with the `key` of the search action that should prepeded to the user's workflow.
* Replace `` with the `key` of the output field from that search action that should be mapped as value for the input field.
Here's an example:
```js
{
key: 'project_id',
required: true,
label: 'Project',
dynamic: 'list_projects.id.name',
search: 'search_projects.id',
}
```
# Frequently Asked Questions
### Why doesn't Zapier support newer versions of Node.js?
We run your code on AWS Lambda, which only supports a few [versions](https://docs.aws.amazon.com/lambda/latest/dg/programming-model.html) of Node. Sometimes that doesn't include the latest version. Additionally, with thousands of integrations running on the Zapier platform, we have to be sure upgrading to the latest Node version will not have a negative impact.
### How do I manually set the Node.js version to run my integration with?
Update your `zapier-platform-core` dependency in `package.json`. Each major version ties to a specific version of Node.js. You can find the mapping [here](https://github.com/zapier/zapier-platform/blob/main/packages/cli/src/version-store.js). We only support the version(s) supported by [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/programming-model.html).
**IMPORTANT CAVEAT**: AWS periodically deprecates Node versions as they reach EOL. They announce this[on their blog](https://aws.amazon.com/blogs/developer/node-js-6-is-approaching-end-of-life-upgrade-your-aws-lambda-functions-to-the-node-js-10-lts/). Similar info and dates are available on [github](https://github.com/nodejs/Release). Well before this date, we'll have a version of `core` that targets the newer Node version.
If you don't upgrade before the cutoff date, there's a chance that AWS will throw an error when attempting to run your integration's code. If that's the case, we'll instead run it under the oldest Node version still supported. All that is to say, **we may run your code on a newer version of Node.js than you intend** if you don't update your integration's dependencies periodically.
### When to use placeholders or curlies?
You will see both [template literal placeholders](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Expression_interpolation) `${var}` and (double) "curlies" `{{var}}` used in examples.
In general, use `${var}` within functions and use `{{var}}` anywhere else.
Placeholders get evaluated as soon as the line of code is evaluated. This means that if you use `${process.env.VAR}` in a trigger configuration, `zapier push` will substitute it with your local environment's value for `VAR` when it builds your integration and the value set via `zapier env:set` will not be used.
> If you're not familiar with [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), know that `const val = "a" + b + "c"` is essentially the same as ``const val = `a$``{b}`` c` ``.
### Does Zapier support XML (SOAP) APIs?
Not natively, but it can! Users have reported that the following `npm` modules are compatible with the CLI Platform:
* [pixl-xml](https://github.com/jhuckaby/pixl-xml)
* [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js)
* [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
Since core v10, it's possible for [shorthand requests](#shorthand-http-requests) to parse XML. Use an `afterResponse` [middleware](#using-http-middleware) that sets `response.data` to the parsed XML:
```js
const xml = require("pixl-xml");
const App = {
// ...
afterResponse: [
(response, z, bundle) => {
// Only works on core v10+!
response.throwForStatus();
response.data = xml.parse(response.content);
return response;
},
],
// ...
};
```
### Is it possible to iterate over pages in a polling trigger?
Yes, though there are caveats. Your entire function only gets 30 seconds to run. HTTP requests are costly, so paging through a list may time out (which you should avoid at all costs).
```js
// some async call
const makeCall = (z, start, limit) => {
return z.request({
url: "https://jsonplaceholder.typicode.com/posts",
params: {
_start: start,
_limit: limit,
},
});
};
// triggers on paging with a certain tag
const performPaging = async (z, bundle) => {
// array of promises
const promises = [];
// 5 requests with page size = 3
let start = 0;
const limit = 3;
for (let i = 0; i < 5; i++) {
promises.push(makeCall(z, start, limit));
start += limit;
}
// send requests concurrently
const responses = await Promise.all(promises);
return responses.map((res) => res.data);
};
module.exports = {
key: "paging",
noun: "Paging",
display: {
label: "Get Paging",
description: "Triggers on a new paging.",
},
operation: {
inputFields: [],
perform: performPaging,
},
};
```
If you need to do more requests conditionally based on the results of an HTTP call (such as the "next URL" param or similar value), using `async/await` (as shown in the example below) is a good way to go. If you go this route, only page as far as you need to. Keep an eye on the polling [guidelines](https://zapier.com/developer/documentation/v2/deduplication/), namely the part about only iterating until you hit items that have probably been seen in a previous poll.
```js
// a hypothetical API where payloads are big so we want to heavily limit how much comes back
// we want to only return items created in the last hour
const asyncExample = async (z, bundle) => {
const limit = 3;
let start = 0;
const twoHourMilliseconds = 60 * 60 * 2 * 1000;
const hoursAgo = new Date() - twoHourMilliseconds;
let response = await z.request({
url: "https://jsonplaceholder.typicode.com/posts",
params: {
_start: start,
_limit: limit,
},
});
let results = response.data; // response.json if you're using core v9 or older
// keep paging until the last item was created over two hours ago
// then we know we almost certainly haven't missed anything and can let
// deduper handle the rest
while (new Date(results[results.length - 1].createdAt) > hoursAgo) {
start += limit; // next page
response = await z.request({
url: "https://jsonplaceholder.typicode.com/posts",
params: {
_start: start,
_limit: limit,
},
});
results = results.concat(response.data);
}
return results;
};
```
### What's the deal with pagination? When is it used and how does it work?
Paging is **only used when a trigger is part of a dynamic dropdown**. Depending on how many items exist and how many are returned in the first poll, it's possible that the resource the user is looking for isn't in the initial poll. If they hit the "see more" button, we'll increment the value of `bundle.meta.page` and poll again.
Paging is a lot like a regular trigger except the range of items returned is dynamic. The most common example of this is when you can pass a `offset` parameter:
```js
const perform = async (z, bundle) => {
const response = await z.request({
url: "https://example.com/api/list.json",
params: {
limit: 100,
offset: 100 * bundle.meta.page,
},
});
return response.data; // or response.json you're using core v9 or older
};
```
If your API uses cursor-based paging instead of an offset, you can use `z.cursor.get` and `z.cursor.set`:
```js
const perform = async (z, bundle) => {
let cursor;
// if fetching a page other than the first (first page is 0),
// get the cursor stored after fetching the previous page.
if (bundle.meta.page > 0) {
cursor = await z.cursor.get();
// if the previous page was the last one and cursor is empty/null,
// return an empty array.
if (!cursor) {
return [];
}
}
const response = await z.request(
"https://5ae7ad3547436a00143e104d.mockapi.io/api/recipes",
{
// cursor typically is a param to pass along to the next request,
// or the full URL for the next page of items.
params: { cursor },
}
);
// after fetching a page, set the returned cursor for the next page,
// or an empty string if the cursor is null
await z.cursor.set(response.nextPage ?? "");
return response.items;
};
```
Cursors are stored per-zap and last about an hour. Per the above, make sure to only include the cursor if `bundle.meta.page > 0`, so you don't accidentally reuse a cursor from a previous poll.
Lastly, you need to set `canPaginate` to `true` in your polling definition (per the [schema](https://github.com/zapier/zapier-platform/blob/main/packages/schema/docs/build/schema.md#basicpollingoperationschema)) for the `z.cursor` methods to work as expected.
### How does deduplication work?
Each time a polling Zap runs, Zapier extracts a unique "primary key" for each item in the response. Zapier needs to decide which of the items should trigger the Zap. To do this, we compare the primary keys to all those we've seen before, trigger on new objects, and update the list of seen primary keys. When a Zap is turned on, we initialize the list of seen primary keys with a single poll. When it's turned off, we clear that list. For this reason, it's important that calls to a polling endpoint always return the newest items.
For example, the initial poll returns objects 4, 5, and 6 (where a higher primary key is newer). If a later poll increases the limit and returns objects 1-6, then 1, 2, and 3 will be (incorrectly) treated like new objects.
By default, the primary key is the item's `id` field. Since v15.6.0, you can customize the primary key by setting `primary` to true in `outputFields`.
There's a more in-depth explanation [here](https://platform.zapier.com/build/deduplication).
### Why are my triggers complaining if I don't provide an explicit `id` field?
For deduplication to work, we need to be able to identify and use a unique field. In older, legacy Zapier Web Builder integrations, we guessed if `id` wasn't present. In order to ensure we don't guess wrong, we now require that the developers send us an `id` field. If your objects have a differently-named unique field, feel free to adapt this snippet and ensure this test passes:
```js
// ...
let items = response.data.items; // or response.json.items if you're using core v9 or older
return items.map((item) => {
item.id = item.contactId;
return item;
});
```
Since v15.6.0, instead of using the default `id` field, you can also define one or more `outputFields` as `primary`. For example:
```js
{
triggers: {
recipe: {
operation: {
outputField: [
{ key: "userId", primary: true },
{ key: "slug", primary: true },
{ key: "name" },
];
}
}
}
}
```
will tell Zapier to use `(userId, slug)` as the unique primary key to deduplicate items when running a polling trigger.
**Limitation:** The `primary` option currently doesn't support mixing top-level fields with nested fields that use double underscores in their keys. For example, if you set `primary: true` on both `id` and `user__id`, the `primary` setting on the `user__id` field will be ignored; only `id` will be used for deduplication. However, if all the `primary` fields are all nested, such as `user__id` + `user__name`, it will work as expected.
### Node X No Longer Supported
If you're seeing errors like the following:
```
InvalidParameterValueException An error occurred (InvalidParameterValueException) when calling the CreateFunction operation: The runtime parameter of nodejs6.10 is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejsX.Y) while creating or updating functions.
```
... then you need to update your `zapier-platform-core` dependency to a non-deprecated version that uses a newer version of Node.js. Complete the following instructions as soon as possible:
1. Edit `package.json` to depend on a later major version of `zapier-platform-core`. There's a list of all breaking changes (marked with a :exclamation:) in the [changelog](https://github.com/zapier/zapier-platform/blob/main/CHANGELOG.md).
2. Increment the `version` property in `package.json`
3. Ensure you're using version `v18` (or greater) of node locally (`node -v`). Use [nvm](https://github.com/nvm-sh/nvm) to use a different one if need be.
4. Run `rm -rf node_modules && npm i` to get a fresh copy of everything
5. Run `zapier test` to ensure your tests still pass
6. Run `zapier push`
7. Run `zapier promote YOUR_NEW_VERSION` (from step 2)
8. Migrate your users from the previous version (`zapier migrate OLD_VERSION YOUR_NEW_VERSION`)
### What Analytics are Collected?
Starting with v8.4.0, Zapier collects information about each invocation of the CLI tool.
This data is collected purely to improve the CLI experience and will **never** be used for advertising or any non-product purpose. There are 3 collection modes that are set on a per-computer basis.
**Anonymous**
When you run a command with analytics in `anonymous` mode, the following data is sent to Zapier:
* which command you ran
* if that command is a known command
* how many arguments you supplied (but not the contents of the arguments)
* which flags you used (but not their contents)
* the version of CLI that you're using
**Enabled** (the default)
When analytics are fully `enabled`, the above is sent, plus:
* your operating system (the result of calling [`process.platform`](https://nodejs.org/api/process.html#process_process_platform))
* your Zapier user id
**Disabled**
Lastly, analytics can be `disabled` entirely, either by running `zapier analytics --mode disabled` or setting the `DISABLE_ZAPIER_ANALYTICS` environment variable to `1`.
We take great care not to collect any information about your filesystem or anything otherwise secret. You can see exactly what's being collecting at runtime by prefixing any command with `DEBUG=zapier:analytics`.
### What's the Difference Between an "App" and an "Integration"?
We're in the process of doing some renaming across our Zapier marketing terms. Eventually we'll use "integration" everywhere. Until then, know that these terms are interchangeable and describe the code that you write that connects your API to Zapier.
# Input Field Configuration
On each trigger, search, or create in the `operation` directive, you can provide fields as an array of objects under `inputFields`.
Those fields have various options you can provide. Here is a brief example:
```js
const App = {
// ...
creates: {
create_recipe: {
// ...
operation: {
// an array of objects is the simplest way
inputFields: [
{
key: "title",
required: true,
label: "Title of Recipe",
helpText: "Name your recipe!",
},
{
key: "style",
required: true,
choices: { mexican: "Mexican", italian: "Italian" },
},
],
perform: () => {},
},
},
},
};
```
Notably, fields come in different types, which may look and act differently in the Zap editor. The default field display is a single-line input field.
| Type | Behavior |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `string` | Accepts text input. |
| `text` | Displays large, `