philiprehberger-webhook_builder

Tests Gem Version Last updated

Webhook delivery client with HMAC signing, retry, and tracking

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-webhook_builder"

Or install directly:

gem install philiprehberger-webhook_builder

Usage

require "philiprehberger/webhook_builder"

client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "your-signing-secret"
)

delivery = client.deliver(event: "order.created", payload: { id: 123, total: 49.99 })
delivery.success?      # => true
delivery.response_code # => 200

Batch Delivery

require "philiprehberger/webhook_builder"

client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "your-signing-secret",
  concurrency: 8
)

events = [
  { event: "order.created", payload: { id: 1 } },
  { event: "order.updated", payload: { id: 2 } },
  { event: "order.deleted", payload: { id: 3 } }
]

results = client.deliver_batch(events)
results.each { |d| puts "#{d.response_code}: #{d.success?}" }

Backoff Strategies

require "philiprehberger/webhook_builder"

# Exponential backoff (default): 1s, 2s, 4s, 8s, ...
client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "secret",
  backoff: :exponential
)

# Linear backoff: 1s, 2s, 3s, 4s, ...
client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "secret",
  backoff: :linear
)

# Fixed backoff: 1s, 1s, 1s, ...
client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "secret",
  backoff: :fixed
)

# Custom Proc backoff
client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "secret",
  backoff: ->(attempt) { attempt * 0.5 }
)

Header Customization

require "philiprehberger/webhook_builder"

# Default headers on all deliveries
client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "secret",
  default_headers: { "X-Tenant" => "acme" }
)

# Per-delivery headers (override defaults)
client.deliver(
  event: "order.created",
  payload: { id: 1 },
  headers: { "X-Priority" => "high" }
)

Delivery Tracking

require "philiprehberger/webhook_builder"

client = Philiprehberger::WebhookBuilder.new(
  url: "https://example.com/webhooks",
  secret: "secret"
)

delivery = client.deliver(event: "user.updated", payload: { id: 42 })

delivery.success?       # => true/false
delivery.response_code  # => 200
delivery.attempts       # => 1
delivery.duration       # => 0.342 (seconds)
delivery.response_body  # => '{"ok":true}'
delivery.error          # => nil or error message

API

Client

Method Description
.new(url:, secret:, timeout:, max_retries:, backoff:, concurrency:, default_headers:) Create a webhook client
#deliver(event:, payload:, headers:) Deliver a webhook event and return a Delivery
#deliver_batch(events) Deliver multiple events concurrently and return an array of Delivery results

Delivery

Method Description
#success? Whether the delivery succeeded (2xx response)
#response_code The HTTP response code
#attempts Number of delivery attempts made
#duration Total duration in seconds across all attempts
#response_body The response body string
#error Error message if delivery failed

Backoff::Exponential

Method Description
.new(base:, max_delay:, jitter:) Create exponential strategy (defaults: base=1, max_delay=30, jitter=false)
#call(attempt) Calculate delay for given attempt

Backoff::Linear

Method Description
.new(base:, max_delay:) Create linear strategy (defaults: base=1, max_delay=30)
#call(attempt) Calculate delay for given attempt

Backoff::Fixed

Method Description
.new(delay:) Create fixed strategy (default: delay=1)
#call(attempt) Returns constant delay

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT