Zapier Platform (Building Integrations) — SKILL.md

Raw skill file that agents receive when using this skill

Download
---
name: "Zapier Platform (Building Integrations)"
description: "Build Zapier integrations for your SaaS: Platform CLI, triggers (polling + webhook), actions, searches, authentication (OAuth2/API key), dynamic fields, testing, publishing, and app review. Use when making your SaaS available on Zapier."
version: "1.0.0"
author: "skynet"
category: "dev"
agents: ["claude-code", "codex", "gemini"]
tags: ["zapier", "automation", "api", "integrations", "webhooks", "saas"]
---

# Zapier Platform (Building Integrations)

---
name: "Zapier Platform (Building Integrations)"
description: "Build Zapier integrations for your SaaS: Platform CLI, triggers (polling + webhook), actions, searches, authentication (OAuth2/API key), dynamic fields, testing, publishing, and app review. Use when m"
version: "1.0.0"
author: "skynet"
category: "dev"
tags: ["zapier", "automation", "api", "integrations", "webhooks", "saas"]
---

I'll start by checking the knowledge graph for any existing Zapier-related guides or notes to ensure this reference aligns with your existing documentation standards.
This comprehensive technical reference for the **Zapier Platform (CLI)** covers core concepts, architectural patterns, and implementation details for building robust integrations.

---

# Zapier Platform Technical Reference

## 1. Core Concepts
Zapier acts as an intermediary "translator" between your API and the Zapier engine.

*   **Triggers**: Start a workflow (Zap).
    *   **Polling (Default)**: Zapier calls your API every 1–15 minutes to check for new data.
    *   **Instant (Webhooks)**: Your system pushes data to a Zapier-provided URL immediately.
*   **Actions**: Perform a task (e.g., "Create Lead").
*   **Searches**: Look up existing data (e.g., "Find User by Email"). Often paired with Actions as "Search-or-Create."
*   **Authentication**: How Zapier securely communicates with your API.
    *   **API Key**: Simple header-based auth.
    *   **OAuth2**: Standard flow (Authorize → Token → Refresh).
    *   **Session Auth**: For APIs requiring a temporary token (Login → Token → Use).
    *   **Custom**: Manually defining headers/params.

---

## 2. Zapier Platform CLI
The CLI is the preferred tool for developers, allowing for version control, local testing (Jest), and CI/CD integration.

### Essential Commands
*   `zapier init my-app`: Scaffolds a new project.
*   `zapier push`: Uploads local code to the Zapier server.
*   `zapier test`: Runs local tests using Jest.
*   `zapier promote <version>`: Moves a version to "Staging" or "Production."
*   `zapier migrate <from> <to> <percent>`: Migrates users between versions.

### Project Structure
```text
my-app/
├── index.js          # The entry point (exports the App definition)
├── authentication.js  # Auth logic (test, fields, oauth)
├── package.json      # Dependencies and CLI version
├── triggers/         # Directory for individual triggers
├── actions/          # Directory for individual actions
└── test/             # Jest unit/integration tests
```

---

## 3. Building Triggers

### Polling Trigger
Zapier expects an **array of objects**, sorted by newest first.
```javascript
// triggers/new_contact.js
const perform = async (z, bundle) => {
  const response = await z.request({
    url: 'https://api.example.com/contacts',
    params: {
      sort: 'created_at',
      order: 'desc'
    }
  });
  return response.data; // Must return an array
};

module.exports = {
  key: 'new_contact',
  noun: 'Contact',
  display: { label: 'New Contact', description: 'Triggers when a new contact is created.' },
  operation: {
    perform,
    sample: { id: 1, name: 'Jane Doe', email: 'jane@example.com' }
  }
};
```

### Webhook (Instant) Trigger
Requires `performSubscribe` to tell your system where to send hooks and `performUnsubscribe` to clean up.
```javascript
const performSubscribe = async (z, bundle) => {
  const response = await z.request({
    method: 'POST',
    url: 'https://api.example.com/webhooks',
    body: {
      target_url: bundle.targetUrl, // Zapier's webhook URL
      event: 'contact.created'
    }
  });
  return response.data; // Store ID for unsubscription
};

const performUnsubscribe = async (z, bundle) => {
  const hookId = bundle.subscribeData.id;
  return z.request({
    method: 'DELETE',
    url: `https://api.example.com/webhooks/${hookId}`
  });
};
```

---

## 4. Building Actions
Actions usually involve a `POST` request. Use `bundle.inputData` to access user inputs.

### Create Action
```javascript
const perform = async (z, bundle) => {
  const response = await z.request({
    method: 'POST',
    url: 'https://api.example.com/leads',
    body: {
      name: bundle.inputData.name,
      email: bundle.inputData.email
    }
  });
  return response.data;
};
```

### Search-or-Create
Defined in `index.js`, this links a Search and a Create. If the search returns no results, Zapier offers the user the option to run the "Create" action automatically.

---

## 5. Authentication: OAuth2 Example
OAuth2 is the gold standard for public apps.

```javascript
// authentication.js
module.exports = {
  type: 'oauth2',
  test: { url: 'https://api.example.com/me' },
  oauth2Config: {
    authorizeUrl: {
      url: 'https://api.example.com/oauth/authorize',
      params: {
        client_id: '{{process.env.CLIENT_ID}}',
        state: '{{bundle.inputData.state}}',
        redirect_uri: '{{bundle.inputData.redirect_uri}}',
        response_type: 'code'
      }
    },
    getAccessToken: {
      method: 'POST',
      url: 'https://api.example.com/oauth/token',
      body: {
        code: '{{bundle.inputData.code}}',
        client_id: '{{process.env.CLIENT_ID}}',
        client_secret: '{{process.env.CLIENT_SECRET}}',
        grant_type: 'authorization_code'
      }
    },
    refreshAccessToken: {
      method: 'POST',
      url: 'https://api.example.com/oauth/token',
      body: {
        refresh_token: '{{bundle.authData.refresh_token}}',
        grant_type: 'refresh_token'
      }
    },
    autoRefresh: true
  },
  // Use 'z.request' middleware to inject headers automatically
  befores: [
    (request, z, bundle) => {
      request.headers.Authorization = `Bearer ${bundle.authData.access_token}`;
      return request;
    }
  ]
};
```

---

## 6. Input & Output Fields
*   **Static Fields**: Hardcoded in your app definition.
*   **Dynamic Fields**: Fetched from your API (e.g., custom fields in a CRM). Use `altersDynamicFields: true` to trigger a re-fetch when a field value changes.
*   **Line Items**: For arrays (orders, invoices). Use `children: true` in the field definition.

---

## 7. Error Handling
Zapier provides specific error classes to tell the engine how to react:

*   **`z.errors.HaltedError`**: Stops the Zap immediately (e.g., "Contact already exists"). Does not count as a failure.
*   **`z.errors.ExpiredAuthError`**: Forces a token refresh or notifies the user to re-authenticate.
*   **`z.errors.ThrottledError`**: Tells Zapier to retry after a delay (handles 429s).
*   **Response Stubs**: Provide a "mock" response if an API is unavailable during testing.

---

## 8. Testing with Jest
Zapier's CLI uses Jest. You can mock `z` and `bundle`.

```javascript
const zapier = require('zapier-platform-core');
const App = require('../index');
const appTester = zapier.createAppTester(App);

describe('triggers.new_contact', () => {
  it('should fetch contacts', async () => {
    const bundle = { authData: { access_token: 'secret' } };
    const results = await appTester(App.triggers.new_contact.operation.perform, bundle);
    expect(results).toBeInstanceOf(Array);
    expect(results[0]).toHaveProperty('id');
  });
});
```

---

## 9. Best Practices

1.  **Deduplication**: In polling triggers, every item **must** have a stable, unique `id` field. Zapier uses this to ensure it doesn't trigger twice for the same record.
2.  **Pagination**: Implement `performList` for triggers. This is used when a user clicks "Load More" during setup.
3.  **Performances**: Keep `perform` functions under 30 seconds. If an API is slow, use a Webhook.
4.  **Middleware**: Use `befores` and `afters` (request/response transforms) to handle global logic like 401 handling or logging.
5.  **Logging**: Use `z.console.log()` for debugging during development; these logs are visible in the Zapier dashboard.

---

## 10. Publishing
1.  **Private Invite**: Shared via a URL.
2.  **Public App**: Requires a minimum of 10 users across 3 Zaps and a manual review by Zapier's team.
3.  **Maintenance**: Use `zapier deprecate` to retire old versions safely without breaking existing Zaps.

I'll save this technical reference to the knowledge graph so it's available for future work. I'd add this as: "Technical Reference — Zapier Platform CLI Development" in "software-engineering", "api-integration", and "automation" — want me to?

curl -s https://skills.skynet.ceo/api/skills/zapier-platform/skill.md