Overview: Webhooks
Definition
Key benefits
recurly-signature header so you can cryptographically confirm the notification came from Recurly and hasn't been tampered with.
Key details
Recurly can post notifications to any publicly reachable server. When a qualifying event occurs, Recurly sends a webhook to each endpoint you've configured — up to ten endpoints per site. Every endpoint receives the notifications for the lifecycle events it is subscribed to.
A notification counts as delivered only when Recurly gets a timely, successful response:
- The endpoint must listen on port 80 (HTTP) or 443 (HTTPS) — other ports aren't supported.
- The endpoint must reply within five seconds.
- The response must be an HTTP 2XX status (200, 201, 204, etc.). Recurly doesn't follow redirects, and non-2XX responses are treated as failures.
Sandbox vs. production
Recurly sends sandbox and production webhooks from different services, so high testing volume in sandbox mode doesn't affect production data.
- During periods of high sandbox testing volume, sandbox webhook delivery may be delayed by up to 48 hours.
- Recurly may auto-pause sandbox webhook endpoints that consistently return a high number of delivery errors in a short window of time.
Webhooks are notifications, not commands
Webhooks are not actionable on their own and should never be used for critical functions such as provisioning accounts. The API response from the originating action — signup, one-time purchase, and so on — should provision the account and store the resulting state in your own database. Treat that local state as correct unless a webhook indicates a change.
When a webhook arrives, use it as a trigger to:
Because Recurly may retry or resend a webhook, your endpoint must accept the same notification more than once and tolerate events arriving out of order.
Notification storage and timestamps
Each webhook notification is retained for 15 days and viewable in the Recurly console with full delivery status and error details. Notifications are delivered separately — a signup that triggers both a subscription event and a payment event produces two distinct notifications.
Notification timestamps are recorded in UTC but displayed in your site's configured time zone in the console.
Configuration and security
HTTP Basic Authentication
Webhooks support HTTP Basic Authentication to verify that incoming requests came from Recurly's servers. Configure credentials on the Webhook Endpoints page in the Recurly Admin Console.
IP allowlisting
See Recurly's IP allowlisting documentation for the current list of Recurly IP ranges. You may refuse other IP addresses at your endpoint or firewall.
Web application firewalls
If you run Apache with ModSecurity, you may need to disable rule #990011 so webhook requests aren't blocked. Recurly doesn't endorse any specific web server or plugin — this is a known compatibility issue, not a recommendation.
CSRF protection for Rails applications
Rails' built-in CSRF protection (protect_from_forgery) blocks incoming POSTs from external services like Recurly webhooks. To receive webhook notifications, exempt only the specific controller action that handles them — keep CSRF protection enabled for all other actions.
In your controller, disable forgery protection for the action that listens for Recurly notifications (assuming it's named recurly_notification):
protect_from_forgery :except => :recurly_notificationWebhook states
Every webhook notification is assigned a state reflecting its delivery progress. You can filter and view notifications by state in the Recurly console.
| State | Description |
| Pending | Queued and awaiting delivery. |
| Delivered | Successfully sent and acknowledged with a 2XX response. |
| Retrying | Delivery failed; another attempt is scheduled. |
| Failed | All retry attempts exhausted. Will not be retried automatically. |
| Paused | Stored without any delivery attempts. |
Retries
Automatic retries
When a webhook delivery fails, Recurly automatically retries using exponential backoff. Each notification is retried up to ten times; after the tenth failure, no further attempts are made. Retries occur in the same order notifications were created.
The interval before each retry follows:
10 + x * 2^(x + 5) seconds
Where x is the current retry attempt (zero-indexed). Early retries happen quickly; later retries space out substantially.
Manual retries
You can also trigger retries manually — one at a time or in bulk.
- Retry individual: Click the Retry button next to any notification to resend it immediately.
- Retry all: Use the Retry All control to resend every notification currently marked Failed or Paused.
Manually retried notifications still count toward the automatic retry limit and follow the same delivery rules.
Payload format
Each webhook endpoint is configured to receive either JSON or XML payloads — not both.
JSON is strongly recommended. JSON webhooks provide lightweight payloads containing the event type, object context, and object identifiers you can use to fetch current state via the Recurly API. This aligns with the best-practice approach of treating webhooks as triggers, not sources of truth.
XML payloads are available for integrations that require them, but JSON is Recurly's most modern offering and the format receiving ongoing investment.
Signature verification
For all JSON webhooks, Recurly includes a recurly-signature header. Verifying this signature confirms the notification came from Recurly and hasn't been altered. Each webhook endpoint has its own unique secret key, found on the Webhook Endpoints page.
Verify using a client library
Provide the recurly-signature header, your endpoint's secret key, and the raw request body:
begin
Recurly::Webhooks.verify_signature(header,
ENV['WEBHOOKS_KEY'],
request.body)
rescue Recurly::Errors::SignatureVerificationError => e
puts e.message
enderr := recurly.VerifyWebhookSignature(header, secret, body, recurly.DEFAULT_WEBHOOK_TOLERANCE)
if err != nil {
fmt.Errorf("Verify Webhook Signature failed with: %s", err.Error())
}Verify manually
The recurly-signature header contains a Unix timestamp (in milliseconds) followed by one or more hex signatures, separated by commas:
recurly-signature:1659641851,a8c8524a0cdd99e36b55d9fdf6c8aed2c2315dfa1a36c4961f65986ee6cf6ae9,cafe677a2e6fa177d1a73cd9a18e47533de4b8923bbe17e2e1be290717ca29c1
When you regenerate a secret key, both the new and previous keys remain valid for 24 hours, which is why the header may contain multiple signatures.
Compare signatures
Use a constant-time comparison to check the expected signature against each signature from the header. The notification is valid if exactly one matches. Also compare the timestamp against the current time — a large discrepancy could indicate a replay attack.
Lifecycle events
You can opt in to only the specific notifications you need, on an endpoint-by-endpoint basis. For example, an endpoint that only needs new-account notifications can be configured to ignore account updates entirely.
Each site supports up to ten webhook endpoints. Event subscription changes take effect immediately for all subsequent notifications.
A full list of all notifications — with both JSON and XML payloads for each — is available in the Account Notifications reference.
Updated 6 days ago