# Get Action Details
Source: https://docs.zapier.com/ai-actions/api-reference/actions/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
Source: https://docs.zapier.com/ai-actions/api-reference/actions/get-prefill-choices
post /api/v2/apps/{app}/choices/{prefill}/
Get prefill choices for an app's prefill.
# Search Actions
Source: https://docs.zapier.com/ai-actions/api-reference/actions/search-actions
get /api/v2/apps/{app}/actions/
Search for Zapier actions by app, ordered by relevancy.
# Create AI Action
Source: https://docs.zapier.com/ai-actions/api-reference/ai-actions/create-ai-action
post /api/v2/ai-actions/
Create a new AI Action that can be executed by the user later.
# Delete AI Action
Source: https://docs.zapier.com/ai-actions/api-reference/ai-actions/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
Source: https://docs.zapier.com/ai-actions/api-reference/ai-actions/get-ai-action
get /api/v2/ai-actions/{ai_action_id}/
Get the details of a specific AI Action.
# List AI Actions
Source: https://docs.zapier.com/ai-actions/api-reference/ai-actions/list-ai-actions
get /api/v2/ai-actions/
List all the current actions for the current user.
# Update AI Action
Source: https://docs.zapier.com/ai-actions/api-reference/ai-actions/update-ai-action
put /api/v2/ai-actions/{ai_action_id}/
Update an existing AI Action.
# Get App Details
Source: https://docs.zapier.com/ai-actions/api-reference/apps/get-app-details
get /api/v2/apps/{app}/
# List Authentications For App
Source: https://docs.zapier.com/ai-actions/api-reference/apps/list-authentications-for-app
get /api/v2/apps/{app}/auths/
Get list of Zapier authentications associated with the requesting user and account.
# Search Apps
Source: https://docs.zapier.com/ai-actions/api-reference/apps/search-apps
get /api/v2/apps/search/
# Check User Auth
Source: https://docs.zapier.com/ai-actions/api-reference/auth/check-user-auth
get /api/v2/auth/check/
Test that the API and auth are working.
# Get Account List
Source: https://docs.zapier.com/ai-actions/api-reference/auth/get-account-list
get /api/v2/auth/accounts/
Get a list of Zapier accounts for the current user.
# Get Oauth Login Link
Source: https://docs.zapier.com/ai-actions/api-reference/auth/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
Source: https://docs.zapier.com/ai-actions/api-reference/auth/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
Source: https://docs.zapier.com/ai-actions/api-reference/execution/execute-stateless-ai-action
post /api/v2/execute/
# Execute Stored AI Action
Source: https://docs.zapier.com/ai-actions/api-reference/execution/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
Source: https://docs.zapier.com/ai-actions/api-reference/execution/preview-stored-ai-action
post /api/v2/ai-actions/{ai_action_id}/preview/
# Rate Execution Log
Source: https://docs.zapier.com/ai-actions/api-reference/execution/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
Source: https://docs.zapier.com/ai-actions/api-reference/system/app-health-check
get /api/v2/health/
Check that the app is up and running.
# Guess Actions
Source: https://docs.zapier.com/ai-actions/api-reference/utilities/guess-actions
post /api/v2/guess-actions/
# Shrink Result
Source: https://docs.zapier.com/ai-actions/api-reference/utilities/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
Source: https://docs.zapier.com/ai-actions/developer-resources/keys
# API Logs
Source: https://docs.zapier.com/ai-actions/developer-resources/logs
# OpenAPI Specification
Source: https://docs.zapier.com/ai-actions/developer-resources/openapi
# Submit an issue
Source: https://docs.zapier.com/ai-actions/help/bug
# Join our Community
Source: https://docs.zapier.com/ai-actions/help/community
# Zapier account list
Source: https://docs.zapier.com/ai-actions/how-tos/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
Source: https://docs.zapier.com/ai-actions/how-tos/action-info/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.
"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
Source: https://docs.zapier.com/ai-actions/how-tos/action-info/choose-auth
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
Source: https://docs.zapier.com/ai-actions/how-tos/action-info/find-action
get /api/v2/apps/{app}/actions/
Search for Zapier actions by app, ordered by relevancy.
# Find an app
Source: https://docs.zapier.com/ai-actions/how-tos/action-info/find-app
get /api/v2/apps/search/
# Introduction
Source: https://docs.zapier.com/ai-actions/how-tos/action-info/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
Source: https://docs.zapier.com/ai-actions/how-tos/action-info/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
Source: https://docs.zapier.com/ai-actions/how-tos/auth
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
Source: https://docs.zapier.com/ai-actions/how-tos/stateless-vs-stored
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
Source: https://docs.zapier.com/ai-actions/how-tos/stateless/intro
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
Source: https://docs.zapier.com/ai-actions/how-tos/stateless/run-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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/action-configuration
*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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/intro
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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/list-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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/manage/create-action
post /api/v2/ai-actions/
Create a new AI Action that can be executed by the user later.
# Delete stored action
Source: https://docs.zapier.com/ai-actions/how-tos/stored/manage/delete-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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/manage/get-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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/manage/update-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
Source: https://docs.zapier.com/ai-actions/how-tos/stored/run-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
Source: https://docs.zapier.com/ai-actions/intro
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
Source: https://docs.zapier.com/ai-actions/libraries/nodejs/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
Source: https://docs.zapier.com/ai-actions/libraries/nodejs/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
Source: https://docs.zapier.com/ai-actions/libraries/nodejs/nextjs-reccommendation
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
Source: https://docs.zapier.com/ai-actions/libraries/nodejs/usage
# 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
Source: https://docs.zapier.com/ai-actions/libraries/react/action-list
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
Source: https://docs.zapier.com/ai-actions/libraries/react/ai-actions-provider
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
Source: https://docs.zapier.com/ai-actions/libraries/react/edit-action
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
Source: https://docs.zapier.com/ai-actions/libraries/react/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
Source: https://docs.zapier.com/ai-actions/libraries/react/stateless-creator
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
Source: https://docs.zapier.com/ai-actions/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
Source: https://docs.zapier.com/ai-actions/tools/stateless-action
[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.
# Core Reference
Source: https://docs.zapier.com/platform/build-cli/core
Reference for `zapier-platform-core`
Most functions get called with `(z, bundle)`. This document is a reference for how to use these objects.
> If you use TypeScript, you can import `ZObject`, `Bundle` and `PerformFunction` from `zapier-platform-core`.
## `z` Object
We provide several methods off of the `z` object, which is provided as the first argument to all function calls in your integration.
> The `z` object is passed into your functions as the first argument - IE: `perform: (z) => {}`.
### `z.request([url], options)`
`z.request([url], options)` is a promise based HTTP client with some Zapier-specific goodies. See [Making HTTP Requests](/platform/build-cli/overview#making-http-requests). `z.request()` will [percent-encode](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) non-ascii characters and these reserved characters: ``:$/?#[]@$&+,;=^@`\``. Use [`skipEncodingChars`](https://github.com/zapier/zapier-platform/blob/main/packages/schema/docs/build/schema.md#requestschema) to modify this behaviour.
### `z.console`
`z.console.log(message)` is a logging console, similar to Node.js `console` but logs remotely, as well as to stdout in tests. See [Log Statements](/platform/build-cli/overview#console-logging)
### `z.dehydrate(func, inputData)`
`z.dehydrate(func, inputData)` is used to lazily evaluate a function, perfect to avoid API calls during polling or for reuse. See [Dehydration](/platform/build-cli/overview#dehydration).
### `z.dehydrateFile(func, inputData)`
`z.dehydrateFile` is used to lazily download a file, perfect to avoid API calls during polling or for reuse. See [File Dehydration](/platform/build-cli/overview#file-dehydration).
### `z.stashFile(bufferStringStream, [knownLength], [filename], [contentType])`
`z.stashFile(bufferStringStream, [knownLength], [filename], [contentType])` is a promise based file stasher that returns a URL file pointer. See [Stashing Files](/platform/build-cli/overview#stashing-files).
### `z.JSON`
`z.JSON` is similar to the JSON built-in like `z.JSON.parse('...')`, but catches errors and produces nicer tracebacks.
### `z.hash()`
`z.hash()` is a crypto tool for doing things like `z.hash('sha256', 'my password')`
### `z.errors`
`z.errors` is a collection error classes that you can throw in your code, like `throw new z.errors.HaltedError('...')`.
The available errors are:
* `Error` (*added in v9.3.0*) - Stops the current operation, allowing for (auto) replay. Read more on [General Errors](/platform/build-cli/overview#general-errors)
* `HaltedError` - Stops current operation, but will never turn off Zap. Read more on [Halting Execution](/platform/build-cli/overview#halting-execution)
* `ExpiredAuthError` - Stops the current operation and emails user to manually reconnect. Read more on [Stale Authentication Credentials](/platform/build-cli/overview#stale-authentication-credentials)
* `RefreshAuthError` - (OAuth2 or Session Auth) Tells Zapier to refresh credentials and retry operation. Read more on [Stale Authentication Credentials](/platform/build-cli/overview#stale-authentication-credentials)
* `ThrottledError` (*new in v11.2.0*) - Tells Zapier to retry the current operation after a delay specified in seconds. Read more on [Handling Throttled Requests](/platform/build-cli/overview#handling-throttled-requests)
For more details on error handling in general, see [here](/platform/build-cli/overview#error-handling).
### `z.cursor`
The `z.cursor` object exposes two methods:
* `z.cursor.get(): Promise`
* `z.cursor.set(string): Promise`
Any data you `set` will be available to that Zap for about an hour (or until it's overwritten). For more information, see: [paging](/platform/build-cli/overview#paging).
### `z.generateCallbackUrl()`
The `z.generateCallbackUrl()` will return a callback URL your app can `POST` to later for handling long running tasks (like transcription or encoding jobs). In the meantime, the Zap and Task will wait for your response and the user will see the Task marked as waiting.
For example, in your `perform` you might do:
```js
const perform = async (z, bundle) => {
// something like this url:
// https://zapier.com/hooks/callback/123/abcdef01-2345-6789-abcd-ef0123456789/abcdef0123456789abcdef0123456789abcdef01/
// consider checking bundle.meta.isLoadingSample to determine if this is a test run or real run!
const callbackUrl = z.generateCallbackUrl();
await z.request({
url: "https://example.com/api/slow-job",
method: "POST",
body: {
// ... whatever your integration needs
url: callbackUrl,
},
});
return { hello: "world" }; // available later in bundle.outputData
};
```
And in your own `/api/slow-job` view (or more likely, an async job) you'd make this request to Zapier when the long-running job completes to populate `bundle.cleanedRequest`:
```http
POST /hooks/callback/123/abcdef01-2345-6789-abcd-ef0123456789/abcdef0123456789abcdef0123456789abcdef01/ HTTP/1.1
Host: zapier.com
Content-Type: application/json
{"foo":"bar"}
```
> We recommend using `bundle.meta.isLoadingSample` to determine if the execution is happening in the foreground (IE: during Zap setup) as using `z.generateCallbackUrl()` can be inappropriate given the disconnect. Instead, wait for the long running request without generating a callback, or if you must, return stubbed data.
By default the payload `POST`ed to the callback URL will augment the data returned from the initial `perform` to compose the final value.
If you need to customize what the final value should be you can define a `performResume` method that receives three bundle properties:
* `bundle.outputData` is `{"hello": "world"}`, the data returned from the initial `perform`
* `bundle.cleanedRequest` is `{"foo": "bar"}`, the payload from the callback URL
* `bundle.rawRequest` is the full request object corresponding to `bundle.cleanedRequest`
```js
const performResume = async (z, bundle) => {
// this will give a final value of: {"hello": "world", "foo": "bar"}
// which is the default behavior when a custom `performResume` is not
// defined.
return { ...bundle.outputData, ...bundle.cleanedRequest };
};
```
> The app will have a maximum of 30 days to `POST` to the callback URL. If a user deletes or modifies the Zap or Task in the meantime, we will not resume the task.
> `performResume` will only run when the Zap runs live, and cannot be tested in the Zap Editor when configuring the Zap. It is possible to use `bundle.meta.isLoadingSample` to load a fixed sample to allow users to test a step that includes `performResume`.
Some considerations:
* `performResume` is not supported by the Platform UI at the moment. It can only be used by integrations built with the CLI.
* In a search-or-write step, if the search part fails and proceeds to the write part, the callback URL generated for the write step might not be recognized or waited for. This can result in the `performResume` operation not being executed, leading to issues in the task flow.
* When migrating actions that use `performResume`, it is important to ensure that the `performResume` code for the new API is backward compatible. This ensures that if a migration occurs while a run is waiting for a callback, it will succeed after being migrated
## `bundle` Object
This object holds the user's auth details and the data for the API requests.
> The `bundle` object is passed into your functions as the second argument - IE: `perform: (z, bundle) => {}`.
### `bundle.authData`
`bundle.authData` is user-provided authentication data, like `api_key` or `access_token`. [Read more on authentication.](/platform/build-cli/overview#authentication)
### `bundle.inputData`
`bundle.inputData` is user-provided data for this particular run of the trigger/search/create, as defined by the [`inputFields`](/platform/build-cli/input-fields). For example:
```js
{
createdBy: 'his name is Bobby Flay',
style: 'he cooks mediterranean',
scheduledAt: "2021-09-09T09:00:00-07:00"
}
```
### `bundle.inputDataRaw`
`bundle.inputDataRaw` is like `bundle.inputData`, but before processing such as interpreting friendly datetimes and rendering `{{curlies}}`:
```js
{
createdBy: 'his name is {{123__chef_name}}',
style: 'he cooks {{456__style}}',
scheduledAt: "today"
}
```
> "curlies" represent data mapped in from previous steps. They take the form `{{NODE_ID__key_name}}`.
You'll usually want to use `bundle.inputData` instead.
### `bundle.meta`
`bundle.meta` contains extra information useful for doing advanced behaviors depending on what the user is doing. It has the following options:
| key | default | description |
| -------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isLoadingSample` | `false` | If true, this run was initiated manually via the Zap Editor |
| `isFillingDynamicDropdown` | `false` | If true, this poll is being used to populate a dynamic dropdown. You only need to return the fields you specified (such as `id` and `name`), though returning everything is fine too |
| `isPopulatingDedupe` | `false` | If true, the results of this poll will be used to initialize the deduplication list rather than trigger a zap. You should grab as many items as possible. See also: [deduplication](/platform/build/deduplication) |
| `limit` | `-1` | The number of items you should fetch. `-1` indicates there's no limit. Build this into your calls insofar as you are able |
| `page` | `0` | Used in [paging](/platform/build-cli/faqs#whats-the-deal-with-pagination-when-is-it-used-and-how-does-it-work) to uniquely identify which page of results should be returned |
| `timezone` | `null` | The timezone the user has configured for their account or specfic automation. Received as [TZ identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones), such as "America/New\_York". |
| `isTestingAuth` | `false` | (legacy property) If true, the poll was triggered by a user testing their account (via [clicking "test"](https://cdn.zapier.com/storage/photos/5c94c304ce11b02c073a973466a7b846.png) or during setup). We use this data to populate the auth label, but it's mostly used to verify we made a successful authenticated request |
| `withSearch` | `undefined` | When a create is called as part of a search-or-create step, `withSearch` will be the key of the search. |
| `inputFields` | `{}` | Contains extra input field context if one or more input fields define this data via their respective `meta` property. If defined, then this object's keys are the respective input field `key` values, and the values for each `key` are an object corresponding to that input field's `meta` object value. See the [FieldSchema reference](https://github.com/zapier/zapier-platform/blob/main/packages/schema/docs/build/schema.md#fieldschema) for more details on how to define input field meta. |
> Before v8.0.0, the information in `bundle.meta` was different. See [the old docs](https://github.com/zapier/zapier-platform-cli/blob/a058e6d538a75d215d2e0c52b9f49a97218640c4/README.md#bundlemeta) for the previous values and [the wiki](https://github.com/zapier/zapier-platform/wiki/bundle.meta-changes) for a mapping of old values to new.
Here's an example of a polling trigger that is also used to power a dynamic dropdown:
```js
const perform = async (z, bundle) => {
const params = { per_page: 100 }; // poll for the most recent 100 teams
if (bundle.meta.isFillingDynamicDropdown) {
// dynamic dropdowns support pagination
params.per_page = 30;
params.offset = params.per_page * bundle.meta.page;
}
const response = await z.request({
url: `${API_BASE_URL}/teams`,
params,
});
return response.json;
};
// ...
```
### `bundle.rawRequest`
> `bundle.rawRequest` is only available in the `perform` for webhooks, `getAccessToken` for OAuth authentication methods, and `performResume` in a callback action.
`bundle.rawRequest` holds raw information about the HTTP request that triggered the `perform` method or that represents the user's browser request that triggered the `getAccessToken` call:
```
{
method: 'POST',
querystring: 'foo=bar&baz=qux',
headers: {
'Content-Type': 'application/json'
},
content: '{"hello": "world"}'
}
```
In `bundle.rawRequest`, headers other than `Content-Length` and `Content-Type` will be prefixed with `Http-`, and all headers will be named in Camel-Case. For example, the header `X-Time-GMT` would become `Http-X-Time-Gmt`.
### `bundle.cleanedRequest`
> `bundle.cleanedRequest` is only available in the `perform` for webhooks, `getAccessToken` for OAuth authentication methods, and `performResume` in a callback action.
`bundle.cleanedRequest` will return a formatted and parsed version of the request. Some or all of the following will be available:
```
{
method: 'POST',
querystring: {
foo: 'bar',
baz: 'qux'
},
headers: {
'Content-Type': 'application/json'
},
content: {
hello: 'world'
}
}
```
### `bundle.outputData`
> `bundle.outputData` is only available in the `performResume` in a callback action.
`bundle.outputData` will return a whatever data you originally returned in the `perform`, allowing you to mix that with `bundle.rawRequest` or `bundle.cleanedRequest`.
### `bundle.targetUrl`
> `bundle.targetUrl` is only available in the `performSubscribe` and `performUnsubscribe` methods for webhooks.
This the URL to which you should send hook data. It'll look something like [`https://hooks.zapier.com/1234/abcd`.](https://hooks.zapier.com/1234/abcd.) We provide it so you can make a POST request to your server. Your server should store this URL and use is as a destination when there's new data to report.
For example:
```js
const subscribeHook = async (z, bundle) => {
const options = {
url: "https://57b20fb546b57d1100a3c405.mockapi.io/api/hooks",
method: "POST",
body: {
url: bundle.targetUrl, // bundle.targetUrl has the Hook URL this app should call
},
};
const response = await z.request(options);
return response.data; // or response.json if you're using core v9 or older
};
module.exports = {
// ...
performSubscribe: subscribeHook,
// ...
};
```
Read more in the [REST hook example](https://github.com/zapier/zapier-platform/blob/main/example-apps/rest-hooks/triggers/recipe.js).
### `bundle.subscribeData`
> `bundle.subscribeData` is available in the `perform` and `performUnsubscribe` method for webhooks.
This is an object that contains the data you returned from the `performSubscribe` function. It should contain whatever information you need send a `DELETE` request to your server to stop sending webhooks to Zapier.
Read more in the [REST hook example](https://github.com/zapier/zapier-platform/blob/main/example-apps/rest-hooks/triggers/recipe.js).
## `bufferedBundle` Object
*Added in v15.15.0.*
This object holds a user's auth details (`bufferedBundle.authData`) and the buffered data (`bufferedBundle.buffer`) for the API requests. It is used only with a `create` action's `performBuffer` function.
> The `bufferedBundle` object is passed into the `performBuffer` function as the second argument - IE: `performBuffer: async (z, bufferedBundle) => {}`.
### `bufferedBundle.authData`
It is a user-provided authentication data, like `api_key` or `access_token`. [Read more on authentication.](/platform/build-cli/overview#authentication)
### `bufferedBundle.groupedBy`
It is a user-provided data for a set of selected [`inputFields`](/platform/build-cli/input-fields) to group the multiple runs of a `create` action by.
### `bufferedBundle.buffer`
It is an array of objects of user-provided data and some meta data to allow multiple runs of a `create` action be processed in a single API request.
#### `bufferedBundle.buffer[].inputData`
It is a user-provided data for a particular run of a `create` action in the buffer, as defined by the [`inputFields`](/platform/build-cli/input-fields).
#### `bufferedBundle.buffer[].meta`
It contains an idempotency `id` provided to the `create` action to identify each run's data in the buffered data.
# Dynamic Dropdowns
Source: https://docs.zapier.com/platform/build-cli/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.

## 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. In some cases, a dynamic dropdown depends on the value chosen in another dynamic dropdown when making its API call. The [Google Sheets](https://zapier.com/apps/google-sheets/integrations#triggers-and-actions) integration displays an example of this pattern.
The example below illustrates a 'New Worksheet' trigger that populates a dynamic dropdown input field to select a worksheet:
```js
{
key: "worksheet",
// ...
operation: {
// ...
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
}
}
}
```
Assume there is another `New Records` trigger with `Spreadsheet` and `Worksheet` dynamic dropdown input fields, which have keys `spreadsheet_id` and `worksheet_id` respectively. The selected spreadsheet value is available via `bundle.inputData.spreadsheet_id` to be used by the `Worksheet` trigger.
```js
const App = {
// ...
triggers: {
// ...
issue: {
key: "new_records",
// ...
operation: {
inputFields: [
{
key: "spreadsheet_id",
required: true,
label: "Spreadsheet",
dynamic: "spreadsheet.id.name",
altersDynamicFields: true,
},
{
key: "worksheet_id",
required: true,
label: "Worksheet",
dynamic: "worksheet.id.name",
},
],
},
},
},
};
```
> Note: Be mindful that a dynamic dropdown can depend on the value chosen in another dynamic dropdown. Two types of dependencies can exist between fields:
>
> *Requirement dependency*: Affects how dependent fields are enabled or disabled within the UI
>
> * Setting `required: false` makes a field optional and always enabled in the UI.
> * Having no required value set makes a field optional and disabled until the dependencies are selected.
>
> *Value dependency*: Affects how dynamic dropdown field options are retrieved
>
> * Setting a required value or not does not affect how the options of a dynamic field are retrieved.
>
> So, if you have an optional dynamic dropdown that depends on another dropdown input field, that field should not have `required: false` set. Input fields are optional by default, but setting `required: false` on an optional dynamic dropdown field that depends on another removes the requirement dependency relationship.
> In the example above, the `worksheet_id` input field will be disabled until the `spreadsheet_id` input field has a value in Zapier's products such as the Zap editor. Notice that setting `altersDynamicFields: true` signifies other input fields need to be recomputed whenever the value of that field changes.
## 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.

### 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
Source: https://docs.zapier.com/platform/build-cli/faqs
### 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](/platform/build-cli/overview#shorthand-http-requests) to parse XML. Use an `afterResponse` [middleware](/platform/build-cli/overview#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;
},
],
// ...
};
```
### What's the deal with pagination? When is it used and how does it work?
Moved to [paging](/platform/build-cli/overview#paging).
### 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](/platform/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
* the integration app the CLI commands are run in
**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.
### What does performGet do?
The `performGet` method is an optional feature in Zapier that allows you to retrieve detailed information about an object. For instance, if your `create` action's `perform` method only returns the new object's `ID`, you can use `performGet` to fetch the object's full properties using that `ID`.
`performGet` is only available for `Create` or `Search` actions and is most useful when the initial `perform` result is limited, and additional information is needed.
The results from `perform` are automatically passed to `performGet` via `bundle.inputData` each time the `create` or `search` runs, allowing you to retrieve more comprehensive details.
It's important to note that `performGet` is only invoked when the result returned by `perform` is not empty.
# Input Field Configuration
Source: https://docs.zapier.com/platform/build-cli/input-fields
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, `