Skip to main content
POST
Submit verification details

Overview

When a payment needs verification details before it can settle, this is where you send them. One call carries every field the payment is waiting on, and you can send them as you collect them rather than all at once. Use it when you receive a VERIFICATION_NEEDED webhook with verification_status of ACTION_REQUIRED.
This replaces POST /pg/payments/{uid}/upload-verification-details/, which is deprecated. The old endpoint took one fixed set of fields, rewrote them all on every call, and worked on a single gateway. Move existing integrations across when convenient; build new ones here.

When this applies

Credits that land in a Virtual Bank Account create a payment against an order. If that order is missing anything the payment needs before it can settle, the platform emits VERIFICATION_NEEDED naming exactly which fields are absent. This endpoint is how you supply them programmatically, rather than editing the order in the dashboard.
1

Payment recorded

A credit is received and a payment is created against the order.
2

Missing fields reported

VERIFICATION_NEEDED fires with an action_required_fields array naming each one.
3

Supply the details

POST them to this endpoint, under the same codes the event used.
4

Read what is left

The response carries outstanding — the codes still owed after that call.

A worked example

Your answers go in fields, keyed by code; files you have already uploaded go in documents as { "code": …, "ref": … }.
JSON only. Files are never posted here. Upload a file with POST /pg/payments/{payment_id}/documents/, which returns a ref, then name that ref under its code in documents.

Which codes to send

The codes come from the payment, not from a fixed list. Read them off action_required_fields on the webhook, or call GET /pg/payments/{payment_id}/verification/ at any time for the full set with a satisfied flag on each item. What a payment is asked for depends on what it is, and it changes as the underlying regulatory requirements do — read the codes rather than hardcoding them, and a revision reaches you without a release on your side. These are the ones an import payment is most often asked for: Document codes work the same way; an import payment is typically asked for invoice.
type_of_goods is not sent here. It is part of the order’s product details, declared when you create the order, and it is never listed as outstanding.
business_model and purpose_code must travel together, and only once. They place the payment, and until both are set nothing else about it can be checked — so send them first, or in the same call as the rest. Send one without the other and the call is refused naming the one you left out; once set, sending either again is a 400.business_model is listed when nothing has declared it, not when it is empty — it defaults to B2C, so sending "business_model": "B2C" is a real answer rather than a no-op. Many orders arrive already classified, so GET the payment first if you are unsure.

Send as much or as little as you have

Send three fields today and two tomorrow and you have sent five. Nothing you sent earlier is wiped by a later call, and a field’s newest value wins. Nobody collects an invoice, an address and an IEC in the same instant.
finalize defaults to true. It applies only to a complete set and never forces an incomplete one through, so a partial send is accepted either way — you do not have to set it. Pass finalize: false when you have sent everything but do not want the payment’s verification completed yet, and the details are recorded without it.
That tolerance ends once verification has been filed. A payment that has moved past ACTION_REQUIRED refuses further sends — GET the payment and read settlement_status before you send.

The response

Payments that go through an additional verification step can return two further values, in_review and action_required. They do not arise on this flow.

Errors

A refused send answers in the standard error envelope, with details keyed by the offending code — so one call reports every problem at once:
An unknown key is a 400, not a silent drop. A misspelled invoice_number that we quietly ignored would leave the payment waiting forever for a field you believe you sent. Every key is checked before any is written, so one typo cannot half-apply a call.

Authorizations

X-Client-ID
string
header
required

Client Application ID - Your unique application identifier used to authenticate API requests. You can find your Client ID in the Developer Settings section of the merchant dashboard.

X-Client-Secret
string
header
required

Client Secret Key - Your secret key used alongside the Client ID for secure authentication. Keep this confidential and never expose it in client-side code. Available in the Developer Settings section of the merchant dashboard.

X-Merchant-ID
string
header
required

Merchant Identifier - The unique ID for the merchant account. This is required for PSP (Payment Service Provider) merchants who manage multiple merchant accounts. You can find merchant IDs in the Merchant Management section of the dashboard.

X-API-Version
string
header
required

API Version - Specifies which version of the API to use (e.g., '1.X.X', '2.X.X', or '3.X.X'). This header allows you to control which API version your integration uses. Default version information is available in the Developer Settings.

Path Parameters

payment_id
string
required

UID of the payment the details are for.

Body

application/json
fields
object

Answers keyed by checklist code, e.g. {"remitter_pan": "ABCPK1234F", "buyer_city": "Bengaluru"}. Values are strings, numbers or booleans. Dates are YYYY-MM-DD; amounts (principal, collected_tcs) are decimal strings in rupees. An unknown key is a 400 rather than a silent drop — a typo you never hear about would leave the payment waiting forever for a field you believe you sent. Omit a key rather than sending null or an empty string; a blank value is also a 400.

Example:
documents
object[]

Documents by reference, or waivers. Each entry names exactly one of ref and waiver_reason; sending both, neither, or the same code twice in one call is a 400.

finalize
boolean
default:true

Whether to run a complete LRS set through the verification gateway. Never forces an incomplete one through.

linked_payment_id
string | null

The earlier payment this one tops up — the buyer was short, or paid the TCS separately. Resolved against your own payments; a link that cannot be resolved is refused. Read by the call that files, so send it with the call that completes the set.

Example:

"PR6938527534"

reason
enum<string> | null

Why the two payments are linked; an audit label on the link. REMAINING_AMOUNT — covering a principal shortfall. TCS_PAYMENT — paying the TCS separately. Only valid together with linked_payment_id.

Available options:
REMAINING_AMOUNT,
TCS_PAYMENT
Example:

"REMAINING_AMOUNT"

Response

Details received. The body says where the payment now stands and what is still owed.

success
boolean
Example:

true

message
string
Example:

"Verification details received"

data
object