Payout API Integration for Fintech Apps
|

How to Integrate Payout API in Your Fintech App: Step-by-Step

Why Every Fintech App Needs a Payout API

Whether you are building a lending platform, neobank, insurance tech, or gig economy app, payout capabilities are a core requirement. Your users expect instant money transfers, and your operations demand automated disbursements.

This guide walks you through integrating the NxtBanking Payout API into your fintech application — from authentication to production deployment.

Prerequisites

  • NxtBanking account with API access (sign up here)
  • API Key and Secret from the dashboard
  • Backend server (Node.js, Python, Java, PHP, or .NET)
  • HTTPS endpoint for webhook callbacks

Step 1: Authentication

All API requests require Bearer token authentication. First, exchange your API key and secret for an access token:

POST /api/v1/auth/token
Content-Type: application/json

{
  "api_key": "your_api_key",
  "api_secret": "your_api_secret"
}

Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 3600
}

Step 2: Register Beneficiary

Before sending a payout, register the beneficiary bank account. NxtBanking automatically performs penny-drop verification.

POST /api/v1/beneficiaries
Authorization: Bearer {access_token}

{
  "name": "Vendor ABC Pvt Ltd",
  "account_number": "1234567890",
  "ifsc": "HDFC0001234",
  "bank_name": "HDFC Bank",
  "type": "current"
}

Step 3: Initiate Payout

POST /api/v1/payouts
Authorization: Bearer {access_token}

{
  "beneficiary_id": "ben_abc123",
  "amount": 50000,
  "mode": "IMPS",
  "reference_id": "ORDER_12345",
  "narration": "Vendor payment for March 2026"
}

Step 4: Handle Webhooks

Set up your callback URL in the dashboard. NxtBanking sends real-time status updates:

POST /your-webhook-endpoint
{
  "event": "payout.completed",
  "payout_id": "pay_xyz789",
  "reference_id": "ORDER_12345",
  "status": "SUCCESS",
  "utr": "1234567890",
  "amount": 50000,
  "timestamp": "2026-02-26T10:30:00Z"
}

Step 5: Error Handling Best Practices

  • Idempotency: Always send a unique reference_id to prevent duplicate payouts.
  • Retry Logic: Implement exponential backoff for transient failures (5xx errors).
  • Balance Check: Always check your wallet balance before initiating payouts.
  • Webhook Verification: Validate webhook signatures to prevent spoofing.
  • Status Polling: If webhook is delayed, poll the payout status endpoint as fallback.

Step 6: Go Live Checklist

  1. Switch from sandbox to production API keys.
  2. Configure production webhook URL with HTTPS.
  3. Set up IP whitelisting for API access.
  4. Enable two-factor authentication on your NxtBanking account.
  5. Test with small amounts (₹1-10) before bulk payouts.
  6. Set up balance alert notifications.

Common Integration Patterns

Lending Platform

Loan approval → Trigger payout to borrower account → Track disbursement → Update loan record on webhook callback.

E-commerce Marketplace

Order delivered → Calculate seller settlement → Deduct commission → Trigger payout to seller → Auto-reconcile.

Gig Economy

Daily earnings calculated → Batch payout at end of day → Individual status tracking per worker → Handle failures with retry.

Frequently Asked Questions

How long does payout API integration take?

Most developers complete integration in 2-4 hours with NxtBanking documentation and sandbox environment.

Can I test without real money?

Yes. NxtBanking provides a full sandbox environment that simulates real payout behavior without actual fund transfers.

What programming languages are supported?

The RESTful API works with any language. We provide official SDKs for Node.js, Python, Java, PHP, and .NET.

Get Your API Keys Now →


Production-Ready Implementation Walkthrough

The skeleton above shows the happy path, but shipping to production means handling the 20% of payouts that don’t complete on the first try. This section gives you the exact patterns we see work across our 400+ live integrations — job queues, idempotency, signed webhooks, and reconciliation.

1. Queue every payout — never call the API from a request handler

A payout API call can take 1–30 seconds on IMPS and up to 2 minutes on NEFT/RTGS. Never block the user’s HTTP request waiting for it. Push the job onto a queue (Bull, Sidekiq, Celery, AWS SQS — your choice) and return a payout_id immediately. The queue worker does the API call and writes the final status back to your database when the webhook arrives.

// Node.js — BullMQ worker
import { Queue, Worker } from "bullmq";

const payoutQueue = new Queue("payouts", { connection: redisConfig });

// API route — enqueue and return
app.post("/api/payouts", authMiddleware, async (req, res) => {
  const job = await payoutQueue.add("push", {
    userId:         req.user.id,
    beneficiaryId:  req.body.beneficiary_id,
    amount:         req.body.amount,
    referenceId:    `PO_${Date.now()}_${req.user.id}`,
  });
  res.status(202).json({ payout_id: job.id, status: "QUEUED" });
});

// Worker — separate process
new Worker("payouts", async job => {
  const token = await getToken();  // cached, reused
  try {
    const res = await pushPayout(token, job.data);
    await db.payouts.update(job.data.referenceId, {
      nxtb_payout_id: res.data.payout_id,
      status:         res.data.status,
    });
  } catch (err) {
    if (err.response?.status === 409) {
      // Duplicate — the original payout already exists. Look it up.
      const existing = await lookupByReference(token, job.data.referenceId);
      await db.payouts.update(job.data.referenceId, existing);
      return;
    }
    throw err;  // Bull retries with exponential backoff
  }
}, { connection: redisConfig, concurrency: 10 });

2. Idempotency with a client-side reference_id

The golden rule of payment integrations: generate a unique reference_id before you write to your own database. Store the reference alongside the pending payout row. If the API call times out or your worker crashes, the next attempt sends the same reference and our idempotency engine returns the original payout — no double-debit risk.

-- PostgreSQL schema for the local payout table
CREATE TABLE payouts (
  id               BIGSERIAL PRIMARY KEY,
  user_id          BIGINT    NOT NULL,
  reference_id     TEXT      NOT NULL UNIQUE,  -- your idempotency key
  nxtb_payout_id   TEXT,                        -- fills in after API call
  beneficiary_id   BIGINT    NOT NULL REFERENCES beneficiaries(id),
  amount           NUMERIC(14, 2) NOT NULL,
  currency         CHAR(3)   NOT NULL DEFAULT 'INR',
  status           TEXT      NOT NULL DEFAULT 'QUEUED',
  failure_reason   TEXT,
  utr              TEXT,
  fee              NUMERIC(10, 2),
  created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at       TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_payouts_status ON payouts(status) WHERE status IN ('QUEUED', 'PROCESSING');
CREATE INDEX idx_payouts_user   ON payouts(user_id, created_at DESC);

3. Webhook signature verification (do this first, always)

# Python / Flask
import hmac, hashlib, os
from flask import Flask, request, abort

SECRET = os.environ["NXTB_WEBHOOK_SECRET"].encode()

@app.post("/webhooks/nxtb/payout")
def payout_webhook():
    sig = request.headers.get("X-NxtB-Signature", "")
    expected = hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(401)

    event = request.get_json()
    # Idempotent update — same webhook may arrive twice
    db.execute("""
      UPDATE payouts
         SET status = %s, utr = %s, failure_reason = %s, updated_at = NOW()
       WHERE reference_id = %s
         AND status NOT IN ('SUCCESS', 'FAILED', 'REVERSED')
    """, (event["status"], event.get("utr"), event.get("failure_reason"),
          event["reference_id"]))
    return "", 200

4. Reconciliation — trust but verify

Webhooks can be lost. Networks split. Your app goes down at 3 AM. Never rely solely on webhooks to update payout state. Run a reconciliation job every 15 minutes that pulls every payout stuck in QUEUED or PROCESSING for more than 2 minutes and queries GET /v1/payouts/{id} directly.

// Node.js — cron job
import cron from "node-cron";

cron.schedule("*/15 * * * *", async () => {
  const stale = await db.payouts.findMany({
    where: {
      status: { in: ["QUEUED", "PROCESSING"] },
      updated_at: { lt: new Date(Date.now() - 2 * 60 * 1000) }
    },
    take: 200
  });

  const token = await getToken();
  for (const p of stale) {
    if (!p.nxtb_payout_id) continue;  // never left our side
    const live = await axios.get(
      `https://api.nxtbanking.com/v1/payouts/${p.nxtb_payout_id}`,
      { headers: { Authorization: `Bearer ${token}` } }
    );
    if (live.data.status !== p.status) {
      await db.payouts.update({
        where: { id: p.id },
        data:  { status: live.data.status, utr: live.data.utr, updated_at: new Date() }
      });
    }
  }
});

5. User-facing status polling

Your React / Flutter / Android client shouldn’t poll the NxtBanking API directly — poll your own backend, which reads from your local payouts table. The table is always up to date (webhook or reconciliation job). This keeps your credentials server-side and gives you millisecond response times.

// React — useSWR hook with 2-second polling until terminal
import useSWR from "swr";

function usePayoutStatus(payoutId) {
  const { data } = useSWR(
    payoutId ? `/api/payouts/${payoutId}` : null,
    fetcher,
    {
      refreshInterval: data =>
        data?.status && ["SUCCESS", "FAILED", "REVERSED"].includes(data.status) ? 0 : 2000
    }
  );
  return data;
}

Pre-launch production checklist

  • Secrets live in your environment manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault) — never in .env files committed to git.
  • Webhook endpoint is HTTPS-only and verifies signatures before any parsing.
  • Every payout row has a unique reference_id populated before the API call.
  • Reconciliation cron runs at least every 15 minutes.
  • Balance monitoring alerts when settlement account dips below 2× daily outflow.
  • Rate-limiting: your app’s user-facing payout endpoint is capped per-user to prevent abuse.
  • Double-entry accounting — every successful payout creates matching debit + credit rows in a ledger table.
  • Operator dashboard shows stuck payouts (QUEUED / PROCESSING > 10 min) with a manual “re-check” button.

For scaling to multi-bank fallback (auto-failover between sponsor banks when one is down), see our follow-up article: Building a Multi-Bank Payout System. For the end-to-end API reference, start with Connected Banking Payout API Guide.

Know More