Schedule icon
Request icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon

Razorpay Settlement Delay Sentinel

Read recent Razorpay settlements every morning, change nothing, and alert Slack when money to the bank has failed, is delayed, or is on hold.

Categories
BusinessData

Razorpay collects a business's payments and settles them to its bank account, usually two working days later. When a settlement fails because the bank details are wrong, or stops because the account is on hold, nothing pages the business: the finance team finds out days later when the bank balance does not match. This blueprint turns a scheduled, read-only call to the Razorpay Settlements API into a cash-flow gate: io.kestra.plugin.core.http.Request reads recent settlements, one jq query counts processed, failed and stuck settlements and adds up the money that reached the bank, and the result routes to a quiet log or a Slack alert.

How it works

  1. list_settlements (io.kestra.plugin.core.http.Request) calls GET /v1/settlements with from and to covering the last lookback_days (Unix seconds) and count=100, using HTTP basic auth with the Razorpay key ID and key secret. It is a read: nothing is created or changed. Retries cover short network blips, and allowFailed plus allowFailure keep an API error from aborting the flow so it can be reported instead.
  2. summarize_settlements (io.kestra.plugin.core.debug.Return) first guards for a missing or non-200 response (UNREACHABLE). Otherwise one jq query counts settlements by status, marks any still created for longer than max_pending_hours as delayed, adds up the amount of processed settlements (converted from paise to rupees), keeps the UTR of the latest one, and classifies the window: any failed settlement is FAILED, any delayed one is DELAYED, no settlements at all is NONE when expect_settlements is true, otherwise HEALTHY.
  3. route_by_settlement_state (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY logs the settled amount and latest UTR, DELAYED and NONE post a Slack message, FAILED and UNREACHABLE page Slack. A defaults fallback logs anything unexpected.
  4. log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run with the HTTP status and the full summary, so the check's own history survives a Slack outage.
  5. The flow-level errors block pages Slack if the flow itself fails, so a broken sentinel is never mistaken for settlements arriving normally.
  6. The morning_check trigger runs at 10:00 IST (04:30 UTC) every day and ships disabled: true.

What you get

  • A daily, read-only settlement check with no custom scripting and no extra plugin to install.
  • A status output (HEALTHY / DELAYED / NONE / FAILED / UNREACHABLE) and a settled_rupees output other flows can use, for example to hold a vendor payout run until settlements have landed.
  • Slack messages that say what went wrong and what to check, not just that something failed.
  • An audit line on every run with the settled amount and the latest UTR, which the finance team can match against the bank statement.

Who it's for

  • Indian businesses taking payments through Razorpay whose cash flow depends on settlements arriving on time.
  • Finance and accounts teams who reconcile Razorpay settlements against the bank statement and want to hear about a failed or held settlement the same morning.
  • Teams that already run their finance data and alerting in Kestra.

Why orchestrate this with Kestra

Razorpay's Dashboard shows settlement status when someone opens it, and a failed or held settlement is easy to miss in a busy week. Kestra turns a plain, read-only API call into a scheduled, typed classification with retries, a failure path and a full execution history, and the status output lets other flows wait for the money before paying vendors or closing the day's books.

Prerequisites

  • A Razorpay account. Test mode is free and uses the same API: generate test keys (they start with rzp_test_) in the Dashboard under Account & Settings > API Keys.
  • A Slack incoming webhook for the DELAYED, NONE, FAILED, UNREACHABLE and flow-failure alerts.

Secrets

  • RAZORPAY_KEY_ID: the Razorpay API key ID.
  • RAZORPAY_KEY_SECRET: the matching key secret.
  • SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.

In Kestra OSS, secrets are supplied as environment variables prefixed SECRET_ and base64-encoded, for example SECRET_RAZORPAY_KEY_ID=$(echo -n 'rzp_test_xxxxxxxx' | base64), and read back with {{ secret('RAZORPAY_KEY_ID') }}. Use the Enterprise secrets backend for encryption at rest and access control.

Quick start

  1. Sign up for Razorpay and generate API keys. Test mode has no real settlements, so to see real data, run against live keys with read-only use in mind; the flow never writes.
  2. Add RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET and SLACK_WEBHOOK as secrets.
  3. Run the flow manually and check status, settled_rupees and the latest UTR in the logs against the Settlements page in the Razorpay Dashboard.
  4. Set max_pending_hours to match your settlement cycle (for example 48 for T+2, 24 if you use instant settlements) and enable morning_check.

How to extend

  • Add a Request to GET /v1/settlements/recon/combined to list the payments, refunds and fees inside each settlement for line-by-line reconciliation.
  • Load the settlements into your warehouse with a JDBC task so the finance team can match UTRs against the bank statement in SQL.
  • Store the last status in a KV store and alert only when it changes.
  • Gate a vendor payout flow on status == HEALTHY with a Flow trigger.

Inputs

  • lookback_days: type INT, default 3.
  • max_pending_hours: type INT, default 48.
  • expect_settlements: type BOOL, default true.

Outputs

  • status: one of HEALTHY, DELAYED, NONE, FAILED, UNREACHABLE.
  • settled_rupees: total amount of processed settlements in the window, in rupees.
  • Example path: {{ fromJson(outputs.summarize_settlements.value).latest_utr }} for the bank UTR of the latest settlement.

Links

Pitfalls

  • count is capped at 100 per call; that covers more than three months of daily settlements, but add paging with skip if you use a much longer window.
  • Amounts come back in paise; the jq query divides by 100, so settled_rupees is in rupees.
  • Settlements only happen on working days, so a long weekend or a bank holiday can make NONE fire with a 1-day window; keep lookback_days at 3 or more, or set expect_settlements to false.
  • This flow only reads; it never creates or changes a settlement, so there is nothing destructive to guard with a dry run.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.