Automated App Development Checks

In accordance with our app development guide, we run your app through a set of automated checks to ensure it’s giving our users the best possible experience. Violations come in two flavors:

  • Errors will block deployment of your app until they’re fixed. These include missing data (that renders an app invalid), style concerns, or security issues
  • Warnings won’t block deployment, but are good things to consider. We’ve done our best to make these only show up when appropriate, but they may be safe to ignore if your app doesn’t use the feature described.

When the checks are run, we’ll give a brief blurb summarizing the issue (with an issue id) along with a link to this page. This will act as a full reference explaining each error and giving examples for each.


A valid markdown link consists of a pair of square brackets with the link text paired with a pair of parens that have the link itself. See the markdown cheatsheet for more info.

If you want to show a full link without actually linking to it, use backticks. This makes it clear to the user that they don’t need to click the link, it’s just used as an example. Any link used in plain text needs to either be a proper link or in backticks.

✘ examples of an incorrect implementation:

See [Google(https://google.com)
See https://google.com

✔ examples of a correct implementation:

See [Google](https://google.com)
See `https://google.com`

If you see this error, you should look through both the description for the trigger/search/action and the help text for any fields that might have bad links.


ZDE002 - Has Redundant Help Text

Help text is optional and meant to provide non-obvious information or links for the user. If the label and help text are too similar, they are considered redundant.

✘ an example of an incorrect implementation:

{
  "label": "Subdomain",
  "help_text": "your subdomain"
}

✔ an example of a correct implementation:

{
  "label": "Subdomain",
  "help_text": "Where you (and your users) can access your forms. eg: https://<SUBDOMAIN>.typeform.com"
}

ZDE003 - OAuth URL Must Be HTTPS

When dealing with credentials, it’s important to communicate using the https protocol to ensure sensitive information isn’t leaked to malicious parties. More information can be found here.

✘ an example of an incorrect implementation:

http://slack.com/oauth/authorize

✔ an example of a correct implementation:

https://slack.com/oauth/authorize

ZDE004 - Cannot Contain “Zapier”, “Sync”, or is Too Long

Your app’s description is a place to talk about your app, not ours! Even if we really like your service, per our App Development guide you’re not allowed to say Zapier in your app’s description.

Additionally, it’s discouraged that you talk about how this integration will sync anything, as the space is supposed to be about your service itself instead of the Zapier integration in particular.

Lastly, this section should be short and sweet. A brief description (roughly tweet-sized) is best.

✘ an example of an incorrect setup:

Google Translate is a service that enables Zapier users to translate text from one language into another.

✔ an example of a correct implementation:

Google Translate translates text from one language into another.

ZDE005 - Needs a Titlecased Label

In order to have a consistent style across Trigger and Action labels, it is required they be presented in title case. If you fail this check, a passing version of your label will be shown.

✘ an example of an incorrect implementation:

new contact

✔ an example of a correct implementation:

New Contact

We use the titlecase library (Github) to enforce this. If you’re not sure how to titlecase something correctly, use this repl like so:

Try it: https://repl.it/@xavdid/titlecase-test


ZDE006 - Consists Only of a Single Static Webhook

Static Webhooks, while convenient to build, leave a lot to be desired from our side. For this reason, Zapier doesn’t allow apps that are a single static hook. To fix this, add more Triggers and Actions.


ZDE007 - Hook Trigger Needs a Polling URL

When users are setting up a hook-based (aka instant) Trigger, it’s important to have a polling fallback. For example, imagine a Zap that triggers on a new slack message. Without a polling url, the test won’t complete without the user sending an actual message in a Slack channel, which is disruptive. Instead, the test fetches a (real) recent message and uses it as the test result. After that, the polling url is only used for tests.

It’s very important that the structure of an object from a webhook and from a poll are identical. Typically, this means modifying a poll result so that it looks like a hook. If a poll has fields that a hook doesn’t, the user may map them to a later step and when the zap is run for real, the value will be blank.

Let’s walk through an example. Say we have a New Contact REST hook trigger. When a new contact is created, Zapier gets a webhook that looks like this:

{
  "id": 1,
  "firstName": "Bruce",
  "lastName": "Wayne",
  "job": "Batman"
}

The accompanying polling url would look something like https://site.com/contacts/list and return:

{
  "results": [
    {
      "id": 1,
      "firstName": "Bruce",
      "lastName": "Wayne",
      "job": "Batman",
      "friends": [2, 3, 4]
    },
    {
      "id": 2,
      "firstName": "Alfred",
      "lastName": "Pennyworth",
      "job": "Butler",
      "friends": [1, 3]
    }
  ]
}

Typically you could return the first object from the results array as part of a poll (and hydrate the friends), but since the hook has no friend information, you should always remove it. A good way to do this is a processContact function that you map all results (of either type) through that reduces each object to the lowest common denominator.



ZDE008 - A Trigger Must Be Designated as a Test Call

To test stored credentials, a Trigger is chosen as the “Test Trigger”. This can be an existing trigger or a hidden one used specifically for this purpose. The important factor is that it’s a call that requires no configuration and will work for all users, regardless of product level or account type.

✘ an example of an incorrect implementation:

https://slack.com/api/channels.info?channel_id=C12345

✔ an example of a correct implementation:

https://slack.com/api/auth.test

Resolution

For v2 apps, you denote a test trigger. There’s more information here.

For CLI apps, you add a test url or function to your authentication, details here.



ZDE009 - Is Missing “ID” Field in Sample Data

For polling Triggers, the deduper uses the id field to decide if it’s seen an object before. It can be any sort of string, but it’s important that it’s unique. If your object is returned with a differently named id field (such as contact_id, use a _post_poll function (web builder) or .map (CLI) to rename it. Hooks are not deduped, so they’re not required to have an id.

An example of such a function might be:

var results = z.JSON.parse(bundle.request.data).results // array of contact objects

return results.map(function(contact) {
  contact.id = contact.contact_id
  delete contact.contact_id
  return contact
})

✘ an example of an incorrect implementation:

{
  "contact_id": 4,
  "contact_name": "David"
}

✔ an example of a correct implementation:

{
  "id": 4,
  "contact_name": "David"
}

ZDE010 - Requires at Least One Search Field

When making a search step, it’s important to have a field to search on! Common examples for searching for a user are by name, email, and username. See here for more information.


ZDE011 - Logo Image Is Not Square

Your app’s logo is used all over the site in square containers. To ensure it’s never warped or distorted, it must be square.

✘ an example of an incorrect implementation:

300px by 400px

✔ an example of a correct implementation:

300px by 300px

ZDE012 - Logo Image Must Be Larger Than 256px

Your app’s logo is used all over the site in various sizes. To ensure it looks good at all sizes, it must be at least 256px by 256px. To resize an image, you can use this tool

✘ an example of an incorrect setup:

192px by 192px

✔ an example of a correct implementation:

300px by 300px

ZDE013 - Logo Image Must Be a PNG

To ensure your logo looks good across devices and at all sizes, it must be a PNG file. To convert an existing image to PNG, you can use this tool.

✘ an example of an incorrect setup:

mylogo.jpg

✔ an example of a correct implementation:

mylogo.png

ZDE014 - Unable to Process Image

For whatever reason, your image was unable to be processed. Try a different file or contact support for more information.


ZDE015 - Connects to [Trigger|Search|Action] NAME, Which Doesn’t Exist

Zapier provides a few constructs for connecting multiple steps in a Zap (such as dynamic dropdowns and search connectors). If these are used, it’s important that the target step exists and is of the correct type.


ZDE016 - Asks for API Key in Action Instead of in Auth

Some apps incorrectly have api_key or similar as an an action field in an action instead of centrally used in auth. This is worse because action fields typically aren’t treated with the same level of security as auth fields are (eg: scrubbed from logs) and aren’t action specific. Additionally, the ability to test the validity of auth doesn’t exist for action fields, so anything auth related should be put into an auth field instead.


ZDE017 - Has a Search Connector, but no Dynamic Dropdown

By design, to get the Add a Search Step button, an action needs both a search connector and a (valid) dynamic dropdown. If you can’t provide a valid dropdown, you can instead point to a dummy trigger that always returns an empty array.

✘ an example of an incorrect setup:

{
  "key": "update_thing",
  "search": "thing.id"
}

✔ an example of a correct implementation:

{
  "key": "update_thing",
  "search": "thing.id",
  "dynamic": "things.id.name"
}

ZDE018 - Has been moved

See ZDE004 instead.


ZDE019 - Important Polling Triggers Should Always Have Sample Data

When users are setting up a Trigger, they need sample data to be returned in order to have fields available to map the Action. If testing the trigger returns no live results, we use Sample Data as a fallback.

It’s very important that the structure of an object from the actual trigger and in the sample data are identical. Learn how to properly set up Trigger sample results.


ZDE020 - Search Requires a URL

When making a search step, it’s important to have a URL to send a search request too. This URL is needed to fetch a search request or search resource to present to the user.


ZDE021 - URL for Triggers|Searches|Actions should be HTTPS

For privacy, consistency, and security purposes, we require all your authentication and app URLs to be on HTTPS, the ‘S’ at the end of HTTPS stands for ‘Secure’. This means all communications between the browser and website are encrypted.


ZDE022 - You must select a category for your App

So we can correctly categorize your App on Zapier, please choose the category that fits best.

You can specify a category for your app by clicking the “Edit Title, Image or Description” button on your App’s Development/Build tab. You’ll see the category option after you update the Intended audience dropdown to “Public”.


ZDW001 - Needs a Noun

We use templates to communicate with the user what sorts of objects they’re dealing with. It helps when we’re able to correctly identify them, so it’s nice to provide a noun. That way, we can say Select a name for your new Contact instead of Select a name for your new Object.

✘ an example of an incorrect setup:

{
    "label": "New Contact"
}

✔ an example of a correct implementation:

{
    "label": "New Contact",
    "noun": "Contact"
}

ZDW002 - Has Help Text With X Characters (Which Is over the Limit of Y)

Help text is meant to be a short blurb that gives the user clear instructions about configuring the step. If there’s more detailed information you need to pass on, link to a help doc instead.

✘ an example of an incorrect implementation:

{
    "label": "Name",
    "help_text": "A contact's name is that which is (usually) given to them by their parent(s). It can come in many forms, some of which are abbreviated. There's also the matter of the surname, which is not chosen directly by the parents."
}

✔ an example of a correct implementation:

{
    "label": "Name",
    "help_text": "The contact's full name, separated by a space."
}

ZDW003 - Consider Using Z.JSON.Parse() Instead of JSON.Parse()

In scripting, we provide a number of helpful methods on the globally available z object. Our version of JSON.parse() has error handling and logging included out of the box, so we recommend you use it. For more information, see the scripting docs.

✘ an example of an incorrect implementation:

var result = JSON.parse(bundle.response.content)

✔ an example of a correct implementation:

var result = z.JSON.parse(bundle.response.content)

ZDW004 - There Should Be No More Than X Important Triggers|Searches|Actions in an App

In order to highlight your most popular steps and give the user a clear recommendation of what to use Zapier for, we encourage a limited number of “important” steps. These are shown first in the UI and aren’t behind a “show more” click.

These can be adjusted in the settings for each individual step, either via checkbox (V2 Platform) or via the important property (CLI)


ZDW005 - Contains a REST Hook Trigger, but The “(Un)Subscribe URL” Is Missing

For convenience, there’s a central subscribe_url for REST hooks. It’s not required, but is helpful! See these docs for more information.


ZDW006 - URLs Should Be HTTPS

When handling customer data (which all Zapier functions do), it’s strongly encouraged that all communication take securely. Using SSL is a big part of that, so ensure your urls have https as their protocol. This goes for subscribe/unsubscribe urls, as well as all triggers and actions. Really any url you see.

If you need help setting up an SSL certificate for your API, we suggest Let’s Encrypt.

✘ an example of an incorrect setup:

http://site.com/messages/subscribe

✔ an example of a correct implementation:

https://site.com/messages/subscribe

ZDW007 - Is Using a Static Webhook

As static webhooks are a little more complicated to set up correct, we discourage their use. We no longer support adding new static webhook triggers to a public app, please use an alternative trigger type.


ZDW008 - “PARAM” Is Included in the Url but Not Marked as Required

URLs can have variables in them (such as https://{{subdomain}}.typeform.com). If those variables are optional and not supplied by the user, the url will be invalid and the step will never work.

If you want add optional parameters to the url, do this by modifying the bundle.request.params object via scripting. For example, for a trigger with key task, define a method task_pre_poll. See the documentation and examples for more details.


It’s often not obvious where a user can find their API key for a service. If you use a pasted API key as an authentication method, it’s strongly encouraged that you link the user to the page (or relevant help doc) that has their key.

✘ an example of an incorrect implementation:

API key is found on the "Integrations" page in settings

✔ an example of a correct implementation:

Go to the [API Details](https://my.site.com/manage/api-details)
screen from your Website Dashboard to find your API Key.

ZDW010 - Should Have a Connection Label Set

Connection Labels helps customers understand and remember which account they connected for a given Connected Account. These should be short and something easily identifiable.

For both CLI and Web Builder, the connection label is a string. You can use any data returned by your test function.

For instance, if a successful run of the auth test returns the following json:

{
  "name": "Malcom Reynolds",
  "email": "youcanttaketheskyfromme@serenity.com",
  "job": "Captain"
}

Your auth label could be the following:

"{{name}} - {{email}}"

The most important part of the label is that it uniquely identifies the auth it’s labeling.

✘ an example of an incorrect Connection Label:

"Slack"

✔ an example of a correct Connection Label:

"{{user}} @ {{team}}"

Assuming the Test Trigger returns an object with a user and a team property.

To fix this, see examples at the following links:

  • CLI: https://github.com/zapier/zapier-platform-cli#basic
  • Web Builder: https://zapier.com/developer/documentation/v2/app-dev-guide/#connection-label

ZDW011 - Search Should Have a Resource URL

Resource URL is used to fetch the full details of a Search result or (in the case of a Search or Action when no results are found) Create result. This is helpful because most searches only return a subset of a result’s fields. It also ensures that regardless of it having found or created, the same data is returned.


ZDW012 - ID fields should have dynamic dropdowns

We’ve found that instead of instructing users to paste an item id into Zapier, providing them with a dynamic dropdown greatly increases the likelihood of the user setting up Zaps correctly. Users will still be able to map custom fields, but this gets them started on the right foot.

Read more about implementing dynamic dropdowns below:

  • CLI: https://github.com/zapier/zapier-platform-cli#dynamic-dropdowns
  • Web Builder: https://zapier.com/developer/documentation/v2/dynamic-dropdowns/

ZDW013 - There Should Be At Least X Important Triggers|Searches|Actions in an App

In order to highlight your most popular steps and give the user a clear recommendation of what to use Zapier for, we encourage the use of “important” steps. Important steps are shown first in the UI, while non-important steps are shown after a “show more” click.

These can be adjusted in the settings for each individual step, either via checkbox (V2 Platform) or via the important property (CLI)


ZDE500 - Upgrading the platform version on a public app is not allowed right now.

Please reach out to partners@zapier.com to get your new version of your app deployed. Unfortuntely, partners are not able to deploy a new platform version public app at this time.


ZDE501 - You cannot add this required field without a previously matching field

Adding a new required field within an existing trigger/action/search or authentication may break all existing Zaps. There are a few ways to handle this:

  1. If the new required field is within a trigger/action/search, you can hide the old/existing trigger/action/search and create a new one with the required field added. All exisitng Zaps will continue to function with the older/hidden item, but new Zaps will use the new trigger/action/search with the required field.
  2. Define the new field without using the ““required”” flag, and then use scripting for the trigger/action/search to specify a default value, before sending requests to your API endpoint.

ZDE502 - You cannot change the auth type

Changing the auth type of your app will break all existing user’s Zaps. Please visit this doc for the available options: https://zapier.com/developer/documentation/v2/migrating-your-zapier-integration/


ZDE503 - Do not remove this trigger!

You cannot remove triggers that have live Zaps. You’ll need to hide the trigger instead so it is no longer available to use in new Zaps.

To mark a trigger as hidden - add hidden: true parameter to the trigger’s display definition.

{
  key: 'my_tigger',
  noun: 'My Trigger'
  display: {
    label: 'My Old Trigger!',
    description: 'Triggers when it trigger',
    hidden: true
  }
}

ZDE504 - You cannot change an existing trigger’s data source

Changing the data source of a trigger breaks live Zaps. Instead, you’ll want to hide this trigger and create a new trigger with the updated data source. All existing Zaps will continue to function as is, but new Zaps will use the new trigger with the updated data source.


ZDE505 - Do not remove this action!

You cannot remove action that have live Zaps. You’ll need to hide the action instead so it is no longer available to use in new Zaps.


You cannot remove search that have live Zaps. You’ll need to hide the search instead so it is no longer available to use in new Zaps.


ZDW500 - The new (OAuth) does not match the old one.

The Client ID, Client Secret, Authorization URL, or Access Token URL has changed. Validate that the current version is correct.


Have any feedback or questions? Let us know.