StampMitraDevelopers

Use cases

KYC API for Fintech and NBFC Onboarding in India

A KYC API for fintech and NBFC onboarding runs one verification call per document (PAN, bank account, GSTIN, CIN, Udyam and more) from your backend and returns structured results. Run cheap, decisive checks first, queue ambiguous cases for human review, and log every result. An API check is evidence only; regulated entities must still follow their regulator's KYC rules.

By StampMitra Developer TeamLast updated: Read this in Hindi

A KYC API for fintech and NBFC onboarding in India is a set of server-to-server checks that confirm a borrower's identity and business documents against registry data while the application is still open. With StampMitra you call one endpoint per document (PAN, bank account, vehicle RC, driving licence, voter ID, passport, GSTIN, CIN, Udyam) and get a structured JSON result plus a referenceId you can store against the applicant. You sequence those calls so the cheapest and most decisive checks run first.

One point has to be clear before any code: a successful API check is evidence you attach to a file. It is not, by itself, regulatory KYC compliance. A regulated entity such as an NBFC must follow its own regulator's KYC rules, which govern who may be onboarded, how identity is established, what is recorded and how often it is refreshed. This guide covers the engineering side: journeys, ordering, cost, retries, review queues, logging and async design.

Everything below is grounded in the verification endpoints documented in the StampMitra verification API reference. Where the platform does not offer something today (for example, self-serve webhooks for verification results), the guide says so and shows the pattern you build on your side instead.

Key takeaways

  • Each check is a synchronous POST /v1/verify/<product> call authenticated with an API key (Authorization: Bearer <key> or x-api-key), scoped per product, for example verification.pan.read.
  • Validate formats in your own code first. Malformed input fails with a 400, but wasted calls and slow journeys still cost you.
  • Both a positive and a negative determination (valid: false, found: false) are completed, billable verifications, so retrying a "no" blindly just pays twice.
  • Run cheap, high-signal checks (PAN, then bank account) before expensive or conditional ones, and stop the journey early when a hard failure appears.
  • Separate three outcomes: pass, hard fail, and "needs a human". Route the third to a manual-review queue with the evidence attached.
  • Store the referenceId, the response body you relied on, the timestamp and your decision. Keep personal data only as long as your purpose and your legal duties require.
  • An API result does not replace your regulator's KYC requirements. For NBFCs the reference is the RBI Know Your Customer (KYC) Direction, 2016, as updated. Ask your compliance team how each API check maps to your policy.
  • Verification endpoints are synchronous today. The webhooks page documents order-lifecycle events, so build your own queue for async verification work.

What does a KYC API actually do in a lending onboarding flow?

A lending onboarding flow collects claims from the applicant: "my PAN is this", "this is my bank account", "this company is mine". A verification API tests each claim against a registry-backed source and returns a normalised answer. It does not decide whether to lend, and it does not decide whether your KYC obligations are met.

The unit of work: one document, one call

Every StampMitra verification product follows the same shape. You send a JSON body with the document number and optional matching fields, you get back a JSON object. The PAN endpoint, for example, is POST https://api-b2b.stampmitra.in/v1/verify/pan with pan required and name optional. The response carries fields such as valid, registeredName, panStatus, nameMatchResult and nameMatchScore, which your rules engine can consume directly. See the PAN verification guide for a deeper walkthrough of that single endpoint.

Where it sits in your stack

The API is called from your backend, never from a browser or mobile app, because the key carries scopes that spend money. A typical lending stack has an application service, a rules or decision engine, a document store and a case-management tool for analysts. The verification calls sit between the application service and the decision engine: they enrich the application with checked facts.

What it does not do

It does not capture consent, hold the applicant's documents, run liveness or video interviews, or file anything with a regulator. Those remain your responsibilities or those of other vendors. Keeping this boundary in view prevents the most common design error in KYC projects, which is treating "the API said valid" as "the customer is KYC-complete".

Which checks apply to individual and to business borrowers?

The checks you pick depend on borrower type and on the product being sold. The table maps the nine verification products named in this guide, with the scope each call needs and the main fact it returns. Two helper endpoints, pan-to-gstin and pan-to-udyam, resolve a PAN to registrations and are covered in the business journey below.

Product slugEndpointScopeKey inputWhat you learn
pan/v1/verify/panverification.pan.readpan, optional nameValidity, status, registered name, name-match result
bank-account/v1/verify/bank-accountverification.bank-account.readaccountNumber, ifsc, optional nameAccount exists, beneficiary name, name match
rc/v1/verify/rcverification.rc.readvehicleNumberRegistration status, owner name, class, insurance date
driving-license/v1/verify/driving-licenseverification.dl.readdlNumber, dobLicence validity, name, status
voter-id/v1/verify/voter-idverification.voter.readvoterId, optional nameValidity, name, date of birth
passport/v1/verify/passportverification.passport.readfileNumber, dob, optional nameValidity, name, application type
gstin/v1/verify/gstinverification.gstin.readgstin, optional businessNameRegistration status, legal and trade name
cin/v1/verify/cinverification.cin.readcinCompany status, incorporation date, directors
udyam/v1/verify/udyamverification.udyam.readudyamNumberRegistration found, status, enterprise type

Individual borrowers

For a salaried or self-employed individual the usual evidence set is a PAN check with name match, a bank-account check with name match for the disbursal or repayment account, and one more identity document where your policy asks for it (driving licence, voter ID or passport). For vehicle-backed loans the RC check ties the asset to the borrower.

Business borrowers

For a proprietorship, partnership or company, you verify the entity and the people behind it: GSTIN for tax registration, CIN for a company's existence and directors, Udyam for MSME status, and the entity's own bank account. A deeper treatment lives in the business verification guide.

A note on what each field means

A field such as registrationStatus: "Active" on a GSTIN response reports what the source returned at call time. It says nothing about the borrower's creditworthiness, and it can change tomorrow. Record the timestamp of every call so a later reviewer knows how fresh the evidence was.

What order should you run the checks in?

Order matters for cost, speed and fraud signal. The principle is simple: run the checks that are cheapest, fastest and most likely to eliminate bad applications first, and let their results decide whether the expensive or conditional checks run at all.

The individual borrower sequence

  1. Local format validation (free). Check PAN against ABCDE1234F, IFSC as 11 characters, account number as 9 to 18 digits, DL and voter ID against the formats in the docs. The API validates input format up front and fails fast with a 400, but catching a typo in your form gives the applicant instant feedback and avoids a round trip.
  2. PAN with name. It anchors the identity. A valid: false, a non-valid panStatus, or a poor nameMatchResult usually ends the journey or sends it to review.
  3. Bank account with name. Run it only after PAN passes, and compare beneficiaryName and nameMatch against the PAN's registeredName. Disagreement between the two registries is a strong fraud signal.
  4. Secondary identity document (driving licence, voter ID or passport) only when your policy requires it or when the PAN result is ambiguous.
  5. RC only for vehicle-backed products, once the person is otherwise accepted.

The business borrower sequence

  1. Format checks on GSTIN, CIN or Udyam number, whichever the applicant supplied.
  2. The primary registry for the entity type: CIN for companies, GSTIN for GST-registered firms, Udyam for micro and small enterprises.
  3. The authorised signatory's or proprietor's individual journey (PAN, then bank).
  4. pan-to-gstin or pan-to-udyam to cross-check that the registrations you were given belong to the PAN you verified.
  5. The entity's bank account.

Why "cheapest first" is not the whole rule

A cheap check that rarely discriminates (for instance, a driving licence for someone who already passed PAN and bank match) adds latency and cost without changing decisions. Order by expected information gain per rupee, not by price alone. Our business verification guide and the individual endpoint pages show which fields give you the discriminating signal for each product.

How do you control verification cost across a funnel?

Verification spend is a funnel-design problem. Most applications drop off before disbursal, so you want to spend little on the many and more on the few who reach underwriting. Prices are per call and metered after a successful response; the live per-call price for each product is on the pricing page and in the product data.

Current per-call prices

The figures below are resolved from StampMitra's product data when this page is built, so they stay in step with the price list.

CheckPrice per call
PAN₹5
Bank account₹4
GSTIN₹5
CIN₹5
Udyam₹5
Vehicle RC₹9
Driving licence₹5
Voter ID₹5
Passport₹9
PAN to GSTIN₹5
PAN to Udyam₹5

Staged spending

Divide the journey into stages and attach a budget to each. Stage one (lead capture) spends nothing. Stage two (soft application) runs PAN only, which at ₹5 per call is the cheapest decisive check. Stage three (underwriting) runs the bank account at ₹4 and any business registry calls. Stage four (pre-disbursal) re-checks only what must be fresh.

Billing rules that shape your logic

The product docs state that usage is recorded, and the wallet debited for priced services, only after a successful response. The same notes state that negative determinations (valid: false, found: false) are completed and billable. Two consequences follow. First, never loop a "not found" result hoping it flips; a retry buys another answer at full price. Second, a 402 PAYMENT_REQUIRED (insufficient wallet balance) is a platform state you should alert on before it stops a whole funnel.

Avoiding duplicate spend

Cache the result against a normalised key (for example the PAN) inside your own system for the duration your policy allows, and check the cache before calling. Deduplicate concurrent submissions from the same applicant at your gateway. The verify endpoints do not document an idempotency header, and the referenceId is StampMitra's transaction id, not an idempotency key, so deduplication is your job. The sandbox versus production keys guide explains how to test this logic without spending real money.

How do you build the checks into a working onboarding service?

This section walks through a minimal but realistic service. It uses the documented PAN and bank-account endpoints and shows how to turn raw responses into three outcomes.

Step 1: get a key and scopes

Sign up for a sandbox key (prefix sk_test_) through signup; production keys (sk_live_) require a KYC-approved organisation, as described in the authentication docs. Create separate keys per service so each carries only the scopes it needs: verification.pan.read, verification.bank-account.read, and so on. A key missing a scope gets a 403 FORBIDDEN.

Step 2: call PAN with the name the applicant typed

cURL
curl -X POST "https://api-b2b.stampmitra.in/v1/verify/pan" \
  -H "Authorization: Bearer $STAMPMITRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pan":"ABCDE1234F","name":"Rahul Kumar"}'

The documented response shape (sample values) is:

JSON
{
  "referenceId": "SMV-20260927-8F42K7",
  "pan": "ABCDE1234F",
  "type": "Individual",
  "valid": true,
  "registeredName": "RAHUL KUMAR",
  "fatherName": "SURESH KUMAR",
  "panStatus": "VALID",
  "panStatusCode": "E",
  "panStatusDescription": "Existing and valid PAN",
  "nameMatchResult": "MATCH",
  "nameMatchScore": "100",
  "aadhaarSeedingStatus": "y",
  "aadhaarSeedingStatusDescription": "Aadhaar linked",
  "lastUpdatedAt": "2023-04-01",
  "message": "PAN is Valid"
}

Step 3: the Node.js version

JavaScript
// Node 18+ (global fetch). Runs on your server only.
const BASE = "https://api-b2b.stampmitra.in";

async function verify(path, body) {
  const res = await fetch(`${BASE}/v1/verify/${path}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.STAMPMITRA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  const json = await res.json();
  if (!res.ok) {
    const err = new Error(json.error?.message ?? "verification failed");
    err.status = res.status;
    err.code = json.error?.code;
    err.requestId = json.error?.requestId;
    throw err;
  }
  return json;
}

async function checkIndividual(app) {
  const pan = await verify("pan", { pan: app.pan, name: app.fullName });
  if (!pan.valid) return { outcome: "HARD_FAIL", reason: "PAN_INVALID", evidence: [pan] };
  if (pan.nameMatchResult !== "MATCH") {
    return { outcome: "REVIEW", reason: "PAN_NAME_MISMATCH", evidence: [pan] };
  }
  const bank = await verify("bank-account", {
    accountNumber: app.accountNumber, ifsc: app.ifsc, name: app.fullName,
  });
  if (!bank.accountExists) return { outcome: "HARD_FAIL", reason: "ACCOUNT_NOT_FOUND", evidence: [pan, bank] };
  if (!bank.nameMatch) return { outcome: "REVIEW", reason: "BANK_NAME_MISMATCH", evidence: [pan, bank] };
  return { outcome: "PASS", evidence: [pan, bank] };
}

Step 4: the Python version

Python
import os, requests

BASE = "https://api-b2b.stampmitra.in"

class VerifyError(Exception):
    def __init__(self, status, code, message, request_id):
        super().__init__(message)
        self.status, self.code, self.request_id = status, code, request_id

def verify(path, body, timeout=20):
    r = requests.post(
        f"{BASE}/v1/verify/{path}",
        headers={"Authorization": f"Bearer {os.environ['STAMPMITRA_API_KEY']}"},
        json=body, timeout=timeout,
    )
    data = r.json()
    if not r.ok:
        e = data.get("error", {})
        raise VerifyError(r.status_code, e.get("code"), e.get("message"), e.get("requestId"))
    return data

pan = verify("pan", {"pan": "ABCDE1234F", "name": "Rahul Kumar"})
print(pan["referenceId"], pan["valid"], pan["nameMatchResult"])

The PAN in Node.js and Python guide goes further on client structure, and the SDKs page lists what is available today.

Step 5: treat the match fields as inputs, not verdicts

nameMatchResult and nameMatchScore are signals. Your policy decides what counts as a pass. Many teams accept an exact match, send partial matches to review, and reject low scores, but the thresholds belong in your risk policy, not in this guide. Note also that nameMatchScore on the PAN sample is a string and on the bank-account sample a number (0.95), so parse each defensively.

How should a business borrower journey be verified?

Business onboarding needs two separate questions answered: does this entity exist and is it active, and does this person have a real connection to it. The registry endpoints answer the first. The second needs your own evidence, such as board resolutions or authorisation letters, which no verification API replaces.

Company (Pvt Ltd or LLP-style entity with a CIN)

Call cin with the Corporate Identification Number. The documented response includes companyName, incorporationDate, cinStatus, companyStatus and a directors array with din, name and designation. Compare companyName with the name on the application and with the GSTIN legalName. Compare each director's name with the individuals who will sign. Run an individual journey (PAN, bank) for each signatory your policy requires. The response does not include directors' PAN, so do not expect to derive it from the CIN.

cURL
curl -X POST "https://api-b2b.stampmitra.in/v1/verify/cin" \
  -H "Authorization: Bearer $STAMPMITRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cin":"U74999DL2015PTC284009"}'

GST-registered firm

Call gstin with the 15-character number and, optionally, businessName. Documented response fields include GSTIN, valid, legalName, tradeName, registrationStatus and principalPlaceOfBusiness. Note the capitalised GSTIN key in the response, while the request uses lowercase gstin. Registration status is a point-in-time fact; a cancelled or suspended registration is a review trigger.

MSME with Udyam

Call udyam with the registration number. The response has found, udyamStatus, enterpriseName, enterpriseType, organizationType and a certificateUrl. Do not store the certificate URL as proof: store the response and the referenceId.

Cross-checking with PAN

pan-to-gstin returns a gstins array (each with gstin, state, status) for a PAN, and pan-to-udyam returns found, status and udyamNumber. These are useful consistency checks: if an applicant presents a GSTIN that is not among those linked to the verified PAN, that is worth a reviewer's attention. Their prices are ₹5 and ₹5 per call. The GSTIN verification guide covers the registration fields in more depth.

Proprietorships

A proprietor and the business are one legal person, so the PAN is the anchor. Verify the proprietor's PAN and bank account, then use pan-to-gstin and pan-to-udyam to discover and confirm the business registrations rather than relying only on what the applicant types.

How do you handle retries, errors and the manual-review queue?

Reliability design in KYC is mostly about distinguishing three kinds of failure: the applicant gave bad data, the source could not answer, and the answer is ambiguous. Each has a different remedy.

Error codes you will see

Every error is normalised to { "error": { "code", "message", "requestId" } }. The documented cases for verify endpoints are in this table.

HTTPcodeMeaningRetry?Typical action
400BAD_REQUESTInput failed format validationNoAsk the applicant to correct the field
401UNAUTHORIZEDMissing or invalid API keyNoFix configuration; page an engineer
402PAYMENT_REQUIREDInsufficient wallet balanceOnly after top-upAlert finance; queue work
403FORBIDDENKey lacks the scopeNoFix key scopes
429TOO_MANY_REQUESTSRate limitedYes, with backoffSlow down, spread calls
502INTERNAL_ERRORUpstream verification service unreachableYes, boundedRetry, then park for review

The documented 429 example message reads "Rate limit exceeded: 60 requests per 60 seconds for this endpoint". Treat that as an illustration of the error body and check the live limits for your account rather than hard-coding 60. The API errors guide expands on each code.

A bounded retry policy

Retry only 429 and 502, with exponential backoff and jitter, and cap attempts (three is a common choice). Because a successful response is billed whether positive or negative, you only pay for answers, but retrying a timeout can create ambiguity: if your client timed out after the server responded, a retry may bill again. Log the requestId from errors and the referenceId from successes so support can reconcile.

JavaScript
async function withRetry(fn, { tries = 3, base = 500 } = {}) {
  for (let i = 0; ; i++) {
    try { return await fn(); }
    catch (e) {
      const retryable = e.status === 429 || e.status === 502;
      if (!retryable || i >= tries - 1) throw e;
      const wait = base * 2 ** i + Math.random() * 250;
      await new Promise(r => setTimeout(r, wait));
    }
  }
}

What goes to manual review

Create a review case, not a rejection, when: the PAN name match is partial; PAN and bank beneficiary names disagree; a registry returns an inactive or cancelled status; an upstream error persists after retries; or a business registration does not link to the verified PAN. Each case should hold the application id, the reason code, the full evidence list with referenceIds and timestamps, and a due date.

Designing the reviewer's screen

Show the applicant's typed values beside the registry values, highlight mismatches, and offer only a small set of actions: approve with note, request more documents, reject with reason. Record the analyst's identity and time for every action. A reviewer's override is a decision your organisation owns; the API result is merely the evidence they looked at.

How do you do async processing and webhooks for verification?

Verification calls on StampMitra are synchronous request and response. You send a request, you wait for the JSON, you act. There is no documented polling endpoint for verification results and no documented webhook for them. The webhooks page describes order-lifecycle events (such as order.fulfilment_completed) with HMAC-SHA256 signing and exponential-backoff retries, and states that self-serve webhook registration is still "coming soon". Do not design a KYC flow that depends on a verification webhook today.

Make your own flow asynchronous

Even with synchronous calls, you often want the applicant's browser to return quickly. The standard pattern is: the front end submits the application, your API stores it with status PENDING and enqueues a job, a worker runs the verification sequence, and the front end polls your status endpoint or receives your own push or webhook.

ComponentResponsibility
Application APIValidates formats, stores the application, enqueues a job, returns 202 and an application id
Queue (SQS, Pub/Sub, Redis streams or similar)Buffers jobs, enforces concurrency limits to respect rate limits
Verification workerRuns the ordered checks, applies retries, writes evidence
Decision serviceMaps evidence to PASS, HARD_FAIL or REVIEW
Case toolHolds REVIEW items for analysts
NotifierTells the applicant or your CRM about status changes

Concurrency and rate limits

The queue is where you enforce a global ceiling on in-flight verification calls so a campaign spike does not trigger 429s. Partition by endpoint if limits are per endpoint, as the 429 example message suggests.

If you later receive order events

For flows that do use StampMitra orders (for example e-stamping loan agreements after approval), webhook payloads carry orderId, status and occurredAt. Verify the signature, check the timestamp, respond 2xx quickly, and make your handler idempotent because deliveries can be retried. See the e-stamp order flow guide for the order side.

What does a worked example look like from request to decision?

Consider an invented applicant, Rahul Kumar, applying to a fictional lender, Kaveri Finserve, for a small personal loan. The values below are the documentation's sample values, not real data.

The bank-account step

After PAN passes, the worker calls the bank-account endpoint with the same name the applicant typed.

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"}'

The documented response shape is:

JSON
{
  "accountNumber": "000123456789",
  "ifsc": "ICIC0001234",
  "bankName": "ICICI Bank",
  "branchName": "Andheri West",
  "accountExists": true,
  "beneficiaryName": "RAVI KUMAR",
  "nameMatch": true,
  "nameMatchScore": 0.95,
  "accountStatus": "ACTIVE"
}

Reading the two results together

In the PAN sample the registered name is RAHUL KUMAR, while the bank sample's beneficiary is RAVI KUMAR. Those are different documentation samples, but they make a useful teaching case: had one applicant produced both, the bank step might still report nameMatch: true against the typed name "Ravi Kumar" while the PAN says Rahul. Matching each registry against the typed name is not enough. Your decision service should also compare the registry names with each other, and the disagreement should create a review case with reason PAN_BANK_NAME_DIFF.

The decision record

The worker writes one evidence row per call (PAN with its referenceId, bank result with the timestamp and your own correlation id) and the decision service writes a single decision row.

JSON
{
  "applicationId": "app_01",
  "outcome": "REVIEW",
  "ruleVersion": "2026-10-a",
  "reasons": ["PAN_BANK_NAME_DIFF"],
  "evidence": [
    { "product": "pan", "referenceId": "SMV-20260927-8F42K7" },
    { "product": "bank-account", "calledAt": "2026-10-16T09:12:44Z" }
  ]
}

The analyst opens the case, sees both names, asks the applicant for an explanation or a bank statement through your own channel, and records the outcome. Notice what the API contributed (two structured facts with timestamps) and what it did not (the judgement that these two names refer to the same person).

A business variant

For a company applicant the same shape applies: the worker calls cin, compares companyName and directors with the application, then runs PAN and bank journeys for the signatory. If cinStatus or companyStatus is anything other than the active values your policy accepts, the outcome is REVIEW with reason ENTITY_NOT_ACTIVE. If it is active and names align, the decision is PASS for this evidence set, which then feeds your wider policy checks.

What should an architecture for KYC verification look like?

Keep the architecture boring: few components, clear data ownership, and one place where decisions are written. The table below describes the text diagram flow from left to right.

LayerComponentReads fromWrites to
EdgeApplication form / mobile appApplicantYour gateway (HTTPS)
GatewayApplication API with auth and rate limitingForm payloadApplication DB, queue
CoreVerification workerQueue, key vaultEvidence store, decision input
ExternalStampMitra /v1/verify/*Worker requestWorker response
CoreDecision serviceEvidence storeDecision log, case tool
OpsCase-management toolDecision logAnalyst actions log
GovernanceAudit log and retention jobAll of the aboveArchive or deletion

Secrets and keys

The API key lives in a secrets manager and is injected into the worker only. Use a sandbox key in every non-production environment. Rotate keys on a schedule and on staff changes, and restrict sensitive scopes to an IP allowlist where your plan supports it (the auth docs note sensitive scopes can be IP-restricted). The platform's API security policy sets out the provider side of the shared responsibility.

Data model sketch

Keep one verification_check row per call: application id, product slug, request fingerprint (a hash, not the raw number, where possible), referenceId, HTTP status, outcome fields you used, the full response stored in an encrypted column, called_at, and requestId for errors. One decision row per outcome: result, rule version, reasons, actor (system or analyst). This lets you replay a decision under the rule version in force at that time.

Isolating personal data

Put raw identifiers in a restricted store with its own access controls, and let downstream analytics see only tokens. A bug in a dashboard should never be able to leak a PAN.

What audit logging and data retention practices make sense?

Logs are how you prove, months later, what was checked and why a decision was taken. They are also a liability, because they hold personal data. Good practice balances both, and your own legal and compliance advisers decide the actual periods.

What to log

Log who initiated the check (system or user id), the product called, the referenceId, the timestamp, the HTTP status, your decision and the rule version. For errors log the requestId from the error body, which StampMitra support can use to trace the call. Log reviewer actions with identity and time. Do not log full identity numbers in application logs, and never log API keys.

Make audit records tamper-evident

Write decision and reviewer events append-only, with a hash chain or write-once storage, and restrict deletion to a documented retention job. If an auditor asks who changed a decision, the system should answer without relying on anyone's memory.

Retention: the general principle

The DPDP Act 2023 (Act No. 22 of 2023) is built around consent and purpose limitation, and it provides that personal data should be erased once the purpose is served or consent is withdrawn, unless law requires retention (sections 6 and 8 as published; confirm the current text and any rules made under it with counsel). For a lender, the "unless law requires retention" clause matters, because sector rules may require you to keep KYC records for fixed periods. Your retention schedule should therefore be set per record type with a legal basis noted for each, not as one global number.

What StampMitra retains and what you should read

StampMitra's own approach to retaining information processed through the platform, including API logs, request identifiers and verification requests and results, is described in the developer data retention and deletion schedule, which states that retention depends on purpose, law and contract rather than one fixed number of days. Roles and responsibilities when StampMitra processes personal data for you are in the Data Processing Addendum and the developer privacy policy. Read them with your counsel; they are the authoritative statements, not this summary.

Consent capture

Before calling a verification endpoint on a person's data, record what the person was told, what they agreed to, when, and through which screen version. Collect only the fields the check needs. This is a good practice summary, not legal advice.

What does an API check NOT satisfy?

This is the section to show your product manager. A StampMitra verification call confirms that a document number returns a result from a data source and, where you pass a name, how it compares. That is useful evidence. It is not any of the following.

Not regulatory KYC compliance

Regulated entities must follow the KYC rules of their own regulator. For NBFCs, banks and other entities regulated by the Reserve Bank of India, the governing instrument is the Master Direction, Know Your Customer (KYC) Direction, 2016 (RBI/DBR/2015-16/18), issued on 25 February 2016 and updated since; the RBI page showed an update as on 14 August 2025 when we checked on 16 October 2026. It applies to every entity regulated by the RBI, including NBFCs, and covers customer acceptance, risk management, customer identification and transaction monitoring, among other things. Its text, including permitted identification methods, record-keeping and periodic updation, is what you must follow. Entities regulated by SEBI, IRDAI or others have their own rules.

Not a substitute for your KYC policy and officers

The Direction expects a board-approved policy and designated officers inside the regulated entity. An API cannot hold those roles. Whether a particular API result may be used as part of your identification process is a question for your compliance function and, where needed, your legal counsel, and may depend on the exact document and method.

Not video or in-person verification

The RBI Direction describes a video-based customer identification process (V-CIP) with its own minimum standards. StampMitra's verification endpoints do not perform live interviews, liveness checks or face matching.

Not a registry of last resort

A "found" result reflects what the source returned at call time. It does not prove the person in front of you is the person the document describes, it does not establish beneficial ownership, and it does not screen for sanctions, adverse media or politically exposed persons. Those need other controls.

Not a credit or fraud decision

Nothing here predicts repayment, and none of the checks should be the sole basis of an adverse decision against an applicant. Keep a human route for people whose data is wrong at source.

StampMitra is a private LegalTech platform operated by Bani Global Industries LLP. It is not a government agency, a regulator, or a law firm, and nothing on this page is legal advice.

When should you use this approach, and when not?

Use the API-driven pattern where speed and consistency matter and where the checks are evidence inside a larger, policy-governed process. Avoid it where the stakes or the rules demand something else.

SituationFitWhy
Pre-screening a loan application before human reviewGoodCheap early signals cut wasted analyst time
Verifying a payout account before the first disbursalGoodBank-account and name match catch typos and mismatches
Business onboarding with GSTIN, CIN or UdyamGoodRegistry facts are structured and quick to compare
Periodic re-checks of existing borrowersGood, with careRe-run only what must be fresh; mind cost and consent
Your only evidence of identity for a regulated onboardingNot enoughFollow your regulator's rules on identification
Deciding beneficial ownership of a complex groupNot enoughNeeds documents and judgement beyond a registry lookup
Fully automated rejection with no appeal routeAvoidSource data can be wrong; keep a human path
Calls directly from a mobile appAvoidKeys must stay on your server

A short decision checklist

Ask whether the check is decisive for your funnel stage, whether you hold consent for it, whether a cached recent result already exists, and whether a failure has a defined next step. If any answer is no, fix that before calling.

What are the most common mistakes and failure causes?

Most production problems in KYC integrations repeat across teams. Here are the ones to design out early.

Treating "valid" as "KYC done"

Teams wire valid: true straight to an "approved" flag. Instead map it to "evidence satisfied" and let a policy engine decide, with the regulator's rules encoded in policy, not in a boolean.

Retrying negative answers

A valid: false is a completed, billable answer. Looping it burns money and leaves you with several conflicting results. Retry only transport-level problems (429, 502, timeouts).

Case and formatting mismatches

Applicants type lowercase PANs, spaces in IFSC codes, or hyphen variations in Udyam numbers. Normalise before sending: trim, upper-case, strip internal spaces. A 400 such as "pan must match the format ABCDE1234F" is the API telling you the input never reached verification.

Ignoring the response-shape differences

GSTIN returns GSTIN in capitals; the bank-account sample has no referenceId in its documented response while other products do; scores arrive as string in one product and number in another. Write one adapter per product and test it against the documented sample, rather than one generic parser.

Storing too much

Saving whole documents, full responses in logs and raw numbers in analytics tables multiplies breach impact. Store what the decision needs, encrypted, with a retention date.

No handling for wallet exhaustion

A 402 at peak hour stops every application. Monitor balance, alert at a threshold, and park jobs for retry after top-up rather than failing applicants.

Shipping sandbox assumptions to production

Sandbox keys test integration against sandbox data; production keys need an approved organisation. Test failure paths (400, 402, 429, 502) in sandbox by faking responses in your own test doubles, and read the sandbox versus production guide before go-live.

How do you test in sandbox and go to production?

Plan testing in three layers: contract tests, journey tests and failure drills. Sandbox access is free to obtain through self-signup, and the home page describes sandbox pricing as free test-mode calls against sandbox data. Which exact documents behave in which way in sandbox is documented in the product pages and may change, so read them rather than assuming.

Contract tests

For each product, send the sample request from the docs and assert the response has the fields your adapter reads. Pin those samples in your repository, and rerun on a schedule so a change to a response shape shows up in your tests before it shows up in customers' journeys.

Journey tests

Script the end-to-end flows: clean individual pass; PAN name mismatch to review; bank account not found to hard fail; company with inactive status to review; upstream error to retry then review. Assert not only outcomes but the evidence stored and the audit entries written.

Failure drills

Use test doubles to inject 401, 402, 403, 429 and 502 responses and confirm each path behaves as the table above prescribes. Kill the worker mid-job and confirm the job resumes without double-billing your own decisions.

Go-live checklist

  1. Production organisation approved and sk_live_ keys created with minimum scopes.
  2. Keys in a secrets manager, rotation documented, IP restrictions applied where available.
  3. Wallet funded, low-balance alert configured.
  4. Rate-limit ceiling configured in the queue.
  5. Retention job and deletion procedure tested against the retention schedule and your own policy.
  6. Compliance sign-off that the evidence use fits your regulator's rules.
  7. Runbook for support: who to contact at [email protected], what identifiers to quote (requestId, referenceId).

How do you troubleshoot problems in production?

Work from the outside in: is it your input, your credentials, your limits, or the upstream source?

Symptom table

SymptomLikely causeCheck
Repeated 400 on one fieldFormat not normalisedCompare with the format in the product docs; trim and upper-case
401 after a deployWrong or missing key in the environmentConfirm the header Authorization: Bearer <key> and that the key matches the environment (sk_test_ vs sk_live_)
403 on a new productKey lacks the scopeAdd the scope such as verification.cin.read to the key
402 mid-campaignWallet depletedTop up; replay parked jobs
429 burstsConcurrency above the endpoint limitLower worker concurrency; add jitter
502 spikesUpstream source unreachableBackoff, then route to review; keep requestId
Unexpected valid: falseGenuine negative, or applicant typoRe-ask the applicant once; do not loop calls
Name match low for correct personSpelling, initials or transliteration differencesSend to review; do not auto-reject

What to send to support

When you need help, email [email protected] or use the support page, and include the requestId from the error body, the endpoint, the UTC timestamp and the key's environment. Never include full PAN or account numbers in an email; the sample values in the documentation suffice to reproduce shape problems.

Observability

Track per-product call counts, error rates by code, latency percentiles, review-queue size and age, and wallet balance. A sudden rise in the review rate for one product usually means a source-side change or a form bug, and you want to see it within hours.

How StampMitra Helps

StampMitra provides the verification layer described above through one consistent API. Today the verification products available are PAN, bank account, vehicle RC, driving licence, voter ID, passport, GSTIN, CIN and Udyam, plus two resolvers that start from a PAN: PAN to GSTIN and PAN to Udyam. Each is a POST /v1/verify/<product> call with a per-product scope, one authentication scheme, a normalised error body with a requestId, and a referenceId on documented verification responses so you can tie a result to a file. Per-call prices are listed on the pricing page and metered only after a successful response.

You can start without a sales call. Sign up for a sandbox key, follow the quickstart, then read the product page for each check you plan to use, such as bank account or CIN. Sandbox keys begin with sk_test_, and moving to production keys involves a KYC-approved organisation. If you build with an AI coding assistant, the build with AI page, the MCP page and the Postman collection page give you ready starting points.

It is worth being direct about the limits. StampMitra is a private LegalTech platform operated by Bani Global Industries LLP, not a government agency, regulator or law firm. The API gives you checked data points. It does not decide your KYC compliance, run video identification, screen sanctions lists, or take on your regulatory duties, and it does not currently offer webhooks for verification results or self-serve webhook registration. Your compliance team remains the owner of how these data points are used in your KYC policy.

On the roadmap, as plans rather than promises: self-serve webhook endpoint registration, more developer tooling around testing, and wider coverage of related checks. Timelines are not committed, so build on what is documented today. For commercial or enterprise terms, use the contact page. Companion reading: the PAN verification guide, the business verification guide and the error handling guide. Terms for acceptable use are in the API acceptable use policy, and the developer onboarding verification policy explains how production access is approved.

API reference at a glance

PAN: paramsPOST /v1/verify/pan

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.
panrequiredstringPAN, format ABCDE1234F.
namestringName to match against records (max 256 chars).
PAN: responsePOST /v1/verify/pan
{
  "referenceId": "SMV-20260927-8F42K7",
  "pan": "ABCDE1234F",
  "type": "Individual",
  "valid": true,
  "registeredName": "RAHUL KUMAR",
  "fatherName": "SURESH KUMAR",
  "panStatus": "VALID",
  "panStatusCode": "E",
  "panStatusDescription": "Existing and valid PAN",
  "nameMatchResult": "MATCH",
  "nameMatchScore": "100",
  "aadhaarSeedingStatus": "y",
  "aadhaarSeedingStatusDescription": "Aadhaar linked",
  "lastUpdatedAt": "2023-04-01",
  "message": "PAN is Valid"
}
PAN: pricePOST /v1/verify/pan

PAN: ₹5 per successful call, pay-as-you-go (lower on Growth and Scale).

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).

Vehicle RC: paramsPOST /v1/verify/rc

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.
vehicleNumberrequiredstringVehicle registration number.
Vehicle RC: responsePOST /v1/verify/rc
{
  "referenceId": "SMV-20260927-3D71Q9",
  "regNo": "MH12AB1234",
  "registrationStatus": "VALID",
  "ownerName": "RAHUL KUMAR",
  "ownerFatherName": "SURESH KUMAR",
  "vehicleClass": "LMV",
  "vehicleManufacturerName": "MARUTI SUZUKI",
  "model": "SWIFT",
  "vehicleColor": "WHITE",
  "chassis": "MA3ERLF1S00123456",
  "engine": "K12MN1234567",
  "registrationDate": "2019-05-14",
  "rcStatus": "ACTIVE",
  "rcExpiryDate": "2034-05-13",
  "insuranceUpto": "2025-05-13",
  "isCommercial": false
}
Vehicle RC: pricePOST /v1/verify/rc

Vehicle RC: ₹9 per successful call, pay-as-you-go (lower on Growth and Scale).

Frequently asked questions

Does using a KYC API make my NBFC KYC compliant?

No. An API check is evidence you attach to a customer file, not regulatory KYC compliance by itself. NBFCs must follow the RBI Know Your Customer Direction, 2016 as updated, and decide with their compliance team how each API result fits their policy.

In what order should I run PAN, bank account and other checks for an individual borrower?

Validate formats locally, then verify PAN with the applicant's name, then the bank account with name match, and add a secondary identity document only if your policy or an ambiguous PAN result requires it. RC applies only to vehicle-backed loans.

Which StampMitra checks help onboard a business borrower?

For businesses you can use the CIN, GSTIN and Udyam endpoints for entity facts, the PAN to GSTIN and PAN to Udyam resolvers to cross-check registrations, and the individual PAN and bank journeys for proprietors or signatories.

Should I retry a verification that returned valid false or found false?

No. StampMitra documents that negative determinations are completed, billable verifications, so retrying just pays again for the same answer. Retry only transport problems such as 429 rate limits or 502 upstream errors, with backoff and a cap.

How do I keep verification cost under control in a lending funnel?

Stage your spend: run free format validation first, then PAN, then the bank account only for applicants who pass, and business registry calls only at underwriting. Cache results inside your system and deduplicate concurrent submissions. Check live per-call prices on the pricing page.

Does StampMitra send webhooks when a verification finishes?

No. Verification endpoints are synchronous request and response. The webhooks page documents order-lifecycle events and says self-serve webhook registration is coming soon, so build your own queue and status updates for asynchronous onboarding flows.

What should go into the manual-review queue?

Send cases with partial name matches, PAN and bank name disagreements, inactive registry statuses, persistent upstream errors or registrations that do not link to the verified PAN. Attach all evidence with reference ids and timestamps, and record the analyst's decision.

What should I log, and how long should I keep verification data?

Log the product called, referenceId, timestamp, status, decision, rule version and reviewer actions, not raw identity numbers or keys. Set retention per record type with a legal basis, guided by the DPDP Act 2023, sector rules, and StampMitra's retention schedule and DPA.

Can I call the verification API from my mobile app?

You should not. API keys carry scopes that spend money, so calls belong on your backend with the key in a secrets manager. Your app talks to your own API, which queues the verification work and returns status.

Sources