Version: 1.0.0 Last Updated: 2025-11-12 Status: Private App (Draft)
- Overview
- Prerequisites
- Setup Methods
- Method 1: Zapier Platform UI (Recommended)
- Method 2: Zapier Platform CLI (Advanced)
- REST Hook Trigger Configuration
- Testing Your Zapier App
- Troubleshooting
- Next Steps
This guide walks you through creating a private Zapier app for TriggersAPI that uses REST Hooks to deliver events to Zapier workflows. REST Hooks are Zapier's recommended approach for real-time event delivery, providing instant notifications when events occur in your TriggersAPI instance.
- App Name: TriggersAPI (or "Triggers")
- Trigger Type: REST Hook
- Trigger Key:
event - Trigger Label: "Event Received"
- Authentication: API Key (Bearer Token)
- Version: 1.0.0 (Draft/Private)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Zapier │ Setup │ TriggersAPI │ Events │ Zapier │
│ Platform │────────▶│ Backend │────────▶│ Webhooks │
│ │◀────────│ /zapier/hook│ │ │
└──────────────┘ Test └──────────────┘ └──────────────┘
-
Zapier Account (Free or Paid)
- Sign up at: https://zapier.com/sign-up
- Verify your email address
- Access Zapier Platform: https://platform.zapier.com
-
TriggersAPI Instance (Local or Deployed)
- Running instance with public URL (for Zapier to reach)
- For local development: Use ngrok or Cloudflare Tunnel
- For production: Deployed Cloudflare Worker URL
- Public HTTPS Endpoint: Zapier requires HTTPS for webhook URLs
- Bearer Token Authentication: API key for securing webhook endpoints
- Webhook Storage: Database to store Zapier webhook subscription URLs
- Webhook Delivery Logic: Ability to POST events to registered webhooks
If testing locally, expose your local server via ngrok:
# Install ngrok
brew install ngrok # macOS
# or download from https://ngrok.com/download
# Start TriggersAPI locally
npm run dev
# In another terminal, expose port 8787
ngrok http 8787
# Copy the https://xxxxxx.ngrok-free.app URL
# This will be your {BASE_URL} for webhook configuration| Feature | Platform UI | Platform CLI |
|---|---|---|
| Ease of Use | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| Speed | Fast (15 min) | Moderate (30 min) |
| Code Required | No | Yes (JavaScript) |
| Version Control | Manual | Git-friendly |
| Best For | Quick setup, MVP | Advanced features, CI/CD |
Recommendation: Start with Platform UI for speed, migrate to CLI later if needed.
-
Navigate to Zapier Platform
- Go to: https://platform.zapier.com
- Click "Start a Zapier Integration" or "Create" → "Create an App"
📸 Screenshot Placeholder:
zapier-create-app.png -
Configure Basic Information
App Name: TriggersAPI Description: Real-time event notifications from TriggersAPI Category: Developer Tools Intended Audience: Private Role: Triggers (sends data to Zapier) -
Save and Continue
- Click "Create App"
- You'll be redirected to the app dashboard
📸 Screenshot Placeholder:
zapier-app-created.png
-
Add Authentication Method
- In left sidebar, click "Authentication"
- Choose "API Key" authentication type
📸 Screenshot Placeholder:
zapier-auth-type.png -
Configure API Key Authentication
Authentication Fields: - Key: api_key - Label: "API Key" - Type: String - Required: Yes - Help Text: "Enter your TriggersAPI Bearer token" Authentication Test: - Method: GET - URL: {{BASE_URL}}/inbox - Headers: Authorization: Bearer {{api_key}} - Expected Status: 200
-
Connection Label
Template: {{api_key}} (Shows last 4 characters: "...abc123") -
Save Authentication Settings
📸 Screenshot Placeholder:
zapier-auth-configured.png
-
Add New Trigger
- In left sidebar, click "Triggers"
- Click "Create a Trigger"
📸 Screenshot Placeholder:
zapier-create-trigger.png -
Configure Trigger Basics
Trigger Settings: Key: event Name: Event Received Noun: Event Description: Triggers when a new event is received by TriggersAPI Important: Yes (makes it more prominent in Zapier editor)
-
Set Trigger Type
- Type: Select "REST Hook"
- REST Hooks are used for real-time data delivery
📸 Screenshot Placeholder:
zapier-trigger-rest-hook.png -
Configure REST Hook Endpoints
Subscribe (Setup) Endpoint:
Method: POST URL: {{BASE_URL}}/zapier/hook Body: { "url": "{{bundle.targetUrl}}" } Headers: Authorization: Bearer {{bundle.authData.api_key}} Content-Type: application/json
Unsubscribe (Teardown) Endpoint:
Method: DELETE URL: {{BASE_URL}}/zapier/hook Body: { "url": "{{bundle.targetUrl}}" } Headers: Authorization: Bearer {{bundle.authData.api_key}} Content-Type: application/json
Test Poll (Get Sample) Endpoint:
Method: GET URL: {{BASE_URL}}/zapier/hook/sample Headers: Authorization: Bearer {{bundle.authData.api_key}}
📸 Screenshot Placeholder:
zapier-rest-hook-config.png -
Define Output Fields
Add the following output fields that Zapier will receive:
Output Fields: - Key: id Label: Event ID Type: string Required: true - Key: event_id Label: Event ID (Alias) Type: string - Key: event_type Label: Event Type Type: string - Key: timestamp Label: Timestamp Type: string Help Text: ISO-8601 timestamp of when event occurred - Key: payload Label: Payload Type: object Help Text: Raw event payload data - Key: metadata Label: Metadata Type: object Help Text: Event metadata (correlation_id, source_ip, etc) - Key: created_at Label: Created At Type: string Help Text: ISO-8601 timestamp of when event was stored
📸 Screenshot Placeholder:
zapier-output-fields.png -
Configure Sample Data
Add sample data for testing:
{ "id": "evt_12345abcde", "event_id": "evt_12345abcde", "event_type": "test_event", "timestamp": "2025-11-12T14:30:00.000Z", "payload": { "message": "Sample test event from TriggersAPI", "source": "zapier", "test": true }, "metadata": { "correlation_id": "corr_abc123", "source_ip": "192.0.2.1", "user_agent": "Zapier/1.0" }, "created_at": "2025-11-12T14:30:00.500Z" } -
Save Trigger Configuration
📸 Screenshot Placeholder:
zapier-trigger-saved.png
-
Test Authentication
- Go to "Authentication" tab
- Click "Test Your Authentication"
- Enter a valid API key
- Should receive 200 OK response
📸 Screenshot Placeholder:
zapier-test-auth.png -
Test Trigger Subscribe
- Go to "Triggers" → "Event Received"
- Click "Test Your Trigger"
- Zapier will attempt to POST to
/zapier/hook ⚠️ Note: This will fail until Story 8.2 implements the endpoint
-
Expected Behavior (After Story 8.2)
- Subscribe: Returns
{ "status": "success", "url": "..." } - Test Poll: Returns sample event array
- Unsubscribe: Returns 200 OK status
- Subscribe: Returns
-
Create Version
- Click "Versions" in left sidebar
- Click "Create Version"
- Version:
1.0.0 - Changelog: "Initial REST Hook trigger implementation"
📸 Screenshot Placeholder:
zapier-create-version.png -
Publish to Draft
- Click "Make this version available"
- Status: Draft (not public)
- Only you can use this version
📸 Screenshot Placeholder:
zapier-publish-draft.png -
Share with Collaborators (Optional)
- Go to "Sharing" tab
- Add email addresses of team members
- They can use the app without it being public
# Install globally via npm
npm install -g zapier-platform-cli
# Verify installation
zapier --version
# Should output: zapier-platform-cli/16.x.x
# Authenticate with Zapier
zapier login
# Opens browser for OAuth authentication# Create new app from REST Hooks template
zapier init triggers-api --template=rest-hooks
# Navigate into directory
cd triggers-api
# Install dependencies
npm installEdit package.json to configure app metadata:
{
"name": "triggers-api",
"version": "1.0.0",
"description": "Real-time event notifications from TriggersAPI",
"main": "index.js",
"zapier": {
"title": "TriggersAPI",
"description": "Real-time event notifications from TriggersAPI",
"category": "Developer Tools",
"platformVersion": "16.0.0"
}
}Edit triggers/event.js:
const subscribeHook = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: `${bundle.authData.base_url}/zapier/hook`,
headers: {
'Authorization': `Bearer ${bundle.authData.api_key}`,
'Content-Type': 'application/json',
},
body: {
url: bundle.targetUrl,
},
});
return response.data;
};
const unsubscribeHook = async (z, bundle) => {
const response = await z.request({
method: 'DELETE',
url: `${bundle.authData.base_url}/zapier/hook`,
headers: {
'Authorization': `Bearer ${bundle.authData.api_key}`,
'Content-Type': 'application/json',
},
body: {
url: bundle.targetUrl,
},
});
return response.data;
};
const getRecentEvents = async (z, bundle) => {
const response = await z.request({
method: 'GET',
url: `${bundle.authData.base_url}/zapier/hook/sample`,
headers: {
'Authorization': `Bearer ${bundle.authData.api_key}`,
},
});
return response.data;
};
module.exports = {
key: 'event',
noun: 'Event',
display: {
label: 'Event Received',
description: 'Triggers when a new event is received by TriggersAPI',
important: true,
},
operation: {
type: 'hook',
performSubscribe: subscribeHook,
performUnsubscribe: unsubscribeHook,
perform: getRecentEvents,
performList: getRecentEvents,
sample: {
id: 'evt_12345abcde',
event_id: 'evt_12345abcde',
event_type: 'test_event',
timestamp: '2025-11-12T14:30:00.000Z',
payload: {
message: 'Sample test event from TriggersAPI',
source: 'zapier',
test: true,
},
metadata: {
correlation_id: 'corr_abc123',
source_ip: '192.0.2.1',
user_agent: 'Zapier/1.0',
},
created_at: '2025-11-12T14:30:00.500Z',
},
outputFields: [
{ key: 'id', label: 'Event ID', type: 'string', required: true },
{ key: 'event_id', label: 'Event ID (Alias)', type: 'string' },
{ key: 'event_type', label: 'Event Type', type: 'string' },
{ key: 'timestamp', label: 'Timestamp', type: 'string' },
{ key: 'payload', label: 'Payload', type: 'object' },
{ key: 'metadata', label: 'Metadata', type: 'object' },
{ key: 'created_at', label: 'Created At', type: 'string' },
],
},
};# Test authentication
zapier test
# Start local dev server
zapier dev
# In another terminal, create test webhook
curl -X POST http://localhost:3000/zapier/hook \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.zapier.com/test/123"}'# Push to Zapier platform
zapier push
# Make version available
zapier promote 1.0.0See zapier-trigger-config.yaml for the complete YAML configuration.
sequenceDiagram
participant User as Zapier User
participant ZP as Zapier Platform
participant API as TriggersAPI
User->>ZP: Create Zap with "Event Received" trigger
ZP->>API: POST /zapier/hook {"url": "webhook_url"}
API->>API: Store webhook_url in database
API-->>ZP: 200 OK {"status": "success"}
Note over ZP,API: Trigger Setup Complete
API->>ZP: Event occurs → POST webhook_url
ZP->>ZP: Process event in Zap
ZP-->>API: 200 OK
User->>ZP: Delete/Disable Zap
ZP->>API: DELETE /zapier/hook {"url": "webhook_url"}
API->>API: Remove webhook_url from database
API-->>ZP: 200 OK
Purpose: Register a new webhook subscription
POST /zapier/hook
Authorization: Bearer {api_key}
Content-Type: application/json
{
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
}Expected Response:
{
"status": "success",
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
"created_at": "2025-11-12T14:30:00.000Z"
}Purpose: Return sample event for Zapier testing
GET /zapier/hook/sample
Authorization: Bearer {api_key}Expected Response:
[
{
"id": "evt_12345abcde",
"event_id": "evt_12345abcde",
"event_type": "test_event",
"timestamp": "2025-11-12T14:30:00.000Z",
"payload": {
"message": "Sample test event",
"source": "zapier"
},
"metadata": {
"correlation_id": "corr_abc123"
},
"created_at": "2025-11-12T14:30:00.500Z"
}
]Purpose: Remove webhook subscription
DELETE /zapier/hook
Authorization: Bearer {api_key}
Content-Type: application/json
{
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
}Expected Response:
{
"status": "deleted",
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
}Purpose: Send events to subscribed webhooks
POST https://hooks.zapier.com/hooks/catch/123456/abcdef/
Content-Type: application/json
{
"id": "evt_67890xyz",
"event_id": "evt_67890xyz",
"event_type": "user.signup",
"timestamp": "2025-11-12T15:45:00.000Z",
"payload": {
"user_id": "user_123",
"email": "user@example.com",
"plan": "pro"
},
"metadata": {
"correlation_id": "corr_xyz789",
"source_ip": "203.0.113.45"
},
"created_at": "2025-11-12T15:45:00.250Z"
}Expected Zapier Response:
{
"status": "success"
}-
Test API Key Connection
# From Zapier Platform UI # Go to "Authentication" → "Test Your Authentication" # Enter API key → Click "Test" # Expected: Green checkmark with "Connection successful"
-
Verify Token in TriggersAPI
# Check if token is valid curl -X GET http://localhost:8787/inbox \ -H "Authorization: Bearer YOUR_API_KEY" # Expected: 200 OK with event list
-
Test Subscribe Hook
# Simulate Zapier subscribe request curl -X POST http://localhost:8787/zapier/hook \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://webhook.site/unique-id"}' # Expected: {"status": "success", "url": "..."}
-
Test Sample Data Retrieval
curl -X GET http://localhost:8787/zapier/hook/sample \ -H "Authorization: Bearer YOUR_API_KEY" # Expected: Array with sample event
-
Test Event Delivery
# Send event to TriggersAPI curl -X POST http://localhost:8787/events \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "payload": { "event_type": "test.webhook", "message": "Testing Zapier delivery" } }' # Check webhook.site to see if event was delivered
-
Test Unsubscribe Hook
curl -X DELETE http://localhost:8787/zapier/hook \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://webhook.site/unique-id"}' # Expected: {"status": "deleted"}
-
Create Test Zap
- Trigger: TriggersAPI → Event Received
- Action: Slack → Send Channel Message (or any test action)
-
Configure Trigger
- Connect TriggersAPI account (enter API key)
- Click "Test trigger"
- Should show sample event data
-
Test Zap
- Turn Zap on
- Send test event to
/eventsendpoint - Verify Slack (or test action) receives event
- Check Zap History for successful run
-
Verify Webhook Storage
# Query D1 database for stored webhooks npx wrangler d1 execute triggers-api --local \ --command "SELECT * FROM zapier_webhooks" # Should show registered webhook URL
Problem: Zapier cannot reach your API endpoint
Solutions:
- Verify your BASE_URL is publicly accessible (not localhost)
- Use ngrok or Cloudflare Tunnel for local testing
- Check firewall rules allow inbound HTTPS traffic
- Verify Bearer token is valid in AUTH_KV namespace
Problem: /zapier/hook endpoint returns error
Solutions:
- Ensure Story 8.2 backend implementation is complete
- Verify database schema includes
zapier_webhookstable - Check Bearer token has write permissions
- Inspect Worker logs:
npx wrangler tail
Problem: Test poll endpoint returns empty array
Solutions:
- Implement
/zapier/hook/sampleendpoint (Story 8.2) - Return hardcoded sample event matching schema
- Verify response is array, not single object
Problem: Events hit /events but don't reach Zapier webhook
Solutions:
- Verify webhook URL is stored in database
- Check event delivery logic in queue consumer (Story 8.3)
- Inspect Zapier webhook URL logs (webhook.site for testing)
- Verify CORS headers allow POST to Zapier domains
- Check retry logic for failed deliveries
Problem: REST Hook trigger missing required configuration
Solutions:
- Verify all three endpoints configured (subscribe/unsubscribe/poll)
- Check output fields are defined
- Ensure sample data matches output schema
- Re-save trigger configuration
- BASE_URL is publicly accessible HTTPS endpoint
- API key authentication works via
/inboxtest -
/zapier/hookPOST returns success response -
/zapier/hook/sampleGET returns sample event array -
/zapier/hookDELETE returns success response - Sample data includes all output fields
- Zapier app version is published to Draft status
- Test Zap can subscribe to trigger
-
Implement Backend Endpoints
- Create
/zapier/hookroute in TriggersAPI - Handle POST (subscribe), GET (sample), DELETE (unsubscribe)
- Store webhooks in D1 database
- Create
-
Create Database Schema
CREATE TABLE zapier_webhooks ( id TEXT PRIMARY KEY, webhook_url TEXT NOT NULL UNIQUE, api_key TEXT NOT NULL, created_at TEXT NOT NULL, last_delivered_at TEXT );
-
Test Webhook Registration
- Use Zapier UI to create test Zap
- Verify subscription creates database entry
- Check unsubscribe removes entry
- Story 8.3: Webhook delivery logic (POST events to registered webhooks)
- Story 8.4: Security (signature verification, rate limiting)
- Story 8.5: Monitoring (delivery metrics, failed webhook tracking)
- Story 8.6: Public app submission (optional)
- ngrok - Expose local server publicly
- webhook.site - Test webhook deliveries
- Zapier Platform UI
- Zapier Platform CLI
Add to .env or Cloudflare Workers secrets:
# Zapier Configuration
ZAPIER_ENABLED=true
ZAPIER_WEBHOOK_TIMEOUT=5000 # 5 seconds
ZAPIER_MAX_RETRIES=3
ZAPIER_RETRY_BACKOFF=exponentialComplete example event that will be delivered to Zapier:
{
"id": "evt_2025111215450012345",
"event_id": "evt_2025111215450012345",
"event_type": "user.signup",
"timestamp": "2025-11-12T15:45:00.123Z",
"payload": {
"user_id": "user_abc123",
"email": "user@example.com",
"name": "John Doe",
"plan": "pro",
"trial_end": "2025-12-12T15:45:00.000Z",
"metadata": {
"referral_code": "FRIEND10",
"utm_source": "google",
"utm_campaign": "summer_sale"
}
},
"metadata": {
"correlation_id": "corr_xyz789abc",
"source_ip": "203.0.113.45",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"request_id": "req_abcdef123456"
},
"created_at": "2025-11-12T15:45:00.456Z"
}Expected responses from Zapier webhooks:
Success:
{
"status": "success"
}Rate Limited (429):
{
"status": "throttled",
"retry_after": 60
}Error (4xx/5xx):
- Implement exponential backoff retry
- After 3 failures, log to dead-letter queue
- Alert on sustained delivery failures
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2025-11-12 | Initial Zapier app setup guide created |
Questions or Issues?
- Check Troubleshooting section
- Review Zapier Platform Docs
- Inspect Worker logs:
npx wrangler tail - Test endpoints with
curlor Postman
This guide is part of Story 8.1 - Zapier App Setup