Skip to content

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.triggers defines the webhook, cron, interval, or plugin_event entrypoint
  • manifest.workflowPreview defines a visual tree for the installed settings screen
  • handler() exposes utility actions like status and test
  • onWebhook() or onPluginEvent() handles the live trigger
  • context.fetch(), context.oauthConnection('key'), context.callTool(), context.listTools(), context.files, and context.pdf are 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:

yaml
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_SECRET

In code, call the right connection explicitly:

javascript
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.

yaml
manifest:
  automation:
    triggers:
      - type: plugin_event
        sourcePlugin: pie-forms
        sourceEventId: form_submitted
    onPluginEvent: true
javascript
async function onPluginEvent(input, context) {
  const submission = input.triggerData && input.triggerData.payload;
  return { success: true, submission };
}
  • sourcePlugin is the plugin's manifest name
  • sourceEventId should match one of that plugin's published heartbeatEvents.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:

javascript
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:

javascript
const tools = await context.listTools();
const gmailInstalled = tools.some((entry) => entry.tool.name === 'gmail_assistant');
  • toolName must match the target plugin's manifest.tool.name
  • action should 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 results object
  • emit context.notify() when one or more steps fail
  • return structured step results instead of only throwing
  • provide a status action that verifies secrets, settings, and OAuth connections
  • provide a test action that runs the workflow with mock data
  • if the workflow subscribes to another PIE plugin, export onPluginEvent and use context.listTools() in status to 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.

yaml
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

javascript
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')
  • status and test actions for setup and dry runs

See plugins/workflow-forms-to-gmail.pie for a cross-plugin workflow example that combines:

  • plugin_event subscription to pie-forms
  • context.ai.summarize() for a human-readable form summary
  • context.callTool('gmail_assistant', 'send', ...) to reuse another installed plugin
  • context.listTools() for dependency checks in status

Built with VitePress