Skip to main content
For Partner-Managed programs, Rain orchestrates travel redemptions against your balance source and fires raindrop_redemption.completed once a booking is confirmed. What you build depends on where your points balances live: Onchain, where you are the source of truth and Rain calls you for balances and burns, or Offchain, where Rain keeps the canonical ledger and you mostly fulfill the booking. This page walks through the flow for both modes and the code recipes that go with each. For shared reference such as status values, API endpoints, and billing, see Travel Redemptions.
Partner-Managed programs skip the Rain-Managed user authorization step. A fresh balance check (webhook for onchain, DB for offchain) is the pre-commit gate before a booking, and there is no raindrop_redemption.created webhook for partner-managed travel redemptions.

Launch the travel portal

Both modes open the travel portal the same way Rain-Managed programs do: in a webview, with an encrypted session token (a JWE) that identifies the cardholder. The token recipe is identical across modes—see Launch the travel portal on the Rain-Managed page.
Rain signs every webhook below with an HMAC SHA256 signature in the Signature header, computed over the exact JSON body using your API key as the secret. Verify it before processing (see How Webhooks Work). Some tenants also receive a version field on redemption webhooks; treat it as optional and ignore any fields you don’t recognize.

Partner-Managed Onchain

Partner Managed (onchain) travel redemption flow

Partner Managed (onchain) travel redemption flow

You are the source of truth for points balances, so you implement three handlers: a synchronous balance check Rain calls before a booking, a completion webhook that tells you to burn the points, and a refund webhook that tells you to restore them. All point amounts below are whole points (1 point = $0.01), not native token units.

Balance check

Rain sends a synchronous raindrop_balance.requested webhook whenever the travel portal needs to display or verify a balance. Respond within 1500ms with the cardholder’s spendable balance—confirmed points minus any you’ve already reserved for in-flight redemptions, so the portal can’t double-spend. Request payload:
body.id is the cardholder’s Rain user ID. Your response:
availableBalance must be a non-negative integer string of whole points. If your endpoint times out, returns non-2xx, or returns a malformed response, Rain returns 503 to the travel portal.

Booking completion

When a booking is confirmed, Rain fires raindrop_redemption.completed. Acknowledge it immediately, then burn the points in your system out of band. raindropAmount is in whole points—scale it to your token’s native units before burning.

Refund

When a booking is canceled, Rain fires raindrop_redemption.refunded so you re-credit the points. The payload carries no amount, so look up how many points you debited by body.id (the redemption ID from the completion webhook).

Webhook listener

A single endpoint handles all three. The balance check responds synchronously; the completion and refund handlers acknowledge first and process out of band.
partnerOnchainWebhookListener.ts
Delivery is at-least-once, so dedupe on body.id (the redemption ID) before burning or restoring. Burns must be retried until they succeed: the booking is already confirmed, so a dropped burn strands the debit.

Test in sandbox

If your travel portal is not connected yet, stand in for it by calling the travel redemption endpoints yourself in sandbox. Use an Admin key or a custom key with raindrops-travel:read and raindrops-travel:write. Each call triggers the webhooks your listener handles, with the payloads shown in Balance check, Booking completion, and Refund.
  1. Check the balance. Call GET /issuing/raindrops/travel-redemptions/balance?userId=<id>. Rain sends raindrop_balance.requested to your endpoint synchronously and waits up to 1500ms. Rain returns your availableBalance as availablePoints.
  2. Create a booking. Call POST /issuing/raindrops/travel-redemptions with userId, amount in whole points, a bookingId that starts with sim_, bookingType, totalBookingAmountCents, and bookingDescription. Rain sends raindrop_balance.requested again as a pre-check. If the user has too few points, the call returns 400. If your endpoint does not answer, the call returns 503. Otherwise, Rain skips the travel provider confirmation, marks the redemption COMPLETED, returns 200, and sends raindrop_redemption.completed. Debit the user when the webhook arrives.
  3. Refund the booking. Call POST /issuing/raindrops/travel-redemptions/{redemptionId}/refund. Rain marks the redemption REFUNDED, returns 200, and sends raindrop_redemption.refunded. The payload has no amount, so re-credit the user by redemption ID.
Billing still applies in sandbox. Each completed sandbox booking creates a charge for totalBookingAmountCents, plus any redemption premium, on your next sandbox tenant report. A refund reverses the charge. Once the travel portal is connected, it makes the same calls with real booking IDs. Rain confirms each booking with the travel provider before completing it. Your webhook handlers do not change.

Partner-Managed Offchain

Partner Managed (offchain) travel redemption flow

Partner Managed (offchain) travel redemption flow

Rain keeps the canonical points ledger in its database, so it handles balances and refunds for you. There is no balance webhook (Rain reads its own DB) and no refund webhook (Rain restores the DB balance directly). The only travel webhook you receive is raindrop_redemption.completed, and it is informational—Rain has already debited the points by the time it fires. Use it to record the booking or trigger fulfillment in your own system.
partnerOffchainWebhookListener.ts
To test in sandbox, follow the Onchain walkthrough. Offchain programs get no balance or refund webhook, because Rain checks and restores the balance from its own ledger. You receive only raindrop_redemption.completed. To debit offchain points for a statement credit, use the statement-credit endpoint.

Webhook summary

For onchain partners, raindrop_redemption.completed signals that you should debit or burn points in your system. For offchain partners, Rain has already debited the DB, so the webhook is informational.

Refunds

After the travel portal receives notice from the supplier (for example, a hotel or airline) that a booking was canceled and must be refunded, it sends Rain a message. Rain then re-issues the burned points to the cardholder and marks the redemption REFUNDED. How the restored points settle depends on the mode: