0.0
The project is in a healthy, maintained state
A Rails library for triggering webhooks. Inspired by ActionMailer from Rails
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 0.9

Runtime

>= 6.0, < 8.0
~> 1.0
~> 0.18.1
 Project Readme

ActionWebhook ๐Ÿช

Gem Version CI Release License: MIT

ActionWebhook is a Rails-friendly framework for delivering structured webhooks with the elegance and familiarity of ActionMailer. Built for modern Rails applications, it provides a clean, testable, and reliable way to send webhooks to external services.

โœจ Features

  • ๐ŸŽฏ ActionMailer-inspired API - Familiar patterns for Rails developers
  • ๐Ÿ“„ ERB Template Support - Dynamic JSON payloads with embedded Ruby
  • ๐Ÿ”„ Smart Retry Logic - Only retries failed URLs, not successful ones
  • โšก ActiveJob Integration - Queue webhooks using your existing job infrastructure
  • ๐ŸŽฃ Flexible Callbacks - Hook into delivery lifecycle events
  • ๐Ÿ›ก๏ธ Error Handling - Comprehensive error handling and logging
  • ๐Ÿงช Test-Friendly - Built-in testing utilities and helpers
  • ๐Ÿ“Š Multiple Endpoints - Send to multiple URLs simultaneously with selective retry
  • ๐Ÿ”ง Highly Configurable - Fine-tune behavior per webhook class
  • ๐Ÿ“ Comprehensive Logging - Detailed logging for debugging and monitoring

๐Ÿ“ฆ Installation

Add this line to your application's Gemfile:

gem 'action_webhook'

And then execute:

$ bundle install

๐Ÿš€ Quick Start

1. Generate a Webhook Class

# app/webhooks/user_webhook.rb
class UserWebhook < ActionWebhook::Base
  def user_created
    @user = params[:user]
    @timestamp = Time.current

    endpoints = [
      {
        url: 'https://api.example.com/webhooks',
        headers: {
          'Authorization' => 'Bearer your-token',
          'Content-Type' => 'application/json'
        }
      }
    ]

    deliver(endpoints)
  end
end

2. Create a Payload Template

<!-- app/webhooks/user_webhook/user_created.json.erb -->
{
  "event": "user.created",
  "timestamp": "<%= @timestamp.iso8601 %>",
  "data": {
    "user": {
      "id": <%= @user.id %>,
      "email": "<%= @user.email %>",
      "name": "<%= @user.name %>",
      "created_at": "<%= @user.created_at.iso8601 %>"
    }
  }
}

3. Trigger the Webhook

# Immediate delivery
UserWebhook.user_created(user: @user).deliver_now

# Background delivery (recommended)
UserWebhook.user_created(user: @user).deliver_later

# With custom queue
UserWebhook.user_created(user: @user).deliver_later(queue: 'webhooks')

๐Ÿ”ง Advanced Configuration

Retry Configuration

ActionWebhook intelligently retries only the URLs that fail, not all URLs in a batch:

class PaymentWebhook < ActionWebhook::Base
  # Configure retry behavior
  self.max_retries = 5
  self.retry_delay = 30.seconds
  self.retry_backoff = :exponential  # :exponential, :linear, or :fixed
  self.retry_jitter = 5.seconds      # Adds randomness to prevent thundering herd

  def payment_completed
    @payment = params[:payment]

    endpoints = [
      { url: 'https://accounting.example.com/webhooks' },
      { url: 'https://analytics.example.com/webhooks' },
      { url: 'https://notifications.example.com/webhooks' }
    ]

    deliver(endpoints)
    # If only analytics.example.com fails, only that URL will be retried
  end
end

Smart Callbacks

Get notified about successful deliveries immediately and permanent failures after retries are exhausted:

class OrderWebhook < ActionWebhook::Base
  # Called immediately when any URLs succeed
  after_deliver :handle_successful_deliveries

  # Called when retries are exhausted for failed URLs
  after_retries_exhausted :handle_permanent_failures

  def order_created
    @order = params[:order]
    deliver(webhook_endpoints)
  end

  private

  def handle_successful_deliveries(successful_responses)
    successful_responses.each do |response|
      Rails.logger.info "Webhook delivered to #{response[:url]} (#{response[:status]})"
    end
  end

  def handle_permanent_failures(failed_responses)
    failed_responses.each do |response|
      Rails.logger.error "Webhook permanently failed for #{response[:url]} after #{response[:attempt]} attempts"
      AdminMailer.webhook_failure(@order.id, response).deliver_later
    end
  end
end

Multiple Endpoints with Selective Retry

Send to multiple endpoints efficiently with intelligent retry logic:

class NotificationWebhook < ActionWebhook::Base
  def user_registered
    @user = params[:user]

    endpoints = [
      {
        url: 'https://analytics.example.com/events',
        headers: { 'X-API-Key' => Rails.application.credentials.analytics_key }
      },
      {
        url: 'https://crm.example.com/webhooks',
        headers: { 'Authorization' => "Bearer #{Rails.application.credentials.crm_token}" }
      },
      {
        url: 'https://marketing.example.com/api/events',
        headers: { 'X-Service-Token' => Rails.application.credentials.marketing_token }
      }
    ]

    deliver(endpoints)
    # If marketing.example.com fails but others succeed,
    # only marketing.example.com will be retried
  end
end

Real-world Example: E-commerce Order Processing

class OrderWebhook < ActionWebhook::Base
  self.max_retries = 3
  self.retry_delay = 30.seconds
  self.retry_backoff = :exponential

  after_deliver :log_successful_integrations
  after_retries_exhausted :handle_integration_failures

  def order_placed
    @order = params[:order]

    endpoints = [
      { url: 'https://inventory.company.com/webhooks' },      # Update inventory
      { url: 'https://shipping.company.com/api/orders' },     # Create shipping label
      { url: 'https://analytics.company.com/events' },       # Track conversion
      { url: 'https://email.company.com/order-confirmation' }, # Send confirmation
      { url: 'https://accounting.company.com/webhooks' }     # Update books
    ]

    deliver(endpoints)
  end

  private

  def log_successful_integrations(responses)
    responses.each do |response|
      OrderIntegration.create!(
        order: @order,
        service_url: response[:url],
        status: 'success',
        http_status: response[:status],
        attempt: response[:attempt]
      )
    end
  end

  def handle_integration_failures(responses)
    responses.each do |response|
      OrderIntegration.create!(
        order: @order,
        service_url: response[:url],
        status: 'failed',
        error_message: response[:error],
        final_attempt: response[:attempt]
      )

      # Alert based on criticality
      case response[:url]
      when /inventory|accounting/
        CriticalAlert.webhook_failure(@order, response)
      else
        StandardAlert.webhook_failure(@order, response)
      end
    end
  end
end

๐Ÿงช Testing

ActionWebhook provides testing utilities to make webhook testing straightforward:

# In your test files
require 'action_webhook/test_helper'

class UserWebhookTest < ActiveSupport::TestCase
  include ActionWebhook::TestHelper

  test "user_created webhook sends correct payload" do
    user = users(:john)

    # Test immediate delivery
    webhook = UserWebhook.user_created(user: user)

    assert_enqueued_webhook_deliveries 1 do
      webhook.deliver_later
    end

    # Test payload content
    perform_enqueued_webhook_deliveries do
      webhook.deliver_later
    end

    # Verify webhook was sent
    assert_webhook_delivered UserWebhook, :user_created
  end
end

๐Ÿ“š Documentation

Comprehensive documentation is available in the docs directory:

๐Ÿค Contributing

We love contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

๐Ÿ“‹ Requirements

  • Ruby >= 3.1.0
  • ActiveJob >= 6.0 (included in Rails 6.0+)

๐Ÿ“„ License

This gem is available as open source under the terms of the MIT License.

๐Ÿ™‹โ€โ™‚๏ธ Support

๐ŸŽฏ Roadmap

  • GraphQL webhook support
  • Webhook signature verification
  • Built-in webhook endpoint discovery
  • Metrics and monitoring integration
  • Advanced filtering and conditional delivery

๐Ÿ“ Documentation Notes

This documentation was generated with assistance from GitHub Copilot to ensure comprehensive coverage and clarity. If you find any errors, inconsistencies, or areas that need improvement, please open an issue so we can make corrections and keep the documentation accurate and helpful for everyone.


ActionWebhook - Making webhook delivery as elegant as sending emails in Rails. ๐Ÿš€