What are Triggers?
Triggers are the starting point of automation flows. They listen for events and start flow executions when those events occur. Every flow must begin with a trigger.
Examples:
New message posted in Slack
New file uploaded to Google Drive
New issue created in GitHub
New row added to database
Trigger Types
Activepieces supports three types of triggers:
Polling Periodically check for new data
Webhook Receive real-time notifications
App Webhook Platform-level webhook handling
Creating a Trigger
Use the CLI to generate a trigger:
You’ll be prompted for:
Piece name : Which piece to add the trigger to
Trigger name : Unique identifier (e.g., new_message)
Display name : Human-readable name (e.g., New Message)
Description : What event the trigger watches for
Trigger type : Polling, Webhook, or App Webhook
Polling Triggers
Polling triggers check for new data at regular intervals (e.g., every 5 minutes).
import { createTrigger , Property , TriggerStrategy } from '@activepieces/pieces-framework' ;
import { pollingAuth } from '../auth' ;
import Parser from 'rss-parser' ;
export const newRssItem = createTrigger ({
auth: pollingAuth ,
name: 'new_item_in_feed' ,
displayName: 'New Item in Feed' ,
description: 'Triggers when a new item is published in an RSS feed' ,
// Polling trigger type
type: TriggerStrategy . POLLING ,
props: {
feedUrl: Property . ShortText ({
displayName: 'Feed URL' ,
description: 'The URL of the RSS feed' ,
required: true ,
}),
},
// Sample data for testing
sampleData: {
title: 'Sample Article' ,
link: 'https://example.com/article' ,
pubDate: '2024-01-15T10:00:00Z' ,
content: 'Article content...' ,
},
// Called when trigger is enabled
async onEnable ( context ) {
// Optional: Store initial state
const parser = new Parser ();
const feed = await parser . parseURL ( context . propsValue . feedUrl );
if ( feed . items . length > 0 ) {
// Store the latest item's date to avoid duplicates
await context . store . put ( 'lastPubDate' , feed . items [ 0 ]. pubDate );
}
},
// Called when trigger is disabled
async onDisable ( context ) {
// Cleanup if needed
await context . store . delete ( 'lastPubDate' );
},
// Called periodically to check for new items
async run ( context ) {
const parser = new Parser ();
const feed = await parser . parseURL ( context . propsValue . feedUrl );
// Get last processed date
const lastPubDate = await context . store . get < string >( 'lastPubDate' );
const lastDate = lastPubDate ? new Date ( lastPubDate ) : null ;
// Filter new items
const newItems = feed . items . filter (( item ) => {
if ( ! lastDate ) return true ;
const itemDate = new Date ( item . pubDate || '' );
return itemDate > lastDate ;
});
// Update last processed date
if ( newItems . length > 0 ) {
await context . store . put ( 'lastPubDate' , newItems [ 0 ]. pubDate );
}
// Return new items (they'll trigger flow executions)
return newItems ;
},
});
Polling Trigger Configuration
type
TriggerStrategy.POLLING
required
Set trigger type to polling
Called when user enables the trigger. Use to initialize state. async onEnable ( context ) {
// Store initial state
await context . store . put ( 'lastId' , null );
}
Called when user disables the trigger. Use for cleanup. async onDisable ( context ) {
// Clean up stored state
await context . store . delete ( 'lastId' );
}
Called periodically to check for new data. Return array of new items. async run ( context ) {
const lastId = await context . store . get ( 'lastId' );
const newItems = await fetchNewItems ( lastId );
if ( newItems . length > 0 ) {
await context . store . put ( 'lastId' , newItems [ 0 ]. id );
}
return newItems ; // Each item triggers a flow execution
}
Webhook Triggers
Webhook triggers receive real-time notifications from external services. They provide instant execution when events occur.
Example: New GitHub Issue
import { createTrigger , Property , TriggerStrategy } from '@activepieces/pieces-framework' ;
import { githubAuth } from '../auth' ;
import { DedupeStrategy , HttpMethod , httpClient } from '@activepieces/pieces-common' ;
export const newIssue = createTrigger ({
auth: githubAuth ,
name: 'new_issue' ,
displayName: 'New Issue' ,
description: 'Triggers when a new issue is created' ,
// Webhook trigger type
type: TriggerStrategy . WEBHOOK ,
props: {
repository: Property . ShortText ({
displayName: 'Repository' ,
description: 'Repository name (owner/repo)' ,
required: true ,
}),
},
sampleData: {
action: 'opened' ,
issue: {
id: 1 ,
number: 42 ,
title: 'Sample Issue' ,
body: 'Issue description' ,
user: {
login: 'username' ,
},
created_at: '2024-01-15T10:00:00Z' ,
},
},
// Register webhook when enabled
async onEnable ( context ) {
const [ owner , repo ] = context . propsValue . repository . split ( '/' );
// Register webhook with GitHub
const response = await httpClient . sendRequest ({
method: HttpMethod . POST ,
url: `https://api.github.com/repos/ ${ owner } / ${ repo } /hooks` ,
headers: {
'Authorization' : `Bearer ${ context . auth . access_token } ` ,
'Content-Type' : 'application/json' ,
},
body: {
name: 'web' ,
active: true ,
events: [ 'issues' ],
config: {
url: context . webhookUrl , // Platform provides this
content_type: 'json' ,
secret: context . webhookSecret , // For verification
},
},
});
// Store webhook ID for cleanup
await context . store . put ( 'webhookId' , response . body . id );
},
// Unregister webhook when disabled
async onDisable ( context ) {
const webhookId = await context . store . get < number >( 'webhookId' );
if ( ! webhookId ) return ;
const [ owner , repo ] = context . propsValue . repository . split ( '/' );
await httpClient . sendRequest ({
method: HttpMethod . DELETE ,
url: `https://api.github.com/repos/ ${ owner } / ${ repo } /hooks/ ${ webhookId } ` ,
headers: {
'Authorization' : `Bearer ${ context . auth . access_token } ` ,
},
});
await context . store . delete ( 'webhookId' );
},
// Process incoming webhook data
async run ( context ) {
const payload = context . payload . body ;
// Filter for new issues only
if ( payload . action !== 'opened' ) {
return [];
}
// Return the issue data
return [ payload ];
},
});
Webhook Trigger Configuration
type
TriggerStrategy.WEBHOOK
required
Set trigger type to webhook
Register the webhook with the external service. async onEnable ( context ) {
const webhookId = await registerWebhook ({
url: context . webhookUrl ,
secret: context . webhookSecret ,
});
await context . store . put ( 'webhookId' , webhookId );
}
Unregister the webhook. async onDisable ( context ) {
const webhookId = await context . store . get ( 'webhookId' );
await unregisterWebhook ( webhookId );
await context . store . delete ( 'webhookId' );
}
Process incoming webhook payload. Return array of events. async run ( context ) {
const payload = context . payload . body ;
// Filter/transform as needed
if ( shouldProcess ( payload )) {
return [ payload ];
}
return [];
}
App Webhook Triggers
App webhooks use platform-level webhooks shared across all users. The platform manages webhook registration automatically.
Example: Slack New Message
packages/pieces/community/slack/src/lib/triggers/new-message.ts
import { createTrigger , Property , TriggerStrategy } from '@activepieces/pieces-framework' ;
import { slackAuth } from '../auth' ;
export const newMessageTrigger = createTrigger ({
auth: slackAuth ,
name: 'new-message' ,
displayName: 'New Public Message Posted Anywhere' ,
description: 'Triggers when a new message is posted to any channel' ,
props: {
ignoreBots: Property . Checkbox ({
displayName: 'Ignore Bot Messages?' ,
required: true ,
defaultValue: false ,
}),
},
// App webhook type
type: TriggerStrategy . APP_WEBHOOK ,
sampleData: {
type: 'message' ,
channel: 'C1234567890' ,
user: 'U1234567890' ,
text: 'Hello, world!' ,
ts: '1234567890.123456' ,
},
// Register to listen for events
async onEnable ( context ) {
// Get team ID from OAuth data
const teamId = context . auth . data [ 'team_id' ] ?? context . auth . data [ 'team' ][ 'id' ];
// Tell platform to route 'message' events for this team to this trigger
context . app . createListeners ({
events: [ 'message' ],
identifierValue: teamId ,
});
},
async onDisable ( context ) {
// Platform handles cleanup automatically
},
// Process events routed by platform
async run ( context ) {
const payload = context . payload . body ;
// Filter out non-channel messages
if ( ! [ 'channel' , 'group' ]. includes ( payload . event . channel_type )) {
return [];
}
// Filter out bot messages if configured
if ( context . propsValue . ignoreBots && payload . event . bot_id ) {
return [];
}
return [ payload . event ];
},
});
App Webhook Configuration
For app webhooks, you also need to configure event processors in the main piece:
export const slack = createPiece ({
displayName: 'Slack' ,
auth: slackAuth ,
// Event processors for app webhooks
events: {
// Parse incoming events and route them
parseAndReply : ({ payload , server }) => {
const eventPayloadBody = payload . body ;
// Handle Slack challenge for webhook verification
if ( eventPayloadBody . challenge ) {
return {
reply: {
body: eventPayloadBody . challenge ,
headers: {},
},
};
}
// Return event type and identifier for routing
return {
event: eventPayloadBody ?. event ?. type ,
identifierValue: eventPayloadBody . team_id ,
};
},
// Verify webhook authenticity
verify : ({ webhookSecret , payload }) => {
const timestamp = payload . headers [ 'x-slack-request-timestamp' ];
const signature = payload . headers [ 'x-slack-signature' ];
const signatureBaseString = `v0: ${ timestamp } : ${ payload . rawBody } ` ;
const hmac = crypto . createHmac ( 'sha256' , webhookSecret );
hmac . update ( signatureBaseString );
const computedSignature = `v0= ${ hmac . digest ( 'hex' ) } ` ;
return signature === computedSignature ;
},
},
actions: [ ... ],
triggers: [ newMessageTrigger ],
});
Trigger Context
The context object in triggers:
interface TriggerContext {
auth : OAuth2PropertyValue | string | CustomAuthValue ;
propsValue : Record < string , any >;
// Webhook-specific
webhookUrl ?: string ; // URL to register with service
webhookSecret ?: string ; // Secret for verification
payload ?: { // Incoming webhook data
body : any ;
headers : Record < string , string >;
rawBody : string ;
};
// App webhook specific
app ?: {
createListeners ( config : {
events : string [];
identifierValue : string ;
}) : void ;
};
// Storage
store : {
get < T >( key : string ) : Promise < T | null >;
put < T >( key : string , value : T ) : Promise < void >;
delete ( key : string ) : Promise < void >;
};
}
Deduplication
Prevent duplicate events from triggering flows multiple times:
import { DEDUPE_KEY_PROPERTY } from '@activepieces/pieces-framework' ;
async run ( context ) {
const newItems = await fetchNewItems ();
// Add dedupe key to each item
return newItems . map (( item ) => ({
... item ,
[ DEDUPE_KEY_PROPERTY ]: item . id , // Use unique identifier
}));
}
The platform will automatically filter out items with duplicate keys.
Testing Triggers
Polling Trigger
Webhook Trigger
App Webhook
Create Test Flow
Add your polling trigger to a new flow
Configure Properties
Fill in required properties
Click Test
The test function (or run if no test function) will execute
Check Output
Verify the trigger returns expected data
Enable Trigger
Add trigger to flow and enable it
Trigger External Event
Perform the action in the external service (e.g., create an issue)
Verify Webhook
Check that webhook was registered correctly
Check Execution
Verify flow executed with correct data
Configure App Webhook
Set up app webhook URL in the service’s settings
Enable Trigger
Add and enable trigger in flow
Trigger Event
Perform action in the service
Verify Routing
Check that event was routed to correct trigger
Best Practices
Store state to track processed items: // Store last processed ID
const lastId = await context . store . get < string >( 'lastId' );
const newItems = items . filter ( item => item . id > lastId );
if ( newItems . length > 0 ) {
await context . store . put ( 'lastId' , newItems [ 0 ]. id );
}
Always return an array, even if empty: async run ( context ) {
const items = await fetchItems ();
if ( ! items || items . length === 0 ) {
return []; // Return empty array, not null
}
return items ;
}
Use dedupe keys to prevent duplicate executions: return items . map ( item => ({
... item ,
[ DEDUPE_KEY_PROPERTY ]: item . id ,
}));
Always clean up in onDisable: async onDisable ( context ) {
const webhookId = await context . store . get ( 'webhookId' );
if ( webhookId ) {
await deleteWebhook ( webhookId );
await context . store . delete ( 'webhookId' );
}
}
Sample data helps users understand the trigger output: sampleData : {
id : '12345' ,
title : 'Example Item' ,
created_at : '2024-01-15T10:00:00Z' ,
author : {
name : 'John Doe' ,
email : '[email protected] ' ,
},
}
Trigger Type Comparison
Feature Polling Webhook App Webhook Latency 5-15 minutes Instant Instant API Calls Regular polling On event only On event only Setup Complexity Simple Moderate Simple User Setup None Webhook registration None (platform handles) Best For RSS feeds, APIs without webhooks Real-time events Shared webhooks (Slack, Discord)
Next Steps
Properties Guide Learn about all property types
Testing Write tests for your triggers