---
title: "Node.js SDK"
description: "Backend-only Node.js client for every EPS API, with HMAC signing, input validation and typed errors built in."
canonical: "https://eps.eko.in/docs/sdk/nodejs"
---


> **Canonical URL:** https://eps.eko.in/docs/sdk/nodejs
> This is a machine-readable Markdown version of the page for AI agents and LLMs. The primary (HTML) version lives at the canonical URL above.

# Node.js SDK

`@ekoindia/eps-sdk` is a thin, dependency-free client for every EPS API. One
generic `call(slug, params)` covers all of them: the endpoint catalog, each
endpoint's params and which of them are required are baked into the package from
the same surface these docs are built from, so the client validates your input
**before** it signs and sends anything.

> Backend only. `accessKey` signs every request — it must never reach a browser,
> a mobile app, or any bundle a user can read. The constructor throws if it
> detects a `window` global, but that guard is a safety net, not a strategy.

## Install

```bash
npm i @ekoindia/eps-sdk
```

Registry: npm — https://www.npmjs.com/package/@ekoindia/eps-sdk

| Fact | Value |
| --- | --- |
| Package | `@ekoindia/eps-sdk` |
| Requires | Node.js 18 or newer |
| Dependencies | None — standard library only (`node:crypto`, global `fetch`). |
| Source | https://github.com/ekoindia/eps-platform/tree/main/packages/sdk-js |


## Your first call

Set the two keys from your secret store, pick an environment, and call an
endpoint by its slug:

```javascript
import { EpsClient } from "@ekoindia/eps-sdk";

const client = new EpsClient({
  developerKey: process.env.EPS_DEVELOPER_KEY,
  accessKey: process.env.EPS_ACCESS_KEY,
  initiatorId: "9962981729",
  environment: "sandbox",
});

const result = await client.call("pan-lite", {
  "pan_number": "ABCDE1234F",
  "name": "Rajesh Kumar",
  "dob": "1994-08-29"
});
console.log(result);
```

`initiatorId` and `userCode` are near-constant per developer, so set them once
on the client. They are injected into every call as the wire params
`initiator_id` and `user_code` — pass either in `params` to override it for a
single call, or pass `null` to clear it.

## Client options

| Option | Type | Required | Notes |
| --- | --- | --- | --- |
| `developerKey` | `string` | Yes | Your EPS developer key, sent as the `developer_key` header. |
| `accessKey` | `string` | Yes | Server-side secret used to sign every request. Never ships to a browser. |
| `environment` | `"sandbox" \| "production"` | Yes | Selects the base URL from the embedded surface. |
| `initiatorId` | `string` | No | Default `initiator_id` (registered mobile of the API user) injected into every call. |
| `userCode` | `string` | No | Default `user_code` (retailer/agent code) injected into every call. |
| `timeoutMs` | `number` | No | Aborts a request that takes longer. Named for its unit — Python and PHP use seconds. (milliseconds, default 30_000) |
| `fetch` | `typeof fetch` | No | Inject a custom fetch implementation (proxies, tests). |

## API surface

The SDK is deliberately small. There is no method per endpoint — `call()` takes
the slug, and the baked surface supplies the method, path, and validation rules.

| Member | Kind | Signature | What it does |
| --- | --- | --- | --- |
| `EpsClient` | class | `new EpsClient(options: EpsClientOptions)` | The client. Throws immediately if constructed where `window` exists — `accessKey` must never reach a browser. |
| `call` | method | `client.call<T = unknown>(slug: string, params?: Record<string, unknown>): Promise<T>` | Validates, signs and sends one endpoint call; resolves with the decoded envelope. |
| `signSecretKey` | function | `signSecretKey(accessKey: string, timestamp: string): string` | The raw signing primitive, exported for debugging. `call()` applies it for you. |
| `MULTIPART_JSON_FIELD` | constant | `MULTIPART_JSON_FIELD = "form-data"` | Name of the single form field carrying the JSON envelope on file-upload endpoints. |
| `EpsClientOptions` | type | `interface EpsClientOptions` | The constructor options above. |
| `SdkEndpoint / SdkParam` | type | `interface SdkEndpoint, interface SdkParam` | Shape of one endpoint in the embedded surface (slug, method, path, params). |

## Authentication

You never compute a signature yourself. On every request the client derives
`secret-key = base64(HMAC-SHA256(timestamp, base64(accessKey)))` and sends it
with `secret-key-timestamp` and `developer_key`. `signSecretKey` is exported
only so you can reproduce a signature while debugging a `403`.

## File uploads

A single `type: "file"` param flips the whole request to `multipart/form-data`.
You still pass every parameter flat; on the wire the SDK packs them the way the
API expects — one form field named `form-data` holding all the non-file params
as a single JSON object, plus one part per upload. A `null` param is dropped (a
form field has no null encoding), while a `null` nested inside an object value
is preserved.

File params accept:

- A local file path (read from disk; the filename is the basename)
- A `Blob` or `File` (a `File` keeps its own name)

## Errors and timeouts

A non-2xx response **throws** — an auth or infrastructure failure is never
returned as if it were a result. The decoded envelope is still on the error, so
you can read `status` and `message` off it.

| Type | Raised when | Fields |
| --- | --- | --- |
| `EpsHttpError` | Any non-2xx response. | `status`, `url`, `body` (decoded envelope or null), `raw` |
| `EpsError` | Unknown slug, missing required param, wrong param type, bad option, or a 2xx body that is not JSON. `EpsHttpError` extends it. | — |
| `TimeoutError (DOMException)` | The request exceeded `timeoutMs`. Surfaced raw, as Node reports it. | — |

| HTTP status | Meaning |
| --- | --- |
| 200 | OK — response returned by our system. |
| 403 | Forbidden — incorrect secret-key or timestamp. |
| 404 | Not Found — wrong request URL. |
| 405 | Method Not Allowed — incorrect HTTP method. |
| 415 | Unsupported Media Type — wrong Content-Type header. |
| 500 | Internal Server Error — connectivity or URL misconfiguration. |


## Environments

Switch with the `environment` option; there is no base-URL override.

| Environment | Base URL | Notes |
| --- | --- | --- |
| `sandbox` (UAT / Sandbox) | https://staging.eko.in/ekoapi/v3 | Self-serve credentials available immediately on signup. |
| `production` (Production) | https://api.eko.in/ekoicici/v3 | Credentials issued after organizational KYC. |

## Node-specific notes

- ESM only (`"type": "module"`). Use `import`, or `await import()` from CommonJS.
- A per-call cancellation signal is not supported yet — `timeoutMs` is the only abort source.

## Every endpoint

`call()` accepts every slug in the EPS catalog. Each endpoint's reference page
shows its parameters, response fields and a ready-to-paste Node.js snippet.

[Browse the API reference](/docs)
