Skip to main content
After your endpoint is live, Rain delivers each event as a signed POST request. This page explains how events move through their lifecycle, how to respond, the retry and delivery guarantees, how to handle out-of-order delivery, and how Rain versions payloads. To register an endpoint and verify signatures first, see Set up webhooks. At a high level, every event follows the same delivery path from Rain to your endpoint.
Signed request Rain POSTsDelivered (2xx)Rain builds the eventDecision / no-op
yesno
TriggerEvent occurs
CheckSubscribed to this event?
HMAC-SHA256Build & sign
Signed POSTSend to your endpoint
2xxDelivered
Not subscribedNo webhook sent
↻ Retry on failurenon-2xx or timeout: up to 15 retries, backoff capped at 1 day, give up after ~1 week

Webhook lifecycle

Most events follow a lifecycle as the underlying resource moves through its states. A spend transaction, for example, can move from requested to created, then updated, and finally completed. Not every event passes through every stage, and some events fire only once. Each webhook payload shares the same envelope:

Respond to webhooks

Acknowledge each webhook quickly and keep your response lightweight:
  • Return a JSON response with a 200 status code to acknowledge successful receipt
  • Process heavy work asynchronously after you acknowledge, so slow handlers don’t trigger timeouts and retries
  • Include relevant information in your response if you need to communicate back to Rain (for example, rejection codes for authorization webhooks)
  • Avoid returning HTML error pages or plain text responses. If you do, Rain truncates and stores them, like this:

Synchronous events

Three events are synchronous: Rain waits for your response before completing the action. Respond within the timeout shown below.
Respond to transaction.requested within the 1500 ms timeout. Slower responses can cause the authorization to fail, surfacing 404 or 402 errors. Keep this handler fast and move heavy processing to a background job.

Retry behavior and delivery guarantees

Rain automatically retries failed webhook deliveries. A delivery is considered failed if your endpoint does not return a 2xx status code, times out, or is unreachable. Because Rain retries, your endpoint may receive the same event more than once, and events may arrive out of order. Design your handler to be idempotent and to tolerate out-of-order delivery (see Handle event ordering).

How retries work

This schedule applies to asynchronous events only. When a delivery fails, Rain retries 15 times with exponential backoff for up to one week. Rain starts with a 2.5s delay for the first retry, and backs off with a coefficient of 3 (so the next retry is 7.5s, then 22.5s, and so on). Rain caps it at a day between retries. Synchronous events are never retried. If transaction.requested, challenge.requested, or raindrop_balance.requested times out, the authorization or challenge it belongs to fails. There is no second delivery to catch, so a missed authorization decision is final.

Idempotency

Webhooks may be delivered multiple times, so your handler should be idempotent. Each webhook payload includes a unique id field that you can use to detect duplicates. Store processed webhook IDs and check for duplicates before processing.

Handle event ordering

Rain webhooks may arrive at your endpoint out of chronological order due to network delays, retries, or processing variations. The eventReceivedAt field in webhook payloads indicates when the underlying event was received by Rain’s system, so you can determine the true chronological order of events even if webhooks arrive out of sequence.

Understand eventReceivedAt

eventReceivedAt is an optional ISO-8601 timestamp field (format: YYYY-MM-DDTHH:mm:ss.sssZ) that indicates when Rain first received the underlying event that triggered the webhook.

Best practices for ordering

Check for eventReceivedAt: The field is optional and may not be present in all webhooks. Always check for its presence before using it. Use UTC timezone: eventReceivedAt is always in UTC (indicated by the Z suffix). Ensure your parsing and comparison logic uses UTC and doesn’t convert to local time. Consider a buffer window: For more robust implementations, use a buffer window (for example, 30 to 60 seconds) before processing webhooks to account for late arrivals.

Availability

For transaction.requested, transaction.created, transaction.updated, transaction.completed, user.updated, and company.updated, eventReceivedAt is opt-in and excluded by default. Contact your Rain account manager to enable it for your tenant.
Some other events, such as raindrop_balance.requested, dispute.chargebackCreated, and transactionReward.created, show eventReceivedAt in their example payloads without this opt-in step. Confirm with your Rain account manager which of your subscribed events include the field by default versus which require enabling it.

Versioning

Each webhook payload is versioned. The version field in the envelope reflects the schema version of that event’s body. Rain adds fields in a backward-compatible way, so new optional fields can appear in a payload without a breaking version change. The version you receive is pinned by your webhook configuration. If you never set a version for an event, Rain sends that event’s default version, which is its earliest — not its latest. To receive fields added in later versions, set the version for that event with the webhook configuration endpoints. Requesting a version that doesn’t exist falls back to the default rather than failing. For the change history of each event, review the changelog entries for that event, which record what changed in each version. Check them before you rely on a recently added field, and treat any field documented as optional as one that may be absent from a given payload.

What’s next

Set up webhooks

Register your endpoint, choose a signing key, and verify signatures.

Transaction events

See the payloads, fields, and triggers for spend, collateral, payment, and transfer events.