Skip to main content
Money has arrived, and the payment is sitting in ACTION_REQUIRED — we have the funds but we do not know what the service was or who bought it. We tell you exactly what it needs, you send it, and the payment completes itself once nothing is outstanding.
This is the services flow — consultancy, software, professional fees, freight. Physical goods ask for an HS code and the buyer’s IEC, which this flow does not collect.
This flow is always scoped to the sub-merchant, never the PSP. The purpose codes you may declare come from the account the payment settles to, not from the credential you call with.Every call on this page carries a payment_id in its path and derives that account from the payment. A PSP names the sub-merchant with X-Merchant-ID when it creates the order, before there is a payment to read it from.

There is no identity step

Worth saying before anything else, because it is what makes this flow short. Nothing you send is verified against an external register — no PAN, no IEC, no registration number. The buyer name is a plain field you send, checked against nothing. Nothing has to clear before you can proceed, and there is no pre-payment step at all — no PAN to verify, no amount to quote. Everything happens after the money lands.

Once the money lands

The payment is created as ACTION_REQUIRED, and a VERIFICATION_NEEDED webhook arrives naming what is outstanding, as a flat array of codes in action_required_fields.
Treat that first list as a nudge, not the requirement set. The credit arrives before anything has classified the payment, so no requirement set claims it yet and the event falls back to a legacy order-field check — it can name product_description and hs_code, which this flow never asks for. Classify the payment, then read the real set from Get Verification Requirements.
A second event follows the classifying call. PAYMENT_DETAILS_MISSING fires once a checklist claims the payment, and it names what that checklist still owes — real services codes, not the legacy order-field guess the first event made. It carries the list under details_missing, not action_required_fields, alongside order_uid, payment_id and message. It tells you nothing Get Verification Requirements does not, so you can ignore it and poll instead.
From there it is one loop, repeated until nothing is outstanding:
  1. Classify the payment if nothing has yet. A VBA credit arrives with no business_model or purpose_code on its order, so there is no checklist to read — Get Verification Requirements answers with those two facts as ordinary outstanding fields. Send them back under the same codes.
  2. Read what it owes from that same endpoint. Eleven fields — nine buyer and invoice ones, plus invoice_amount and declaration — and the commercial invoice. Some may already be satisfied — the credit usually carries buyer_name.
  3. Upload the invoice via Upload Document, and keep the ref it returns. Classify first — the upload is routed by the payment’s flow, so it is refused until step 1 has landed.
  4. Send what you have via Submit Verification Details — fields as a map, documents as {code, ref}. Partial sends are fine; later sends merge over earlier ones.
  5. Read the response: status says where the payment stands, and outstanding names what is still owed.
  6. Once nothing is outstanding the set is filed, the payment moves to in_review, and it settles from there.
The finalizing call also raises an event, either way it goes — VERIFICATION_NEEDED both times, and verification_status is what separates them. A filed set sends IN_REVIEW with an empty action_required_fields. A finalizing call the checks refuse sends ACTION_REQUIRED naming the code that failed — the same code the response names.Both matter when the party holding the webhook URL is not the party making the call: a PSP filing on behalf of a sub-merchant gets the response, and whoever configured the merchant’s webhook URL gets the event.
If you ever need the whole picture rather than just what changed — you missed a webhook, or you are resuming a payment someone else started — Get Verification Requirements returns the complete set, satisfied or not. It is idempotent; call it any time.

Submit Verification Details

The full request body for B2B services, and every error.

Get Verification Requirements

The full response shape, and what the services checklist asks for.

Declare what the service was

Every services payment settles under one FEMA purpose code, and you pick exactly one: S0802, S0803, S1005, S1009, S1010, S1013, S1015, S1016, S1017, S1105 or S1106, all against business_model: "B2B". They share one checklist.
You can only send a code your merchant account is enabled for. That list is set during KYC, from the business categories you declared. A valid code you are not enabled for is rejected just like one that does not exist — if the code you need is missing, that is an onboarding conversation, not an API problem.

When it doesn’t go straight through

Five things that will bite you

The buyer is the Indian remitter, not your customer abroad. These fields describe whoever in India paid you, not the overseas party being paid. buyer_postal_code is stored as an Indian PIN code, so send the remitter’s Indian address — filling this block with your own address is the most common way a first integration goes wrong. The amount you declare has to match the credit. invoice_amount is on the checklist, in rupees, and it is checked against what actually landed in the VBA — more than a paisa apart and the finalizing call is refused naming invoice_amount. It is a cross-check, not a figure that decides anything: what settles is still the credit itself. There is no amount_to_be_settled and no TCS; both are a 400, like any unknown key. The declaration is a field, not an implication. Send declaration: true — that the service was received and the invoice is genuine. Anything falsy refuses the call naming declaration. Partial sends merge. Three fields today and two tomorrow is five fields. Nothing you sent earlier is wiped, so send each thing as you get it rather than waiting for the full set. Editing closes the moment the set is filed. While the payment is in review the fields and the invoice are frozen — no corrections, no swapping the file. If you spot a mistake after filing, wait for it to be sent back and fix it then.

While it is in review

Ops either approves the verification, sends it back for changes, or rejects it. Only a send-back raises a webhook — approve and reject raise none, so for those two, poll Get Verification Requirements and read settlement_status:
A send-back arrives as VERIFICATION_NEEDED, with verification_status back at ACTION_REQUIRED. message carries the Ops comment — the same text the merchant is emailed, and usually the only thing that says why it came back. action_required_fields names whatever the checklist still owes; it is empty only when the checklist really is satisfied and the comment alone says what to change.An empty list here does not mean there is nothing to do. The payment is open again because a person asked for a change. Branch on verification_status — the same event carries IN_REVIEW with an empty list when a submit passes, and that one genuinely needs nothing from you.
A rejection is terminal and moves settlement_status to NOT_APPLICABLE — the payment will not settle, and nothing you send reopens it. Ops raises no webhook for the decision itself, so this value is how you learn it. A full refund of the credit is raised in the same moment. Where the payment sits decides what happens next:
Reconcile a rejection against the refund, not against this endpoint. NOT_APPLICABLE tells you the verification is over; only the refund tells you the money moved. If the refund could not be raised at all it is recorded as FAILED with the reason on it, so a rejection always leaves a refund record to find.

After it settles: proof of delivery

Verification review is not the last compliance step. Once the money has reached you, one thing is still owed: evidence that the service was actually delivered. It does not gate your settlement — it is what backs the remittance if the transaction is ever examined.
  1. Upload each proof via Upload Document with type=delivery_proof, and keep each ref. You can do this before the payment settles — the upload endpoint does not wait on settlement.
  2. Wait for PAYMENT_SETTLED. Only the submit refuses before settlement; List Settlement Documents reads fine either way and reports is_settled.
  3. File them with Submit Settlement Documents, sending {"refs": [...]}. One call names the set, drops any upload it does not name, and locks the result. This flow takes many files under the single delivery_proof type, and needs at least one.
There is no attach step, and nothing to draft. An upload is durable and readable back, so the proofs you have uploaded are the draft — collect them over days, check them with List Settlement Documents, and send the refs when the set is ready. Each ref is bound to the payment it was uploaded against.
Submitting is final, refs is the whole set — an uploaded proof you do not name is dropped — and the whole call is one transaction: a refused ref files nothing and drops nothing. Read the set back with List Settlement Documents before you lock it; it lists what you have uploaded, and its state is FRESH until a submit lands, SUBMITTED after.

What this flow never asks for

Integrations go wrong here more often by sending something extra than by leaving something out. The complete list of what has no place in a B2B services payment: Every one of them is a 400 if you send it, the same as any unknown key.
invoice_amount and declaration are asked for, and used to be on this list. Both are checklist items now: the amount is cross-checked against the credit, and the declaration has to be true.

Collection Overview

How a payment moves from the buyer’s bank to your account abroad.

List Settlement Documents

What is filed so far, and whether the set is locked.

Submit Settlement Documents

File the delivery proofs by ref and lock the set.