---
title: "Java SDK"
description: "Backend-only Java client for every EPS API, published from a git tag via JitPack, with HMAC signing built in."
canonical: "https://eps.eko.in/docs/sdk/java"
---


> **Canonical URL:** https://eps.eko.in/docs/sdk/java
> 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.

# Java SDK

`com.github.ekoindia:eps-sdk-java` is a client for every EPS API built on the
JDK's own `java.net.http`. One generic `call(slug, params)` covers all of them:
the endpoint catalog, each endpoint's params and which of them are required are
packaged 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 ship in an Android
> app or any artifact a user can decompile.

## Install

```bash
com.github.ekoindia:eps-sdk-java
```

Registry: JitPack — https://jitpack.io/#ekoindia/eps-sdk-java

| Fact | Value |
| --- | --- |
| Package | `com.github.ekoindia:eps-sdk-java` |
| Requires | Java 17 or newer |
| Dependencies | One: Gson, because Java has no JSON parser in the standard library. HTTP uses the JDK's own `java.net.http`. |
| Source | https://github.com/ekoindia/eps-platform/tree/main/packages/sdk-java |

- Published through JitPack from a git tag — add the JitPack repository alongside Maven Central.
- Gradle: `maven { url 'https://jitpack.io' }`, then `implementation 'com.github.ekoindia:eps-sdk-java:<tag>'`.
- Maven: a `<repository>` with id `jitpack.io` and url `https://jitpack.io`, then the `com.github.ekoindia:eps-sdk-java` dependency.


## Your first call

Read the two keys from the environment, pick an environment, and call an
endpoint by its slug:

```java
import in.eko.eps.EpsClient;
import java.util.Map;

EpsClient client = EpsClient.builder()
    .developerKey(System.getenv("EPS_DEVELOPER_KEY"))
    .accessKey(System.getenv("EPS_ACCESS_KEY"))
    .initiatorId("9962981729")
    .environment("sandbox")
    .build();

Map<String, Object> result = client.call("pan-lite", Map.of(
        "pan_number", "ABCDE1234F",
        "name", "Rajesh Kumar",
        "dob", "1994-08-29"
    ));
System.out.println(result);
```

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

## Client options

The client is built through a fluent builder; every option below is a builder
method.

| 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. |
| `.httpClient(…)` | `HttpClient` | No | Control timeouts, proxies or redirects by supplying your own client. (default: 30s connect + 30s per request) |

## API surface

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

| Member | Kind | Signature | What it does |
| --- | --- | --- | --- |
| `in.eko.eps.EpsClient` | class | `EpsClient.builder()…build()` | The client, built through a fluent builder. |
| `call` | method | `client.call(String slug, Map<String, Object> params): Map<String, Object>` | Validates, signs and sends one endpoint call; returns the decoded envelope. |
| `resolveTarget` | method | `client.resolveTarget(String slug, Map<String, Object> params): Target` | The signed request for a call, without sending it. |
| `buildHeaders` | method | `client.buildHeaders(boolean multipart): Map<String, String>` | The four auth headers for a single request. |
| `sign` | method | `EpsClient.sign(String accessKey, String timestamp): String` | Static. The raw signing primitive, exposed for debugging. |
| `EpsClient.EpsFile` | type | `record EpsFile(String name, byte[] content)` | An in-memory upload. |
| `EpsClient.MULTIPART_JSON_FIELD` | constant | `MULTIPART_JSON_FIELD = "form-data"` | Name of the single form field carrying the JSON envelope on file-upload endpoints. |

## 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`. `EpsClient.sign` is public 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 a map value is
preserved.

File params accept:

- A `String` path, a `java.nio.file.Path`, or a `java.io.File`
- An `EpsClient.EpsFile(name, content)` for an in-memory upload

## Errors and timeouts

Every failure is an unchecked exception, so nothing forces a `try`/`catch` you
do not want. A non-2xx response throws `EpsHttpException` — the decoded envelope
is on `body`.

| Type | Raised when | Fields |
| --- | --- | --- |
| `EpsClient.EpsHttpException` | Any non-2xx response. | `status`, `url`, `body` (decoded envelope or null), `raw` |
| `EpsClient.EpsException` | Unknown slug, missing required param, wrong param type, transport failure, or a 2xx body that is not JSON. Unchecked — it extends `RuntimeException`. | — |

| 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 `.environment(...)`; 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. |

## Java-specific notes

- Upload parts are sent as `application/octet-stream`; the MIME type is not sniffed.
- Gson is configured with `serializeNulls()` — required for wire conformance, since every other SDK keeps explicit nulls.

## 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 Java snippet.

[Browse the API reference](/docs)
