Workflow Automations
Workflow automations are single tier: automation agents that orchestrate multiple services in one codebase. Instead of building a drag-and-drop flow, PIE lets you keep the whole pipeline in real JavaScript so AI can generate something custom-tailored to the exact workflow.
Typical examples:
- when a Stripe payment succeeds, append a row to Google Sheets and send a SendGrid email
- when a webhook arrives from GitHub, enrich it with OpenAI or Anthropic, then post a Slack alert
- poll a CRM every hour, generate a summary, and save the results into a managed database
Why This Pattern
The advantage over Zapier or n8n is flexibility. The AI builder can research each external service, generate the manifest, set up the right secrets and settings, and write the handler code in one pass. You are not limited to prebuilt nodes or a fixed workflow DSL.
Each workflow is still just a normal PIE automation:
manifest.automation.triggersdefines the webhook, cron, interval, orplugin_evententrypointmanifest.workflowPreviewdefines a visual tree for the installed settings screenhandler()exposes utility actions likestatusandtestonWebhook()oronPluginEvent()handles the live triggercontext.fetch(),context.oauthConnection('key'),context.callTool(),context.listTools(),context.files, andcontext.pdfare available inside the workflow
Settings vs Secrets
Use userFields for non-sensitive values that belong in the settings UI:
- destination email address
- Google Sheet ID
- Slack channel
- amount thresholds
- feature toggles
Use userSecrets for anything that must stay encrypted:
- API keys
- webhook signing secrets
- service account JSON
- long-lived access tokens
Use developerSecrets for credentials the plugin author manages, such as OAuth client IDs and client secrets.
Multi-Service OAuth
If one workflow needs several OAuth-backed services, define them with oauthConnections:
manifest:
oauthConnections:
googleSheets:
provider: google
scopes:
- https://www.googleapis.com/auth/spreadsheets
clientIdSecret: GOOGLE_CLIENT_ID
clientSecretSecret: GOOGLE_CLIENT_SECRET
slackAlerts:
provider: slack
scopes:
- chat:write
clientIdSecret: SLACK_CLIENT_ID
clientSecretSecret: SLACK_CLIENT_SECRETIn code, call the right connection explicitly:
const sheets = context.oauthConnection('googleSheets');
const slack = context.oauthConnection('slackAlerts');Cross-Plugin Events
Workflows can also subscribe to events published by other installed PIE plugins. This is useful when you want to reuse an existing capability like forms, CRM sync, or inbox tooling instead of rebuilding it inside the workflow.
manifest:
automation:
triggers:
- type: plugin_event
sourcePlugin: pie-forms
sourceEventId: form_submitted
onPluginEvent: trueasync function onPluginEvent(input, context) {
const submission = input.triggerData && input.triggerData.payload;
return { success: true, submission };
}sourcePluginis the plugin's manifestnamesourceEventIdshould match one of that plugin's publishedheartbeatEvents.events[].id- if you omit
sourceEventId, the workflow receives any published event from that plugin
Calling Other Plugin Tools
Once another plugin is installed and enabled for the same user, a workflow can call its tool actions directly:
const result = await context.callTool('gmail_assistant', 'send', {
to: context.userConfig.notificationEmail,
subject: context.userConfig.emailSubject,
body: 'A new form submission arrived.',
});Use context.listTools() when you need to inspect what is available at runtime:
const tools = await context.listTools();
const gmailInstalled = tools.some((entry) => entry.tool.name === 'gmail_assistant');toolNamemust match the target plugin'smanifest.tool.nameactionshould match the target tool's supported action name- only plugins installed and enabled for the same user are callable
Code Conventions
Keep workflow handlers predictable so the AI builder and humans can both follow them:
- separate each pipeline step into its own try/catch block
- store outcomes in a shared
resultsobject - emit
context.notify()when one or more steps fail - return structured step results instead of only throwing
- provide a
statusaction that verifies secrets, settings, and OAuth connections - provide a
testaction that runs the workflow with mock data - if the workflow subscribes to another PIE plugin, export
onPluginEventand usecontext.listTools()instatusto verify dependencies
Settings Flow Preview
If you want the installed plugin settings to show a Zapier-like flow, add workflowPreview metadata to the manifest. This does not execute anything. It only tells PIE how to draw the visual workflow in the settings modal.
manifest:
workflowPreview:
title: Stripe -> Sheets -> Email
description: Visual flow shown in plugin settings
nodes:
- id: stripe-trigger
kind: trigger
label: Stripe payment_intent.succeeded
service: Stripe
userSecrets: [STRIPE_WEBHOOK_SECRET]
- id: append-sheet
kind: action
label: Append row to Google Sheets
service: Google Sheets
dependsOn: [stripe-trigger]
oauthConnection: googleSheets
userFields: [sheetId]
- id: send-email
kind: action
label: Send notification email
service: SendGrid
dependsOn: [append-sheet]
userSecrets: [SENDGRID_API_KEY]
userFields: [notificationEmail, fromEmail]Use it for:
- showing the trigger and downstream steps in order
- marking which step depends on which setting, secret, or OAuth connection
- giving users a quick mental model of what the workflow does before they finish setup
Example Shape
async function onWebhook(input, context) {
const payload = input.triggerData;
const results = {};
// Step 1: validate and normalize the trigger payload
try {
// ...
results.normalize = { success: true };
} catch (error) {
results.normalize = { success: false, error: error.message };
}
// Step 2: write to an external API
try {
// ...
results.destination = { success: true };
} catch (error) {
results.destination = { success: false, error: error.message };
}
const failed = Object.entries(results).filter(([, value]) => !value.success);
if (failed.length > 0) {
await context.notify(`Workflow partial failure: ${failed.map(([name]) => name).join(', ')}`, {
title: 'Workflow Alert',
urgent: true,
});
}
return { success: failed.length === 0, results };
}
async function handler(input, context) {
if (input.params.action === 'status') {
return { ok: true };
}
if (input.params.action === 'test') {
return onWebhook({ triggerData: { mock: true } }, context);
}
return { success: false, error: 'Unknown action' };
}
module.exports = { handler, onWebhook };Files and PDFs in Workflows
Workflow automations can also use:
context.files.upload(),list(),get(),delete()context.pdf.extractText(url, options)
This is useful for flows like:
- download an invoice PDF from a third-party API
- extract text from the PDF
- store the original file in PIE
- push parsed data into another service
Reference Example
See plugins/workflow-stripe-sheets-email.pie for a complete workflow example that combines:
- Stripe webhook trigger
- Google Sheets append step
- SendGrid email notification
- named OAuth via
context.oauthConnection('googleSheets') statusandtestactions for setup and dry runs
See plugins/workflow-forms-to-gmail.pie for a cross-plugin workflow example that combines:
plugin_eventsubscription topie-formscontext.ai.summarize()for a human-readable form summarycontext.callTool('gmail_assistant', 'send', ...)to reuse another installed plugincontext.listTools()for dependency checks instatus