Skip to main content

Webhook Chaining

Send webhook payloads to multiple destinations in sequence or parallel. This feature allows you to create complex workflows where a single webhook triggers multiple actions.

Overview​

Webhook chaining enables you to send a single webhook payload to multiple destinations, either sequentially (one after another) or in parallel (all at once). This is useful for scenarios like:

  • Data Pipeline: Save to database, then publish to message queue
  • Multi-Destination: Archive to S3 while also processing via Redis
  • Workflow Orchestration: Transform data, validate, then forward to multiple services

Quick Start​

Simple Array Format​

The simplest way to create a chain is using an array of module names:

{
"chained_webhook": {
"data_type": "json",
"chain": ["s3", "redis_rq"],
"chain-config": {
"execution": "sequential",
"continue_on_error": true
},
"authorization": "Bearer token"
}
}

This will execute s3 first, then redis_rq, using default configurations for each module.

Detailed Format with Per-Module Config​

For more control, use detailed module configurations:

{
"chained_webhook": {
"data_type": "json",
"chain": [
{
"module": "s3",
"connection": "s3_storage",
"module-config": {
"bucket": "webhook-archive",
"prefix": "events"
},
"retry": {
"enabled": true,
"max_attempts": 3
}
},
{
"module": "redis_rq",
"connection": "redis_local",
"module-config": {
"queue_name": "process_events"
}
}
],
"chain-config": {
"execution": "sequential",
"continue_on_error": true
},
"authorization": "Bearer secret"
}
}

Configuration Reference​

Chain Items​

Each item in the chain array can be:

  1. String (simple format): Just the module name

    "chain": ["s3", "redis_rq"]
  2. Object (detailed format): Full configuration per module

    "chain": [
    {
    "module": "s3",
    "connection": "s3_storage",
    "module-config": { ... },
    "retry": { ... }
    }
    ]

Chain Item Fields​

  • module (required): Module name (e.g., "s3", "redis_rq", "postgresql")
  • connection (optional): Connection name from connections.json
  • module-config (optional): Module-specific configuration (same as top-level module-config)
  • retry (optional): Per-module retry configuration
    "retry": {
    "enabled": true,
    "max_attempts": 3,
    "initial_delay": 1.0,
    "max_delay": 60.0,
    "backoff_multiplier": 2.0
    }

Chain Configuration​

The chain-config object controls how the chain executes:

  • execution (optional, default: "sequential"):

    • "sequential": Execute modules one after another
    • "parallel": Execute all modules simultaneously
  • continue_on_error (optional, default: true):

    • true: Continue executing remaining modules even if one fails
    • false: Stop chain execution on first error (sequential mode only)

Execution Modes​

Sequential Execution​

Modules execute one after another in order. The next module starts only after the previous one completes (or fails if continue_on_error is false).

Use cases:

  • Dependent operations (save to DB, then publish to Kafka)
  • Data transformation pipeline
  • Ordered processing requirements

Performance: Total latency = sum of all module latencies

{
"chain-config": {
"execution": "sequential",
"continue_on_error": true
}
}

Parallel Execution​

All modules execute simultaneously using asyncio.gather. All modules start at the same time and execute independently.

Use cases:

  • Independent operations (save to S3 and Redis simultaneously)
  • Fan-out to multiple destinations
  • Performance optimization when modules don't depend on each other

Performance: Total latency = slowest module latency

{
"chain-config": {
"execution": "parallel",
"continue_on_error": true
}
}

Note: continue_on_error always applies in parallel mode - all modules will attempt execution regardless of failures.

Examples​

Example 1: Archive then Process (Sequential)​

Save webhook to S3 for archival, then queue for processing:

{
"archive_and_process": {
"data_type": "json",
"chain": [
{
"module": "s3",
"connection": "s3_storage",
"module-config": {
"bucket": "webhook-archive",
"prefix": "events"
}
},
{
"module": "redis_rq",
"connection": "redis_local",
"module-config": {
"queue_name": "process_events"
}
}
],
"chain-config": {
"execution": "sequential",
"continue_on_error": true
},
"authorization": "Bearer secret"
}
}

Example 2: Multi-Destination Fan-Out (Parallel)​

Save to database and publish to RabbitMQ simultaneously:

{
"fanout_webhook": {
"data_type": "json",
"chain": [
{
"module": "postgresql",
"connection": "postgres_local",
"module-config": {
"table": "webhook_events"
}
},
{
"module": "rabbitmq",
"connection": "rabbitmq_local",
"module-config": {
"queue_name": "event_queue"
}
},
{
"module": "log"
}
],
"chain-config": {
"execution": "parallel",
"continue_on_error": true
},
"authorization": "Bearer secret"
}
}

Example 3: Chain with Retries​

Each module can have its own retry configuration:

{
"reliable_chain": {
"data_type": "json",
"chain": [
{
"module": "s3",
"connection": "s3_storage",
"module-config": {
"bucket": "webhooks"
},
"retry": {
"enabled": true,
"max_attempts": 5,
"initial_delay": 1.0,
"max_delay": 30.0
}
},
{
"module": "http_webhook",
"module-config": {
"url": "https://api.example.com/webhooks",
"method": "POST"
},
"retry": {
"enabled": true,
"max_attempts": 3
}
}
],
"chain-config": {
"execution": "sequential",
"continue_on_error": false
},
"authorization": "Bearer secret"
}
}

Example 4: Simple Array Format​

For basic use cases, use the simple array format:

{
"simple_chain": {
"data_type": "json",
"chain": ["log", "save_to_disk"],
"chain-config": {
"execution": "sequential"
},
"authorization": "Bearer token"
}
}

Error Handling​

Continue on Error (Default: true)​

When continue_on_error is true:

  • Failed modules are logged but don't stop the chain
  • Remaining modules continue execution
  • Useful for best-effort delivery to multiple destinations

When continue_on_error is false (sequential only):

  • Chain stops at first failure
  • Remaining modules are marked as not executed
  • Useful when later modules depend on earlier ones succeeding

Error Logging​

Chain execution results are logged with details:

  • Success/failure status for each module
  • Error messages for failed modules
  • Summary statistics (total, successful, failed)

Example log output:

Chain execution for webhook 'my_webhook': 2/3 modules succeeded, 1 failed
- Module 's3' succeeded
- Module 'redis_rq' failed: Connection timeout
- Module 'log' succeeded

Security Features​

Chain Length Limits​

  • Maximum chain length: 20 modules (prevents DoS attacks)
  • Minimum chain length: 1 module
  • Validation occurs before chain execution

Input Validation​

  • Module name validation (must exist in ModuleRegistry)
  • Type validation for all configuration fields
  • Rejection of unknown fields to prevent injection
  • Configuration structure validation

Resource Management​

  • Concurrency limits via TaskManager (default: 100 concurrent tasks)
  • Proper resource cleanup (module teardown) after execution
  • Memory-safe deep copying with fallback to shallow copy
  • Error sanitization to prevent information disclosure

Performance Considerations​

Sequential Chains​

  • Latency: Sum of all module latencies
  • Example: S3 (200ms) + Redis (50ms) = 250ms total
  • Use when: Modules depend on each other or order matters

Parallel Chains​

  • Latency: Slowest module latency
  • Example: S3 (200ms) + Redis (50ms) = 200ms total
  • Use when: Modules are independent
  • Resource usage: Each module creates a task (limited by TaskManager)

Best Practices​

  • Use sequential for dependent operations
  • Use parallel for independent operations
  • Monitor task manager metrics with parallel chains
  • Consider payload size (large payloads × many modules = high memory usage)
  • Recommended chain length: 5-10 modules for sequential, limited by task manager for parallel

Backward Compatibility​

The module field still works for single destinations. If both module and chain are present, chain takes precedence.

{
"legacy_webhook": {
"module": "log" // Still works!
},
"new_webhook": {
"chain": ["log", "s3"] // Chain takes precedence
}
}