A multi-tenant gateway that lets you hand out ONE base URL to any partner or mobile app. They call third-party services (PAN, bank, CRIF, Aadhaar, Account Aggregator…) by just changing the path — your real vendor credentials never leave this server.
The Hub sits between your consumers (mobile app, partner companies) and the third-party vendors you pay for (Signzy, CRIF, Finduit, etc.).
[ Mobile app / Partner ] --x-api-key--> [ API HUB ] --vendor creds--> [ Signzy / CRIF / Finduit ]
│
MySQL: companies, keys,
usage, quotas, alerts
A consumer makes one request:
POST https://your-domain/gw/pan
x-api-key: <the company's key>
Content-Type: application/json
{ "panNumber": "ABCDE1234F" }
The Hub then:
pan) → its vendor (signzy), base URL, path and default body.cd api-hub
cp .env.example .env # fill in DB + admin + vendor creds
npm install
npm start # boots, auto-creates the DB + tables, seeds vendors/services
Open http://localhost:6200/ and log in with the ADMIN_USERNAME / ADMIN_PASSWORD from your .env. Change the password immediately under Settings.
DB_NAME) and all tables are created automatically on first boot. Vendor credentials from .env are seeded once, then managed from the panel.Seven sections in the sidebar:
Add a company → you get its first API key instantly. Copy it and hand it over — it goes in the x-api-key header of every request.
Vendor = a third-party provider (Signzy, Finduit, MoneyOne). It holds the base URL + credentials + an auth type that decides how creds are injected:
| Auth type | How creds are sent |
|---|---|
signzy | Authorization: <authToken> |
finduit | dynamic Bearer token (auto-fetched from the auth endpoint) |
moneyone | headers: x-client-id, x-client-secret, x-organisation-id, x-app-identifier |
Service = one callable endpoint (the URL end). It points at a vendor, has a path, an optional base-URL override, and a default_body (static fields merged into every request). Change a base URL or path here anytime — it takes effect within ~15 seconds (catalog cache).
Add a new third-party API in seconds: Services → “+ Add service” → pick a vendor, set the path. Done — it's live at /gw/<your-key>.
By default every company uses the global vendor config. If one company needs something different, open the company → “Add override”:
Resolution order: service override → service config → vendor override → vendor default.
Each company has a daily and monthly limit (0 = unlimited). When exceeded, requests get 429 Quota exceeded and a quota_breach alert can fire. Usage counters roll over automatically by calendar day/month.
Channel is email (needs SMTP in .env) or webhook (any URL — receives a JSON POST). Alerts are throttled to once per window to avoid spam.
Hand your partner exactly this: the base URL, their API key, and the service list below. Every call is POST, JSON, with the key header.
curl -X POST https://your-domain/gw/pan \
-H "x-api-key: THEIR_KEY" \
-H "Content-Type: application/json" \
-d '{"panNumber":"ABCDE1234F"}'
Response shape is always:
{ "success": true, "service": "pan", "data": { ...vendor's raw response... } }
Errors: 401 bad/missing key, 403 disabled, 429 quota, 404 unknown service, 503 service not configured, 502 upstream failure.
Every company also gets its own login-protected portal at /portal (separate from the admin panel). There they can, for themselves only:
The admin invites a portal user from the company's page (“+ Invite user”), which generates a temporary password to hand over. Portal users only ever see their own company's data.
| Path | Vendor | Purpose |
|---|---|---|
/gw/pan | Signzy | PAN verification |
/gw/bank | Signzy | Bank account verification |
/gw/penny-drop | Signzy | Penny-drop verification |
/gw/digilocker-create | Signzy | Create Aadhaar Digilocker URL |
/gw/digilocker-eaadhaar | Signzy | Fetch eAadhaar |
/gw/crif | Signzy | CRIF credit bureau report |
/gw/bureau-encrypt | Signzy | Bureau consent — encrypt |
/gw/bureau-consent | Signzy | Bureau consent — create |
/gw/bureau-decrypt | Signzy | Bureau consent — decrypt |
/gw/aa-auth | Finduit | AA authenticate |
/gw/aa-redirect | Finduit | AA consent redirect URL |
/gw/aa-consent-data | Finduit | AA fetch consent data |
/gw/aa-fi-data | Finduit | AA fetch financial statements |
Live list (with per-company status) is always available at GET /gw/_health and inside the panel under Services.
Every call is POST, JSON, with the x-api-key header. Below, replace YOUR_KEY with the company's key and use your own domain instead of localhost:6200. The response is always wrapped as { "success", "service", "data" } where data is the vendor's raw response.
/gw/pancurl -X POST http://localhost:6200/gw/pan \
-H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{"panNumber":"ABCDE1234F"}'
Body: panNumber (10-char PAN). Returns name, DOB, status, address.
/gw/bankcurl -X POST http://localhost:6200/gw/bank \
-H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{"beneficiaryAccount":"1234567890","beneficiaryIFSC":"HDFC0000001","beneficiaryName":"John Doe"}'
Body: beneficiaryAccount, beneficiaryIFSC (required); beneficiaryName, beneficiaryMobile (optional, enable name match).
/gw/penny-dropcurl -X POST http://localhost:6200/gw/penny-drop \
-H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{"beneficiaryAccount":"1234567890","beneficiaryIFSC":"HDFC0000001"}'
Same body as bank verify. Does a ₹1 credit to confirm the account is live; returns beneficiary name + RRN.
Step 1 — create the consent URL (/gw/digilocker-create):
curl -X POST http://localhost:6200/gw/digilocker-create \
-H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{
"signup": false,
"callbackUrl": "https://your-domain/api/digilocker/webhook",
"successRedirectUrl": "https://your-domain/digilocker/close",
"failureRedirectUrl": "https://your-domain/digilocker/close?status=failed",
"docType": ["ADHAR"],
"purpose": "kyc",
"getScope": true,
"internalId": "user-123",
"getEAadhaarPdf": true,
"getEAadhaarJpeg": true,
"pinlessAuthentication": true
}'
Returns { data: { result: { url, requestId } } }. Open url in a WebView / browser for the user to give Aadhaar consent. Save requestId.
Step 2 — fetch eAadhaar after consent completes (/gw/digilocker-eaadhaar):
curl -X POST http://localhost:6200/gw/digilocker-eaadhaar \
-H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{"requestId":"<requestId from step 1>"}'
Returns name, DOB, gender, address, photo, and the eAadhaar PDF/JPEG. Call it only after the user finishes step 1 (else you'll get a "pending" response).
url (in-app browser / WebView) and comes back via a deep link to your successRedirectUrl. The Hub just brokers the two API calls./gw/crifServed via Signzy's bureau endpoint (/api/v3/bureau/crif); the Hub injects the shared Signzy Authorization token. Body:
{
"phoneNumber": "9999999999",
"pan": "ABCDE1234F",
"firstName": "First",
"lastName": "Last",
"dateOfBirth": "1990-01-01", // yyyy-mm-dd
"pincode": "641001",
"gender": "Male", // Male / Female
"address": "Coimbatore",
"consent": {
"consentFlag": true,
"consentTimestamp": 1690215946415,
"consentIpAddress": "192.168.0.1",
"consentMessageId": "CM_1"
}
}
/gw/aa-redirect — create a consent + get the AA redirect URL (user approves bank-data sharing)./gw/aa-consent-data — poll/fetch consent status with the consent handle./gw/aa-fi-data — fetch the financial statements once consent is active.Bodies pass through to Finduit (txnid / consentId / sessionId); the Hub auto-fetches the Finduit bearer token for you.
The final bank-statement (FI) data is pushed by Finduit, not returned synchronously. Register this single URL with Finduit as your FIU callback:
<APP_PUBLIC_URL>/hooks/finduit (optionally protect with FINDUIT_WEBHOOK_TOKEN → sent as header x-webhook-token).
How it routes to the right consumer, with no data loss:
/gw/aa-redirect / /gw/aa-fi-data, the Hub records the returned consentHandle / txnid / sessionId against that company (aa_sessions)./hooks/finduit. The Hub correlates the payload's ids back to the owning company, ACKs Finduit instantly, and enqueues a delivery.Delivery signature: header X-Hub-Signature: sha256=<hmac> where the HMAC-SHA256 is over <X-Hub-Timestamp>.<raw body> using the company's webhook_secret. Failed deliveries retry automatically (30s → 1h backoff) and are visible in the portal.
.env out of git. Set a strong SESSION_SECRET.# run under a process manager
pm2 start src/server.js --name api-hub
# nginx: expose the gateway + panel on your domain
location / {
proxy_pass http://127.0.0.1:6200;
proxy_set_header X-Forwarded-For $remote_addr;
}
Consumers then use https://your-domain/gw/<service>. The panel is at https://your-domain/ (protect it — restrict by IP or keep it on an internal host if possible).
Agar vendor already hai (e.g. Signzy): Services → “+ Add service”, path daal do. Naya vendor hai: pehle DB me vendor add karo (ya code me auth builder), phir service.
Uski key “Revoke” karo, ya poori company “disabled” kar do. Turant band.
Vendors ya Services me base URL edit karo — ~15 sec me live. Kisi ek company ke liye alag chahiye to uska override use karo.
Dashboard → Top companies, ya company kholo → “Usage by service”. Detailed row-level: Logs (+ CSV export).