LoopFour

Webhook Triggers

Trigger workflows from external webhook events

Webhook Triggers

Webhook triggers start workflows when external systems send events to your webhook endpoints. The Workflows API supports webhooks from multiple providers with automatic signature verification.

Configuration

{
  "trigger": {
    "type": "webhook",
    "provider": "stripe",
    "events": ["invoice.paid", "invoice.created"],
    "verifySignature": true
  }
}
FieldTypeRequiredDescription
typestringYesMust be "webhook"
providerstringNoProvider name for signature verification
eventsstring[]NoFilter to specific event types
pathstringNoCustom path for the webhook URL
verifySignaturebooleanNoEnable/disable signature verification

Supported Providers

ProviderWebhook URLSignature Header
Stripe/webhooks/stripe/{companyId}stripe-signature
Salesforce/webhooks/salesforce/{companyId}N/A (SOAP XML)
HubSpot/webhooks/hubspot/{companyId}x-hubspot-signature-v3
QuickBooks/webhooks/quickbooks/{companyId}intuit-signature
PandaDoc/webhooks/pandadoc/{companyId}x-pandadoc-signature
NetSuite/webhooks/netsuite/{companyId}x-netsuite-signature
Gmail/webhooks/gmail/{companyId}N/A (Pub/Sub)
Custom/webhooks/custom/{companyId}/{path}N/A

Provider Examples

Stripe

{
  "name": "Payment Received Notification",
  "trigger": {
    "type": "webhook",
    "provider": "stripe",
    "events": ["invoice.paid", "payment_intent.succeeded"]
  },
  "steps": [...]
}

Stripe Webhook URL:

https://your-domain.com/webhooks/stripe/{COMPANY_ID}

Configure in Stripe Dashboard:

  1. Go to Developers → Webhooks
  2. Add endpoint with your webhook URL
  3. Select events: invoice.paid, payment_intent.succeeded
  4. Copy the signing secret to your environment

Common Stripe Events:

  • invoice.paid - Invoice was paid
  • invoice.payment_failed - Payment attempt failed
  • payment_intent.succeeded - Payment completed
  • customer.created - New customer created
  • subscription.created - New subscription started
  • subscription.canceled - Subscription was canceled

Salesforce

Salesforce supports three webhook types:

{
  "trigger": {
    "type": "salesforce_event",
    "eventKind": "platform_event",
    "platformEventName": "Invoice_Created__e"
  }
}

Salesforce CDC Change Types:

  • create - Record created
  • update - Record updated
  • delete - Record deleted
  • undelete - Record restored

HubSpot

{
  "trigger": {
    "type": "hubspot_event",
    "eventKind": "deal.propertyChange",
    "propertyName": "dealstage"
  }
}

HubSpot Event Kinds:

  • contact.propertyChange - Contact property changed
  • company.propertyChange - Company property changed
  • deal.propertyChange - Deal property changed
  • deal.creation - Deal created
  • deal.deletion - Deal deleted
  • * - Match all events

QuickBooks

{
  "trigger": {
    "type": "webhook",
    "provider": "quickbooks",
    "events": ["Invoice", "Payment"]
  }
}

QuickBooks Event Types:

  • Invoice - Invoice events
  • Payment - Payment events
  • Customer - Customer events
  • Estimate - Estimate events
  • Bill - Bill events

PandaDoc

{
  "trigger": {
    "type": "webhook",
    "provider": "pandadoc",
    "events": ["document_state_changed", "document_completed"]
  }
}

PandaDoc Events:

  • document_state_changed - Document status changed
  • recipient_completed - Recipient signed
  • document_completed - All signatures collected
  • document_paid - Payment received

NetSuite

{
  "trigger": {
    "type": "webhook",
    "provider": "netsuite",
    "events": ["record.create", "record.update"]
  }
}

NetSuite Events:

  • record.create - Record created
  • record.update - Record updated
  • record.delete - Record deleted
  • scheduled.script - Scheduled script execution

Custom Webhooks

For providers not listed above, use custom webhooks:

{
  "trigger": {
    "type": "webhook",
    "path": "my-integration"
  }
}

Custom Webhook URL:

https://your-domain.com/webhooks/custom/{COMPANY_ID}/my-integration

Event Filtering

Only trigger on specific events by listing them in the events array:

{
  "trigger": {
    "type": "webhook",
    "provider": "stripe",
    "events": ["invoice.paid"]
  }
}

Without events, the workflow triggers on all events from that provider.

Signature Verification

Webhooks are verified using provider-specific signatures to ensure authenticity.

ProviderHeaderAlgorithm
Stripestripe-signatureHMAC-SHA256 with timestamp
HubSpotx-hubspot-signature-v3HMAC-SHA256
QuickBooksintuit-signatureHMAC-SHA256
PandaDocx-pandadoc-signatureHMAC-SHA256
NetSuitex-netsuite-signatureHMAC-SHA256

Store webhook signing secrets securely in environment variables. Never commit them to version control.

Handling Verification Failures

When signature verification fails, the webhook returns 401 Unauthorized:

{
  "error": "Invalid signature"
}

Check that:

  1. Your signing secret matches the provider's configuration
  2. The request hasn't been tampered with
  3. The timestamp is within acceptable bounds (for Stripe)

Webhook Response

All webhooks return a consistent response format:

{
  "received": true,
  "eventId": "evt_xxx",
  "workflowsTriggered": 2
}
FieldDescription
receivedAlways true for successful receipt
eventIdUnique identifier for this webhook event
workflowsTriggeredNumber of workflows triggered by this event

Input Data

The full webhook payload is available in your workflow as {{input}}:

Stripe Example

{
  "input": {
    "id": "evt_1234",
    "type": "invoice.paid",
    "data": {
      "object": {
        "id": "in_xxx",
        "amount_paid": 9900,
        "customer": "cus_xxx",
        "customer_email": "customer@example.com"
      }
    }
  }
}

Access nested data in your steps:

{
  "config": {
    "to": "{{input.data.object.customer_email}}",
    "amount": "{{input.data.object.amount_paid / 100}}"
  }
}

Salesforce CDC Example

{
  "input": {
    "ChangeEventHeader": {
      "entityName": "Opportunity",
      "changeType": "UPDATE",
      "changedFields": ["Amount", "StageName"]
    },
    "Id": "006xxx",
    "Name": "Big Deal",
    "Amount": 50000,
    "StageName": "Closed Won"
  }
}

Webhook Events Table

All incoming webhooks are stored in the webhook_events table for audit and replay:

FieldDescription
idUnique event ID
company_idCompany that received this event
providerProvider name (stripe, salesforce, etc.)
event_typeEvent type (invoice.paid, etc.)
event_idProvider's event ID
payloadFull event payload (JSON)
signature_verifiedVerification status
statusProcessing status
workflows_triggeredArray of triggered workflow runs

Testing Webhooks

Using cURL

# Test Stripe webhook
curl -X POST http://localhost:3000/webhooks/stripe/{COMPANY_ID} \
  -H "Content-Type: application/json" \
  -H "stripe-signature: test" \
  -d '{
    "type": "invoice.paid",
    "data": {
      "object": {
        "id": "in_test",
        "amount_paid": 9900,
        "customer_email": "test@example.com"
      }
    }
  }'

Using Stripe CLI

# Forward Stripe webhooks to localhost
stripe listen --forward-to localhost:3000/webhooks/stripe/{COMPANY_ID}

Using ngrok

# Expose local server
ngrok http 3000
 
# Use the ngrok URL in provider webhook settings
# https://abc123.ngrok.io/webhooks/stripe/{COMPANY_ID}

Troubleshooting

Webhook Not Triggering Workflow

  1. Check workflow status - Must be active
  2. Verify event filter - Events must match workflow's events array
  3. Check signature - Ensure signing secrets are configured
  4. Review webhook events - Check the webhook_events table

Duplicate Events

Webhooks are deduplicated by eventId. If you receive duplicates:

  1. Ensure idempotent workflow design
  2. Check provider retry settings
  3. Verify webhook acknowledgement is reaching provider

Timeout Issues

Webhooks must respond within provider timeouts:

  • Stripe: 30 seconds
  • HubSpot: 5 seconds
  • QuickBooks: 30 seconds

The API acknowledges webhooks immediately and processes asynchronously.

Next Steps