Ruby library for Standard Webhooks
Installation
Add standardwebhooks to your Gemfile and run bundle install.
Or install it without bundler:
gem install standardwebhooks
Verifying webhooks
StandardWebhooks::Webhook#verify expects:
- The exact raw request body as a string.
- A hash containing
webhook-id,webhook-timestamp, andwebhook-signature.
It returns the parsed JSON payload as a Ruby hash and raises
StandardWebhooks::WebhookVerificationError when verification fails.
require 'standardwebhooks'
webhook = StandardWebhooks::Webhook.new(ENV.fetch('WEBHOOK_SECRET'))
payload = webhook.verify(raw_body, {
'webhook-id' => request_headers.fetch('webhook-id'),
'webhook-timestamp' => request_headers.fetch('webhook-timestamp'),
'webhook-signature' => request_headers.fetch('webhook-signature'),
})
puts payload['type']
Pass the raw body exactly as received. Verifying a parsed-and-reserialized JSON body can fail because the signature is computed over the original bytes.
Rails example
class WebhooksController < ApplicationController
def create
webhook = StandardWebhooks::Webhook.new(ENV.fetch('WEBHOOK_SECRET'))
payload = webhook.verify(request.raw_post, {
'webhook-id' => request.headers.fetch('webhook-id'),
'webhook-timestamp' => request.headers.fetch('webhook-timestamp'),
'webhook-signature' => request.headers.fetch('webhook-signature'),
})
process_event(payload)
head :ok
rescue StandardWebhooks::WebhookVerificationError => error
Rails.logger.warn("Invalid webhook: #{error.}")
head :bad_request
end
private
def process_event(payload)
case payload['type']
when 'customer.created'
CustomerCreateJob.perform_later(payload.fetch('data'))
end
end
end
Rack or Sinatra example
post '/webhooks' do
request.body.rewind
raw_body = request.body.read
webhook = StandardWebhooks::Webhook.new(ENV.fetch('WEBHOOK_SECRET'))
payload = webhook.verify(raw_body, {
'webhook-id' => request.env.fetch('HTTP_WEBHOOK_ID'),
'webhook-timestamp' => request.env.fetch('HTTP_WEBHOOK_TIMESTAMP'),
'webhook-signature' => request.env.fetch('HTTP_WEBHOOK_SIGNATURE'),
})
handle_event(payload)
status 204
rescue StandardWebhooks::WebhookVerificationError
halt 400
end
Signing payloads
If you are producing Standard Webhooks or need deterministic signatures in tests,
use #sign:
require 'standardwebhooks'
webhook = StandardWebhooks::Webhook.new(ENV.fetch('WEBHOOK_SECRET'))
msg_id = 'msg_2KWPBgLlAfxdpx2AI54pPJ85f4W'
= Time.now.to_i
payload = '{"type":"example.event","timestamp":"2022-11-03T20:26:10.344522Z","data":{"foo":"bar"}}'
signature = webhook.sign(msg_id, , payload)
headers = {
"webhook-id" => msg_id,
"webhook-timestamp" => .to_s,
"webhook-signature" => signature,
}
# …
Development
Building
bundler exec rake build
Running tests
bundle exec rspec spec