StampMitraDevelopers

Guides

Penny Drop vs Name Match: Bank Account Verification Explained

A penny drop confirms an account exists, accepts a credit and returns the holder name the bank holds. A name match compares that name with the payee you expect. They are two halves of one check, and the StampMitra bank account endpoint returns both. Treat status and match labels as open sets, calibrate thresholds on your data, and never retry a timed-out call blindly.

By StampMitra Developer TeamLast updated: Read this in Hindi

Penny drop and name match are not two competing ways to verify a bank account. They are two halves of one check. A penny drop is how you find out whether an account exists, accepts a credit and who the bank says holds it. A name match is how you decide whether that holder is the person or business you meant to pay. The StampMitra bank account endpoint does both in a single call: it returns the account status and the name at the bank, and, when you send a name, a nameMatchResult and nameMatchScore comparing the two.

The reason the distinction matters is that each half fails differently. A penny drop can succeed on an account that belongs to someone else. A name match can be perfect on an account that is closed. A payout flow that relies on only one will either pay the wrong person or reject a good one. This guide explains what each half proves, where the name at the bank comes from and why it differs from the name on a PAN card, how to turn the response into accept, review and reject decisions, how it compares with lighter and heavier alternatives, and the one place where retries need care.

Key takeaways

  • A penny drop confirms the account exists and is reachable and returns the holder name as the bank holds it. A name match compares that name with the one you expect. You need both.
  • One endpoint, POST /v1/verify/bank-account, scope verification.bank-account.read. Required accountNumber (9 to 18 digits) and ifsc (11 characters). Optional name and phone.
  • The response carries valid, nameAtBank, bankName, utr, city, branch, micr, nameMatchScore, nameMatchResult, accountStatus, accountStatusCode, ifscDetails and a referenceId.
  • In the documented sample nameMatchScore is a string ("100"), nameMatchResult is GOOD_MATCH and accountStatus is ACCOUNT_IS_VALID. The reference shows one example of each, so treat the set of values as open.
  • The name at the bank is often shorter, abbreviated or in a different order than a person's formal name. Build for that: normalise, score, then review.
  • Compare the bank name with the payee you verified elsewhere (for example the name on their PAN), not only with what they typed.
  • The reference does not say that verification calls are idempotent. A timeout on a penny drop is not safe to retry blindly: check whether the first call completed.
  • Bank account verification is flagged higher risk in StampMitra's production access form and opens after an extra check, so apply early.
  • A valid account with a matching name proves the account is real and plausibly theirs. It does not prove their intent or that the account is not a pass-through.

Two ideas that people merge

When someone says "we do penny drop verification", they usually mean four separate things at once.

QuestionWhat answers itFailure it catches
Does this account exist and is it open?The penny drop itself, a small credit that succeeds or failsTypos in the number or IFSC, closed or frozen accounts
Who does the bank say owns it?The beneficiary name returned with the creditPaying a different person than intended
Does that name match who I expect?A name comparison, which is your step or the platform'sWrong payee, mule or borrowed accounts, careless typos
Does the person asking own this account?Not answered by eitherImpersonation, account takeover

The first two come from the bank through the transfer rail. The third is a comparison, which can be done by the API (nameMatchScore) and again by you. The fourth is not answered by a penny drop at all, and needs another factor, such as a document, a reverse penny drop or a payment from the customer's own account.

Keeping these four questions separate makes the design obvious. You can fail on any of the first three automatically, and you route only the third to a human when the score is borderline.

How a penny drop works, and what you learn

A penny drop sends a very small credit to the account being checked and reads the outcome. In bank transfer terms it is an ordinary credit to a beneficiary, using the account number and IFSC you supply. If the credit succeeds, the account exists, is open to credits and is correctly addressed. The bank also returns the beneficiary name as it holds it, and the transfer carries a reference, which in the StampMitra response is the utr field.

Four caveats shape how you use it.

First, the amount and who bears its cost are not stated in the StampMitra reference. The product description calls the endpoint a penny-drop confirmation of an account and holder name, and the response includes a utr. Do not tell your customers an amount until you have confirmed it with support. Do tell account holders that a very small credit may appear in their statement, so that nobody is surprised.

Second, accounts can be valid and still refuse credits for reasons that have nothing to do with fraud: a frozen or dormant account, a restriction on the account type, or a temporary bank-side failure. A failed credit is therefore evidence about the account, not proof of dishonesty. Handle it with "please check the details or try another account", not an accusation.

Third, the name the bank returns is whatever the bank has on its books. It follows the bank's own rules for length, abbreviation and order. It can differ from the PAN name and the name on a cheque book.

Fourth, the check depends on the bank's systems being reachable. That brings failure modes (timeouts, upstream unavailable) that a purely format-based check does not have, and it is why retry behaviour deserves its own section below.

The StampMitra bank account endpoint

ItemValue
PathPOST /v1/verify/bank-account on https://api-b2b.stampmitra.in
AuthAuthorization: Bearer <key> or x-api-key: <key>
Scopeverification.bank-account.read
Required fieldsaccountNumber (9 to 18 digits), ifsc (11 characters, for example ICIC0001234)
Optional fieldsname (beneficiary name to match), phone (account holder's 10-digit Indian mobile number, when available; improves match confidence)
BillingMetered as bank-account.verify after a successful response; bank account verification has its own rate on the pricing page
SandboxA key starting sk_test_; sample data only; never debits your wallet
cURL
curl -X POST "https://api-b2b.stampmitra.in/v1/verify/bank-account" \
  -H "Authorization: Bearer $STAMPMITRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"accountNumber":"000123456789","ifsc":"ICIC0001234","name":"Ravi Kumar"}'
JSON
{
  "referenceId": "SMV-20260927-3B71A0",
  "valid": true,
  "nameAtBank": "RAVI KUMAR",
  "bankName": "ICICI Bank",
  "utr": "017952100012345",
  "city": "Mumbai",
  "branch": "Andheri West",
  "micr": "400229002",
  "nameMatchScore": "100",
  "nameMatchResult": "GOOD_MATCH",
  "accountStatus": "ACCOUNT_IS_VALID",
  "accountStatusCode": "1",
  "ifscDetails": {
    "bank": "ICICI Bank",
    "ifsc": "ICIC0001234",
    "micr": "400229002",
    "address": "Andheri West, Mumbai",
    "city": "Mumbai",
    "state": "Maharashtra",
    "branch": "Andheri West"
  }
}
FieldMeaningUse
referenceIdStampMitra's id for this call, form SMV-YYYYMMDD-XXXXXXXStore it. It is not an idempotency key
validWhether the account verifiedBranch on it first
nameAtBankHolder name as the bank returned itThe string to compare against the payee
bankName, branch, city, micrDetails of the branchShow the bank and branch back to the user so they can spot a wrong IFSC
utrReference of the bank transfer used for the checkKeep it with the record; it is how the bank can trace the call
nameMatchScoreComparison score between your name and the bank's name, returned as a string ("100" in the sample)Parse to a number before comparing
nameMatchResultLabel of the comparison (GOOD_MATCH in the sample)A label; do not hard-code the set of values
accountStatus, accountStatusCodeStatus of the account (ACCOUNT_IS_VALID and "1" in the sample)Decide on status before name
ifscDetailsBranch details from the IFSCContext; check state or bank against what the user claimed

Compare this with the PAN endpoint, which also returns a nameMatchScore string and a nameMatchResult, but with different label values. A shared helper that handles both must treat labels as open sets and parse the score for each.

Reading accountStatus, nameMatchResult and nameMatchScore

The reference documents one example for each of these fields. It does not enumerate other statuses or labels, and it does not publish the comparison logic or score thresholds. Design around that.

For status, write "ACCOUNT_IS_VALID passes; anything else is not a pass". An unrecognised status goes to review or to "please try another account", depending on your product, and you log the raw value and referenceId. For the name, treat nameMatchResult as a hint and nameMatchScore as a signal, and apply your own threshold, calibrated on your own data, using the method in how to verify a PAN and match the name. Everything said there about thresholds, normalisation, layering and review applies here too.

The one difference to note is the target. For PAN, the target name is the person's registered name. For a bank account the target is the bank's own string, which is typically shorter and more abbreviated than the formal name. A threshold calibrated on PAN names will often be too strict for bank names.

Why the name at the bank differs from the name you expect

Bank names are the single largest source of false rejections in payout flows. They are not wrong. They are shaped by the system that stores them.

PatternExampleCause
Truncation"RAVI KUMAR SHARMA ENTERPR"Beneficiary name fields have a character limit on some rails and banks
Surname dropped or reduced to an initial"RAVI K"The bank holds an abbreviated name
Different order"KUMAR RAVI"The bank stores surname first
Titles"MR RAVI KUMAR", "SMT PRIYA SHARMA"Honorifics held in the name field
Joint accountsFirst holder onlyOnly the primary holder's name is returned
Business accountsTrade name rather than legal nameThe account was opened under the brand
ProprietorshipProprietor's name or shop nameEither can be on the account
Minor, HUF or other special accounts"RAVI KUMAR HUF" or a guardian's nameThe account is in a different capacity
Spelling variants"LAXMI" and "LAKSHMI"Names were entered by hand at account opening
Legacy dataOld name after a changeThe bank did not update its record

Two lines of defence follow. First, normalise both strings (case, punctuation, honorifics, abbreviations such as "MOHD" and "MOHAMMED") and compare tokens, as in the PAN guide. Second, decide in advance how much of the difference your product tolerates for each kind of payee. A gig worker paid small amounts weekly can tolerate more variance than a lender disbursing a loan.

Which name should you compare against?

"Compare to the payee name" is not specific enough. You usually have three candidate names, and which you pick changes the result.

CandidateWhere it comes fromWhen to use
Name the payee typed in your formThe userWeakest. It is whatever the user chose to write
Registered name from PAN verificationregisteredName in the PAN responseStrongest for individuals: an independent record, not typed by the payee
Legal name from GSTIN or CIN lookuplegalName or companyNameStrongest for businesses

For individuals, the better design is to verify the PAN first, then compare the bank's nameAtBank with the PAN's registeredName, and use the typed name only as a fallback. A mismatch then means the account is probably not in the name of the person whose PAN you verified, which is exactly the signal you want. For businesses, compare with the registered legal name and be prepared to accept the trade name for proprietorships, where the account may be in either. The wider picture of which identifier to verify first is in PAN vs GSTIN vs CIN.

In your request, still send name. The platform's comparison is part of the same call and costs nothing extra. Send the name you will use as the target, so the score you receive is the score you act on.

Turning the response into a decision

Keep the decision in one pure function. The version below accepts a clean match, sends partial matches and unknown statuses to review, and fails only when the lookup says the account is not valid.

JavaScript
// bank-decision.mjs
const num = (v) => Number.parseFloat(String(v ?? "").replace(/[^0-9.]/g, ""));

export function decideBank(res, { acceptScore = 85, rejectScore = 40 } = {}) {
  const ref = res.referenceId;
  if (!res.valid) return { decision: "FAIL", reasons: ["ACCOUNT_NOT_VALID"], ref };

  const reasons = [];
  if (res.accountStatus !== "ACCOUNT_IS_VALID") reasons.push(`STATUS_${String(res.accountStatus || "EMPTY").toUpperCase()}`);

  const score = num(res.nameMatchScore);
  if (Number.isNaN(score)) reasons.push("NO_SCORE");
  else if (score < rejectScore) reasons.push("NAME_FAR");
  else if (score < acceptScore) reasons.push("NAME_PARTIAL");

  if (reasons.includes("NAME_FAR")) return { decision: "FAIL", reasons, ref };
  if (reasons.length) return { decision: "REVIEW", reasons, ref };
  return { decision: "PASS", reasons, ref };
}

The two thresholds are placeholders to show the shape, not recommendations. Calibrate them on your own results. The structure is what to copy: a high threshold above which you pass, a low threshold below which you fail, and a review band between them. A very low score on a valid account is a signal that the account may belong to someone else, which is why it fails rather than going to review.

And the same logic in Python:

Python
import re

def decide_bank(res: dict, accept_score: float = 85.0, reject_score: float = 40.0) -> dict:
    ref = res.get("referenceId")
    if not res.get("valid"):
        return {"decision": "FAIL", "reasons": ["ACCOUNT_NOT_VALID"], "ref": ref}
    reasons = []
    if res.get("accountStatus") != "ACCOUNT_IS_VALID":
        reasons.append("STATUS_" + str(res.get("accountStatus") or "EMPTY").upper())
    try:
        score = float(re.sub(r"[^0-9.]", "", str(res.get("nameMatchScore") or "")))
    except ValueError:
        score = None
    if score is None:
        reasons.append("NO_SCORE")
    elif score < reject_score:
        reasons.append("NAME_FAR")
    elif score < accept_score:
        reasons.append("NAME_PARTIAL")
    if "NAME_FAR" in reasons:
        return {"decision": "FAIL", "reasons": reasons, "ref": ref}
    return {"decision": "REVIEW" if reasons else "PASS", "reasons": reasons, "ref": ref}

Validate the inputs before spending a call

A malformed value fails with a 400 before anything is sent to a bank. The documented message is accountNumber must be a 9-18 digit numeric bank account number. Catch it earlier.

JavaScript
// bank-input.mjs
export function checkBankInput({ accountNumber, ifsc }) {
  const acct = String(accountNumber ?? "").replace(/\s+/g, "");
  const code = String(ifsc ?? "").replace(/\s+/g, "").toUpperCase();
  if (!/^[0-9]{9,18}$/.test(acct)) return { ok: false, field: "accountNumber", reason: "Account numbers here are 9 to 18 digits." };
  if (!/^[A-Z]{4}0[A-Z0-9]{6}$/.test(code)) return { ok: false, field: "ifsc", reason: "An IFSC has 11 characters: four letters, a zero, then six letters or digits." };
  return { ok: true, accountNumber: acct, ifsc: code };
}

The IFSC shape (four letters, a zero, six alphanumeric characters) is the standard format. A pattern check cannot tell whether the branch exists, but it removes typing errors for free. Ask the user to type the account number twice if your flow can afford the friction, since a one-digit mistake in an account number can still pass the length check and, with a valid IFSC, point at a different real account.

Retries and duplicate calls: the one place to be careful

For PAN, GSTIN and CIN, retrying a timed-out call is usually harmless beyond the cost. For a penny drop, a call involves an actual transfer through a bank. A timeout on your side does not tell you whether the transfer went through. A blind retry can mean two credits to the same account and two charges to you.

The reference says the verification endpoints are not idempotent: the referenceId identifies a call and is explicitly not an idempotency key, and no idempotency header is documented for them. So protect yourself.

  1. Make your job idempotent. Record "verification requested" against the payee and the account before you call, and record the outcome after. If a job restarts, it must check the record before issuing a second call.
  2. Do not retry timeouts automatically. Retry 429 (the request was rejected for rate, nothing was sent) with backoff. For a timeout or a 502 after the request was sent, first look for evidence that the first call completed.
  3. Check the logs. GET /v1/logs with service=bank-account.verify, a from time just before your call and a status filter shows whether the platform recorded a call. If it did, use that result rather than calling again.
  4. Surface the ambiguity. If you cannot tell, tell the user "we are still checking this account" and resolve it asynchronously, rather than trying again in a tight loop.

The wider rules for each status code are in errors, retries and idempotency. The point to carry from there is that bank account verification is the endpoint where "just retry it" is least safe.

Alternatives, and where each fits

A penny drop is one of several ways to establish that an account is real and plausibly the payee's. They differ in what they prove, the friction for the user, the cost and what they require.

MethodWhat it provesFrictionNotes
Penny drop (this endpoint)Account exists, accepts credits, and the bank's holder nameLow: the user types account number and IFSCThe user may see a small credit in their statement
Reverse penny dropThat someone with control of the account sent you moneyMedium: the user must make a paymentStronger on control of the account; needs a collection setup that StampMitra does not provide through this endpoint
UPI handle verificationThat a handle exists and returns a nameLowProves a UPI address, which may not be the settlement account you want
Cancelled cheque or bank statementThat the payee can produce a documentMediumNeeds review, and documents can be altered
Consent-based account data sharingAccount details shared by the customer through a regulated channelMediumA separate regulated flow, not part of this endpoint

For most payout and vendor flows, a penny drop with a name match is the proportionate default. Use a stronger method when the risk is takeover or impersonation, and a document when your policy or your partner bank asks for one. These methods are complementary. A cancelled cheque image plus a matching penny drop tells you more than either alone.

Three payees, three policies

The same response deserves different treatment depending on what you are about to pay and what an error costs. Here are three fictional situations that use the same decision shape with different thresholds.

A weekly payout to a gig worker. Amounts are small and frequent, there are thousands of payees, and a wrongly rejected worker is a support ticket and a churned partner. The policy: verify the PAN first, compare the bank's nameAtBank with the PAN's registered name using a generous threshold, and let a partial match pass with a flag if the first name and the surname initial agree. Route the rest to a review lane that asks for a cancelled cheque photograph. Hold the first payout until the check resolves, then release the held amount. The cost of a mistake is bounded by one week of earnings.

A vendor onboarding for a trading company. Amounts are larger, vendors are fewer and each is worth real effort. The policy: verify the vendor's PAN, GSTIN and, for a company, CIN; then verify the bank account and compare against the legal name from the lookup, accepting the trade name only for proprietorships. A partial match always goes to a person, who asks for a cancelled cheque or a bank letter. A changed account later triggers the same flow and a second approver before the first payment. The cost of a mistake here is a misdirected invoice payment, which is why the review lane is not optional.

A loan disbursement. The amount is large, the borrower has been through full KYC, and the account will receive money that is hard to recall. The policy is strict: the bank name must match the verified borrower name at a high threshold, joint accounts are reviewed, and any partial match goes to a person with the borrower's documents in front of them. The lender also checks the ifscDetails against the borrower's stated bank and city. A name mismatch blocks disbursement until resolved, however embarrassing the delay. Regulated lenders should also confirm with their regulator or partner bank what disbursement controls are required, since those rules sit outside this API.

The three policies use the same decideBank function with different acceptScore and rejectScore values, and differ mainly in what the review lane asks for and what holds money. The code stays the same. Policy is configuration.

Designing the account entry screen

Most failed verifications are born in the form. A few details reduce them.

Ask for the account number twice, or show it back in groups. A single wrong digit in a nine to eighteen digit number can produce a different, real account. Asking twice, or displaying the number in groups of four for the user to check, costs a second and prevents the most expensive typo.

Collect the IFSC from a selector where you can. A bank-and-branch picker that fills the IFSC avoids transposed characters. If users must type it, validate the pattern as shown above and, after a successful call, show back the bank name and branch from the response ("State Bank of ..., Andheri West") so the user can notice a wrong IFSC.

Say what will happen. One line beneath the button: "We will send a very small amount to this account to confirm it. You do not need to do anything." That sentence prevents a flood of support messages from users who spot an unexpected credit. Check the actual behaviour with support before you publish the line.

Give failure messages a next action. Compare these.

SituationPoor messageBetter message
valid false"Verification failed""We could not confirm this account. Please check the account number and IFSC, or try another account."
Name far from the payee"Name mismatch""The name on this account looks different from the name we have for you. Please use an account in your own name, or upload a cancelled cheque."
Partial match"Match score low""We are checking a small difference in the name. This usually takes less than a day."
Timeout"Error""The bank did not respond. We are checking whether the verification went through and will update you."

The last row connects to the retry rules above: the message tells the truth about the ambiguity and does not invite the user to resubmit.

Never reveal the bank's returned name to someone who has not proven they own the payee profile. Showing "We found: RAVI KUMAR SHARMA" in response to a stranger's guess turns your form into a name lookup service.

Evidence and disputes

Sooner or later a payee will say you paid the wrong account, or a partner bank will ask how you decided to pay an account. The verification record is your answer, and it only helps if you kept the right pieces.

For each verification, keep: the payee id, the referenceId, the utr, the time of the call, the account number masked to its last four digits and the full IFSC, the nameAtBank or a hash of it, the score, the status, your decision and its reasons, and the identity of any reviewer who overrode the default. With the referenceId and the utr, StampMitra support and the bank can trace the specific call. With the reasons, you can show why a partial match was accepted, and who accepted it.

When a dispute arrives, resist the urge to re-run the verification as proof. A new call reflects the bank's data today, not the data at the time of your decision, and it adds a second referenceId that muddies the record. Pull the original record, read its fields, and only re-verify if you need the current state to decide a next step. If the underlying account details were wrong at the time, your record should show that the check passed and why, which is what lets you work out whether the failure was a policy gap, a data problem or fraud.

Where verification sits in a payout flow

Place it at the moment an account becomes a payment destination, and again whenever that changes.

  1. At onboarding. Verify the payee's identity first (PAN for individuals, GSTIN and CIN for businesses), then verify the bank account and compare the bank's name with the verified name.
  2. On change of account. A changed bank account is one of the classic fraud events in vendor and payout systems. Treat it like a new payee: verify again, and consider a hold or a second approval before the first payment to the new account.
  3. On a schedule, if your policy needs it. Accounts close and freeze. A periodic re-check of dormant payees before a large payment catches accounts that are no longer valid.
  4. Before large one-off payments. The cost of a call is small compared with a misdirected high-value payment.

Store the referenceId, the utr, the decision, the reasons, the score and the time against the payee. The guide on what to log and keep describes what to retain and what to mask. The bank account number is sensitive: show only the last four digits in your own interfaces.

What this check does and does not protect against

RiskDoes the check help?
Typo in account number or IFSCYes. The credit fails or goes to a name that does not match
Paying the wrong person by mistakeYes, if you compare to a verified name
Payee supplies someone else's valid accountPartly. A name mismatch shows it, but a matching name on a borrowed account of a relative does not
Mule or pass-through accountsNo. A mule account can be genuinely valid and in a real person's name
Account takeover of a legitimate payeeNo. The details are valid; the person changing them is not the payee
Closed or frozen accountYes, in most cases the credit fails
Intent to defraudNo

Say this plainly to your risk team. Verification narrows a class of errors and some frauds. It does not make a payee trustworthy.

Testing and going live

In sandbox, the documented test values are account SANDBOX0000000001 for a valid account and SANDBOX9999999999 for not valid. Any other input gets a stable but arbitrary outcome, roughly two in three valid, and every name is synthetic. Note that those test values do not follow the production format of nine to eighteen digits. If a request with them is rejected by validation on your account, treat that as a mismatch between the documented test values and the format check, and ask support instead of coding around it. Sandbox never contacts a bank, so it cannot show you how real bank names differ from real PAN names. Test your decision function with fixtures built from the patterns in the table above, and calibrate thresholds with real data after go-live.

Bank account verification is one of the products the production access form flags as higher risk, so it opens after a short extra check once your application is approved. Plan the timing: apply for it with the rest of your products, not after launch. The full path, including documents, provisional limits and the wallet, is in sandbox vs production: what you can test.

Cost and ordering

Bank account verification has its own rate: ₹4 per call on pay-as-you-go, with plan pricing in the pricing table. Because each call involves a bank transfer, a failed or retried call is a cost you want to avoid, which makes ordering matter. Run offline input checks first. Verify the payee's identity next. Call the bank endpoint last, and only for payees who passed the earlier steps. The order of checks across a whole vendor flow is covered in Building a vendor onboarding flow.

Common mistakes

  • Treating penny drop and name match as alternatives instead of two halves of one check.
  • Comparing the bank name only with what the user typed instead of an independently verified name.
  • Using a PAN-calibrated threshold for bank names, which are shorter and more abbreviated.
  • Retrying a timed-out call blindly and sending two credits.
  • Rejecting joint-account or trade-name cases outright instead of routing them to review.
  • Hard-coding the set of accountStatus or nameMatchResult values from a single sample.
  • Skipping re-verification when a payee changes their bank account.
  • Showing full account numbers in logs and screens.
  • Telling customers an amount without confirming it.
  • Assuming a valid account with a matching name means a trustworthy payee.

What to do next

Get a sandbox key through the quickstart, call the endpoint with the sandbox values, and read the bank account reference and the product page. Then read how to verify a PAN and match the name for the matching method this guide builds on. For the whole payee flow, see KYC vs KYB and the vendor flow guide. Questions go to support.

API reference at a glance

Bank Account

Bank Account: paramsPOST /v1/verify/bank-account

Request parameters

Request parameters
FieldTypeDescription
AuthorizationrequiredheaderBearer <api-key> (e.g. sk_test_... in sandbox, sk_live_... in production). The x-api-key header is also accepted.
accountNumberrequiredstring9-18 digit numeric bank account number.
ifscrequiredstring11-character IFSC code, e.g. ICIC0001234.
namestringBeneficiary name to match.
phonestringAccount holder's 10-digit Indian mobile number, when available; improves match confidence.
Bank Account: responsePOST /v1/verify/bank-account
{
  "referenceId": "SMV-20260927-3B71A0",
  "valid": true,
  "nameAtBank": "RAVI KUMAR",
  "bankName": "ICICI Bank",
  "utr": "017952100012345",
  "city": "Mumbai",
  "branch": "Andheri West",
  "micr": "400229002",
  "nameMatchScore": "100",
  "nameMatchResult": "GOOD_MATCH",
  "accountStatus": "ACCOUNT_IS_VALID",
  "accountStatusCode": "1",
  "ifscDetails": {
    "bank": "ICICI Bank",
    "ifsc": "ICIC0001234",
    "micr": "400229002",
    "address": "Andheri West, Mumbai",
    "city": "Mumbai",
    "state": "Maharashtra",
    "branch": "Andheri West"
  }
}
Bank Account: pricePOST /v1/verify/bank-account

Bank Account: ₹4 per successful call, pay-as-you-go (lower on Growth and Scale).

Frequently asked questions

What is the difference between penny drop and name match?

A penny drop sends a small credit to learn whether the account exists, accepts credits and who the bank says holds it. A name match compares that holder name with the person or business you expect. They are two halves of one check, not alternatives.

What does the StampMitra bank account endpoint return?

It returns valid, nameAtBank, bankName, utr, city, branch, micr, nameMatchScore, nameMatchResult, accountStatus, accountStatusCode, ifscDetails and a referenceId. Send name to get a match comparing your name with the bank's, and optionally phone to improve match confidence.

Why does the name at the bank differ from the PAN name?

Banks store names under their own rules: truncation, abbreviation, surname first, honorifics, only the first holder of a joint account, trade names on business accounts and legacy names after a change. Normalise both strings, score the match and route partial matches to review.

Which name should I compare the bank name against?

Prefer an independently verified name over what the payee typed: the registered name from the PAN lookup for individuals, and the legal name from GSTIN or CIN for businesses. A mismatch then signals that the account may not belong to the person you verified.

Is it safe to retry a timed-out bank account verification?

Not blindly. The call involves a bank transfer and the reference documents no idempotency for verification endpoints, so a retry can send a second credit. Record each attempt, check the request logs for a completed call first, and retry only 429 responses automatically.

Is nameMatchScore a number?

In the documented sample it is a string, shown as 100, with nameMatchResult GOOD_MATCH. Parse it to a number before comparing, and do not hard-code the set of labels from one example. Calibrate your own accept and review thresholds on real results.

Does a valid account with a matching name prove the payee is trustworthy?

No. It shows the account is real and plausibly theirs. It does not stop mule or pass-through accounts, account takeover of a legitimate payee or intent to defraud, so keep other controls such as holds and second approvals on changed accounts.

What are the sandbox values for bank account verification?

SANDBOX0000000001 returns a valid account with a sample holder and SANDBOX9999999999 returns not valid. Other inputs get a stable but arbitrary outcome. These values do not follow the production nine to eighteen digit format, so ask support if validation rejects them.

Why is bank account verification flagged as higher risk?

In StampMitra's production access form, bank account verification and e-Sign are flagged higher risk and open after a short extra check, while PAN, GSTIN, CIN, Udyam and e-Stamp are treated as lower risk. Apply for it together with your other products.

What should I store after a bank account verification?

Store the referenceId, utr, time, masked account number with the full IFSC, the score, status, your decision and reasons, and any reviewer override. Show only the last four digits of the account number in your own screens and logs.

Sources

  • StampMitra API reference data for /v1/verify/bank-account: request fields, response fields, errors, scopes and billing, as in this repository on 2026-10-29. The reference documents one example value for the status and match fields.
  • StampMitra sandbox test values, copied from the sandbox responder, 2026-10-29.
  • StampMitra production access form: product risk flags and review steps, as implemented in this repository on 2026-10-29.
  • Reserve Bank of India and National Payments Corporation of India publications on IMPS and IFSC conventions: https://www.rbi.org.in and https://www.npci.org.in (general conventions only; confirm current rules there, 2026).