StampMitraDevelopers
आधिकारिक SDK

Node.js / TypeScript SDK

StampMitra Developer API का आधिकारिक Node.js और TypeScript क्लाइंट: typed responses, अपने आप retries और signed-webhook helpers के साथ identity verification और e-Stamp।

Pre-release 0.1.0 - package registry पर प्रकाशन जारी है

Package
@stampmitra/sdk
संस्करण
0.1.0
आवश्यकता
Node.js 18 या उससे नया (global fetch का उपयोग करता है)। Core Bun, Deno और Cloudflare Workers पर भी चलता है। कोई runtime dependency नहीं, ESM/CJS दोनों, पूरी तरह typed।
लाइसेंस
MIT
Changelog
CHANGELOG.md
इंस्टॉल

Package इंस्टॉल करें

npm
npm install @stampmitra/sdk

प्रकाशित होने के बाद उपलब्ध। अभी npm पर प्रकाशित नहीं हुआ है। यह कमांड package के रिलीज़ होने के बाद काम करेगा; तब तक रिलीज़ के लिए GitHub repository देखते रहें।

Quickstart

पाँच पंक्तियों में PAN verify करें

बनाते समय sandbox key (sk_test_...) इस्तेमाल करें। यह मुफ़्त है, आपके वॉलेट से कुछ नहीं कटता और sandbox डेटा पर चलती है।

TypeScript
import { StampMitra } from '@stampmitra/sdk';

const client = new StampMitra(); // reads STAMPMITRA_API_KEY
const res = await client.verify.pan({ pan: 'ABCDE1234F', name: 'Rahul Kumar' });
console.log(res.referenceId, res.valid, res.nameMatchResult);
Authentication

API keys और environments

Sandbox

Sandbox keys sk_test_ से शुरू होती हैं। ये मुफ़्त हैं और sandbox डेटा लौटाती हैं।

Live

Live keys sk_live_ से शुरू होती हैं। इनके लिए स्वीकृत production access request चाहिए और हर सफल कॉल पर शुल्क लगता है।

apiKey दें या STAMPMITRA_API_KEY environment variable सेट करें। client.environment का मान 'test' या 'live' होता है। जो key ^sk_(test|live)_[A-Za-z0-9_-]{8,}$ से मेल नहीं खाती वह अस्वीकार होती है, जब तक आप allowCustomKey: true न दें। Browser runtime में constructor sk_live_ keys को अस्वीकार करता है और test keys पर चेतावनी देता है।

सिर्फ़ server-side। sk_ keys को कभी browser या mobile ऐप में न डालें और न ही किसी repository में commit करें। इन्हें environment variables या secrets manager में रखें। Authentication
Client options
new StampMitra({
  apiKey,
  baseUrl: 'https://api-b2b.stampmitra.in',
  timeout: 30_000,
  maxRetries: 2,
  fetch,
  defaultHeaders,
  appInfo: { name: 'my-app', version: '1.0.0', url: 'https://example.com' },
  logger,
  validateInput: true,
  allowCustomKey: false,
});

// Every method also takes per-request options as its last argument:
// { timeout, maxRetries, idempotencyKey, headers, signal }
Resources

हर resource एक जगह

Response bodies API के उदाहरणों से typed हैं। जिन payloads का आकार API ने अभी तय नहीं किया (e-Stamp details, duty, parties, payment intent) वे खुले objects के रूप में typed हैं।

All resources
// Verification (POST /v1/verify/*). Every call, including negative results, is billed.
await client.verify.pan({ pan: 'ABCDE1234F' });
await client.verify.gstin({ gstin: '07AAAAA1111A1Z1', businessName: 'Jane Doe Enterprises' });
await client.verify.rc({ vehicleNumber: 'MH12AB1234' });
await client.verify.voterId({ voterId: 'ABC1234567' });
await client.verify.drivingLicense({ dlNumber: 'MH1420110012345', dob: '1990-01-01' }); // IP-allowlisted key
await client.verify.passport({ fileNumber: 'PA1079341954215', dob: '1990-01-01' }); // IP-allowlisted key
await client.verify.cin({ cin: 'U74999DL2015PTC284009' });
await client.verify.udyam({ udyamNumber: 'UDYAM-MH-12-1234567' });
await client.verify.panToGstin({ pan: 'ABCDE1234F' });
await client.verify.panToUdyam({ pan: 'ABCDE1234F' });
await client.verify.bankAccount({ accountNumber: '000123456789', ifsc: 'ICIC0001234', name: 'Ravi Kumar' });

// Services and e-Stamp reference data
await client.services.list();
await client.services.get('estamp.instrument.rent_agreement');
await client.estamp.getSchema('estamp.instrument.rent_agreement');
await client.estamp.listStates();
await client.estamp.listInstruments('MH');
await client.estamp.listArticles('INSTRUMENT_CODE');
await client.estamp.calculateDuty({ /* see e-Stamp docs */ });

// Orders and payments
const order = await client.orders.create({
  externalOrderId: 'o1',
  externalCustomerId: 'c1',
  serviceCode: 'estamp.instrument.rent_agreement',
  details: {},
});
await client.estamp.addParties('ORDER_ID', { /* parties */ });
await client.orders.get('ORDER_ID');
await client.orders.getStatus('ORDER_ID');
await client.orders.getPayment('ORDER_ID');
await client.orders.listDocuments('ORDER_ID');
await client.orders.getDocumentDownload('ORDER_ID', 'DOCUMENT_ID'); // short-lived signed URL
await client.payments.intents.create({ /* see payment docs */ });
await client.payments.intents.get('INTENT_ID');

// Usage, logs, webhook deliveries
await client.usage.summary();
await client.logs.list({ service: 'estamp', status: 'failure', limit: 50 });
await client.webhooks.events.list({ limit: 20 });
await client.webhooks.events.replay('EVENT_ID');

// Raw response access
const { data, response } = await client.verify.pan({ pan: 'ABCDE1234F' }).withResponse();
console.log(response.status, response.requestId, response.attempts);
Pagination

Pagination

listAll आपके लिए nextCursor को आगे बढ़ाता है; list items और nextCursor वाला एक पेज लौटाता है।

Pagination
for await (const entry of client.logs.listAll({ service: 'estamp' })) console.log(entry.id);

const page = await client.logs.list({ limit: 50 }); // page.items, page.nextCursor

for await (const delivery of client.webhooks.events.listAll()) {
  // ...
}
Errors

Error classes

हर error StampMitraError से बना है और status, code, message, requestId व raw देता है। ValidationError में field और RateLimitError में retryAfter (सेकंड) जुड़ता है। Errors सिर्फ़ HTTP status से map होते हैं, इसलिए non-JSON error body पर भी सही class मिलती है।

HTTP statusClassटिप्पणी
400 / 422ValidationErrorगलत इनपुट। Validation helpers इसे request भेजने से पहले local रूप से भी उठाते हैं।
401AuthenticationErrorAPI key न मिली, गलत है या रद्द हो चुकी है।
402PaymentRequiredErrorवॉलेट में बैलेंस या क्रेडिट कम है।
403PermissionErrorसर्वर का संदेश जस का तस दिया जाता है, जैसे Passport और Driving Licence के लिए IP-allowlisted key चाहिए।
404NotFoundErrorऑर्डर, सेवा या इवेंट की id मौजूद नहीं है।
409ConflictErrorIdempotency-Key किसी दूसरी body के साथ दोबारा इस्तेमाल हुई, या state में टकराव है।
429RateLimitErrorरुककर दोबारा कोशिश करें; सर्वर Retry-After भेजे तो वह मान उपलब्ध रहता है।
5xxUpstreamErrorसर्वर या upstream की ओर की विफलता। 502, 503 और 504 पर अपने आप retry होता है।
network / timeout / abortConnectionError / TimeoutError / RequestAbortedErrorकोई उपयोगी response नहीं मिला: कनेक्शन विफल, timeout या cancel।
TypeScript
import { ValidationError, RateLimitError } from '@stampmitra/sdk';

try {
  await client.verify.pan({ pan: 'ABCDE1234F' });
} catch (e) {
  if (e instanceof ValidationError) console.error('Bad input:', e.field, e.message);
  else if (e instanceof RateLimitError) console.log(e.retryAfter, e.requestId);
  else throw e;
}
Retries और idempotency

Retries और idempotency

Validation

क्लाइंट-साइड validation

इनपुट trim और upper-case होकर API के formats के विरुद्ध local रूप से जाँचे जाते हैं (PAN, GSTIN, IFSC, CIN, Udyam, vehicle number, 9 से 18 अंकों का bank account, passport file number, driving licence, voter ID, YYYY-MM-DD तारीखें, नाम की लंबाई)। गलत इनपुट पर ValidationError (local: true) उठता है, बिना network call और बिना शुल्क के। इसे validateInput: false से बंद करें।

Webhooks

Webhook signatures जाँचें

हर डिलीवरी में X-Webhook-Signature, X-Webhook-Timestamp (Unix सेकंड) और X-Webhook-Event-Id होते हैं। Signature, timestamp, एक बिंदु और raw body का hex HMAC-SHA256 है, जो आपके endpoint secret से बनता है। जाँच हमेशा raw body पर करें, दोबारा serialise किए गए JSON पर नहीं।

दो entry points हैं: @stampmitra/sdk/webhooks async है और WebCrypto का उपयोग करता है (Next.js, Cloudflare Workers, Node 18+), जबकि @stampmitra/sdk/webhooks-node synchronous Node संस्करण है। पुराने timestamps (डिफ़ॉल्ट tolerance 300 s) अस्वीकार होते हैं और तुलना constant-time में होती है।

TypeScript
// Edge runtimes, Next.js, Node 18+ (async, WebCrypto)
import { constructEventFromHeaders } from '@stampmitra/sdk/webhooks';
const event = await constructEventFromHeaders(rawBody, request.headers, secret);

// Synchronous Node variant
import { verifySignature, constructEvent } from '@stampmitra/sdk/webhooks-node';
const ok = verifySignature(rawBody, timestampHeader, signatureHeader, secret); // boolean

verifySignature boolean लौटाता है और गलत इनपुट पर कभी exception नहीं उठाता। constructEvent WebhookSignatureError उठाता है, जिसका reason malformed, stale या mismatch होता है।

Framework के उदाहरण

Framework के उदाहरण
import express, { type Request, type Response } from 'express';
import { constructEvent, WebhookSignatureError } from '@stampmitra/sdk/webhooks-node';

const app = express();

// IMPORTANT: verify against the RAW body, not re-serialised JSON.
app.post(
  '/webhooks/stampmitra',
  express.raw({ type: 'application/json' }),
  (req: Request, res: Response) => {
    try {
      const event = constructEvent(
        req.body as Buffer,
        String(req.header('X-Webhook-Timestamp')),
        String(req.header('X-Webhook-Signature')),
        process.env.STAMPMITRA_WEBHOOK_SECRET!,
      );
      console.log('event', req.header('X-Webhook-Event-Id'), event);
      res.sendStatus(200);
    } catch (err) {
      if (err instanceof WebhookSignatureError) return void res.status(400).send(err.reason);
      throw err;
    }
  },
);

app.listen(3000);

Webhooks: इवेंट के प्रकार, payload और retry नीति Webhooks पेज पर हैं।

सुरक्षित logging

सुरक्षित logging

डिफ़ॉल्ट रूप से कुछ भी log नहीं होता। Request और response के records पाने के लिए logger: { debug(msg, meta) {} } दें; इनमें Authorization और x-api-key redact रहते हैं और PAN, GSTIN, account numbers व नाम masked रहते हैं।

SDKs

इंटीग्रेट करने के दूसरे तरीके

REST API किसी भी भाषा से चलती है, और बाकी SDKs भी उसी contract पर बने हैं।

API कॉल करने के लिए तैयार हैं?

मुफ़्त sandbox key लें और ऊपर का quickstart लगभग 5 मिनट में चलाएँ।