Partner API
Create Payments
/v1/partner/payments/batchesCreates one or more payout transactions asynchronously. Send a single payment or a full batch in one request. Corporate context is resolved automatically from the API key.
Header: x-api-key
Use the bank-provisioned partner API key for authentication.
- Header: x-api-key
- Header: Idempotency-Key (required) — a unique value per logical payment attempt. Retrying the same key with the same payload replays the original response instead of creating a second batch; the same key with a different payload returns 409 (idempotency_key_already_used_error). There is no body-field alternative — a value inside the JSON payload is ignored.
- We recommend a UUIDv4 for the Idempotency-Key value, generated fresh for every request.
- Returns 202 Accepted (async) when the intake pipeline is active, or 201 Created on the sync path.
- Tenant context is resolved automatically from the API key — never pass bankTenantId, corporateTenantId, corporateId, or actorUsername.
- Amount is an integer in paise. 100000 = ₹1,000.
- Transaction IDs are pre-assigned in the 202 response — store them immediately to track processing state.
- requestedAt is the ISO-8601 UTC time when the request was accepted and durably recorded. Idempotent retries return the original value.
- A single batch can span multiple packages. The route resolves a subscription for each unique packageCode.
- If the system user is not granted a package, those rows are rejected. Within a granted package, a rejected row follows that package's file-rejection setting; under fail_full_file, one rejected row fails that package's whole group.
- Subscribe to payment.job.result_page and payment.job.completed webhooks for intake completion. GET /v1/partner/payments/jobs/:jobId remains the reconciliation and polling fallback.
- Intake completion means validation and routing finished; it does not mean the created payments are approved or settled.
Request Fields
initiatedByUserIdstringrequiredThe user identifier (user-***) that this API integration authenticates and acts on behalf of — either a service user provisioned for automation, or a real human user's own account.
The initiating user's role permissions determine which payment packages and debit accounts can be utilized. If the role lacks authorization for the requested package or debit account, the transaction row is rejected (package_access_denied / debit_account_access_denied).
Corporate admins can provision service users, or grant permissions to existing human users, under Settings → Users and configure role permissions under Roles & Access.
apiRefNumberstringrequiredA client-supplied reconciliation label for this batch (e.g., PAYROLL-JUN-2026).
Reusable / non-unique — you may send the same value across retries to reconcile a logical run (e.g., re-submitting rows that failed on low balance) under one label. It is not an approval identifier or uniqueness key.
Uniqueness and safe retries are handled by the Idempotency-Key header; per-payment duplicate protection is handled by the row-level transactionReference.
Echoed back on the response, in job status, and on webhooks for reconciliation.
paymentsarray of objectsrequiredA non-empty list of payment instruction objects to ingest. A single request can contain from 1 up to 20,000 payments.
Every payment item is evaluated independently — a single batch can span multiple payment packages, debit accounts, and payment methods.
payments[].transactionReferencestringrequiredA unique reference string for this individual transaction (e.g., SAL-EMP-001). Required and unique on every channel.
Used for idempotent deduplication and tracking across downstream lifecycle webhooks (transaction.*) and bank reports. Put repeatable labels in remark or tag. A reference can only be reused after the prior payment has reached a terminal failed state (failed, cancelled, or rejected).
payments[].beneficiaryIdstringrequiredThe unique identifier of an active, approved beneficiary profile registered under your corporate account (e.g., EMP001).
The beneficiary must be actively mapped to the specified packageCode. If unmapped, inactive, or pending maker-checker approval, the transaction row is rejected.
payments[].amountintegerrequiredThe payment amount represented as a positive integer in paise (1 INR = 100 paise).
For example, pass 100000 for ₹1,000.00, or 500000 for ₹5,000.00. Never pass decimal values, floating-point numbers, or money objects.
payments[].packageCodestringrequiredThe payment package code governing processing rules, daily volume limits, cut-off windows, maker-checker approval matrix routing, and allowed payment rails for this row (e.g., SALPAY, VENDPAY).
Subscribed packages can be queried via the List Payment Packages endpoint.
payments[].paymentMethodCodestringoptionalThe payment rail or method to use for routing (e.g., NEFT, RTGS, IMPS, INTERNAL).
Default behavior: When omitted, the system automatically resolves to default payment method of the package.
payments[].debitAccountNumberstringoptionalThe corporate funding account number from which funds will be disbursed.
Default behavior: If omitted, the system automatically defaults to the package default debit account configured on your corporate subscription.
If specified explicitly, the account must be configured for the payment package and accessible by the initiatedByUserId role.
payments[].beneficiaryAccountNumberstringoptionalThe destination bank account number for the beneficiary.
Default behavior: If omitted, the payment automatically routes to the beneficiary's primary / default bank account registered on their profile.
Specify this field explicitly only if the beneficiary has multiple registered bank accounts and you wish to direct payout to a specific non-default account.
payments[].remarkstringoptionalOptional transaction note or narration (max 100 characters) passed downstream to the bank for account statement narration.
payments[].tagstringoptionalOptional free-text label for internal categorization, ledger grouping, or custom reporting segmentation.
payments[].metadataobjectoptionalKey-value map for storing arbitrary custom structured metadata alongside the transaction (e.g., {"invoiceId": "INV-2026-456", "department": "Operations"}).
Stored as-is and preserved across all webhook payload envelopes and reporting APIs for reconciliation.
payments[].payoutDatestringoptionalOptional future-dated execution date, in ISO format YYYY-MM-DD (e.g. 2026-12-25).
Default behavior: when omitted, the payment executes immediately. When set to a future date, the transaction is held and released on that date.
POST /v1/partner/payments/batches
x-api-key: fp_test_51H8xKlMnOpQrStUv
Idempotency-Key: 9b6cfb6f-13e6-46f8-be53-616c4d5d4142
Content-Type: application/json
{
"initiatedByUserId": "system-user-8f3a2b",
"apiRefNumber": "PAYROLL-JUN-2026",
"payments": [
{
"transactionReference": "SAL-EMP-001",
"beneficiaryId": "EMP001",
"packageCode": "SALPAY",
"amount": 500000,
"paymentMethodCode": "NEFT",
"debitAccountNumber": "8291075255",
"payoutDate": "2026-12-25",
"remark": "June salary",
"tag": "payroll",
"metadata": {
"employeeId": "EMP-001",
"month": "2026-06"
}
},
{
"transactionReference": "VEND-OUT-002",
"beneficiaryId": "VEND001",
"amount": 2500000,
"packageCode": "VENDPAY",
"paymentMethodCode": "RTGS",
"beneficiaryAccountNumber": "9876543210",
"remark": "June vendor payout",
"metadata": {
"invoiceId": "INV-2026-456"
}
}
]
}{
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"apiRefNumber": "PAYROLL-JUN-2026",
"requestedAt": "2026-08-11T06:17:23.456Z",
"message": "Payment request accepted. Transaction IDs are pre-assigned. Processing will complete asynchronously.",
"transactions": [
{
"transactionId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"transactionReference": "SAL-EMP-001"
},
{
"transactionId": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
"transactionReference": "VEND-OUT-002"
}
]
}{
"type": "invalid_parameters_error",
"title": "One or more parameters in your request were invalid.",
"detail": "The 'Idempotency-Key' header is required. Supply a unique string to ensure requests are processed exactly once.",
"status": 400
}