HomeProduct DocsAPI ReferenceChangelog
RecurlyAPI GuidesRecurly.jsWebhooksAPI ReferenceSupportBook demo
Product Docs

Overview: Webhooks

Webhooks let you notify your internal systems and partner applications whenever something important happens in Recurly. Configure up to ten endpoints per site, subscribe each one to only the events it needs, and use each notification as a trigger to confirm current state via the API — not as a source of truth on its own.
Available on all Recurly plans

Definition

A Recurly webhook is an HTTP POST notification sent to your endpoint whenever a qualifying event occurs — for example, when a new account is created or a payment fails. Webhooks are alerts: use them to discover that something may have changed, then confirm the authoritative state with the Recurly API before updating your own systems.

Key benefits

Real-time event alerts Get notified the moment something changes in Recurly — new accounts, subscription updates, payment events, and more — without polling the API.
Per-endpoint event filtering Subscribe each of your up to ten endpoints to only the specific lifecycle events it needs, so every system receives exactly the signals it cares about.
Signature verification Every JSON webhook includes a recurly-signature header so you can cryptographically confirm the notification came from Recurly and hasn't been tampered with.
Automatic retries Failed deliveries are retried up to ten times using exponential backoff, so transient endpoint issues don't mean lost notifications.

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:

1

Call the Recurly API

Confirm the current status of the resource — don't rely on the webhook payload alone.

2

Compare against your local record

Check whether the API response reflects a change from what your database already has.

3

Update only if there's a real change

Write to your database only when the API confirms something has actually changed.

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.

Note For example: an account closes and Recurly sends a notification. If delivery fails, the notification is retried later. In the meantime, the account could reopen — triggering another notification. If your endpoint comes back online, it may receive the closed-account notification after the account was reopened. Always verify current state with the API before acting on a closed-account event.

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.

Warning If you delete an endpoint, any notifications sent to it within the past 15 days will no longer appear 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_notification

Webhook states

Every webhook notification is assigned a state reflecting its delivery progress. You can filter and view notifications by state in the Recurly console.

StateDescription
PendingQueued and awaiting delivery.
DeliveredSuccessfully sent and acknowledged with a 2XX response.
RetryingDelivery failed; another attempt is scheduled.
FailedAll retry attempts exhausted. Will not be retried automatically.
PausedStored 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.

Note Signature verification only applies to JSON payloads — XML payloads are not signed.

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
end
err := 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.

1

Extract the timestamp and signature(s)

Split the header value on commas. The first element is the timestamp; the remaining elements are signatures. Any other format is invalid.

2

Prepare the message

Concatenate the timestamp, a literal . character, and the exact raw request body.

3

Compute the expected signature

Compute the HMAC-SHA256 digest of the prepared message using your endpoint's secret key.

4

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.