• Home
  • About
    • Thoughts To Pen photo

      Thoughts To Pen

      My thoughts on Computer Programming || Psychology || Personal Finances || & much more...

    • Learn More
    • Twitter
    • Instagram
    • Github
    • StackOverflow
  • Posts
    • All Posts
    • All Tags
  • Projects
  • Portfolio
  • Resources
  • About

Complete Guide to Shoonya (Finvasia) API Authentication in Java: OAuth 2.0, Zero-Dependency TOTP & WebSocket Streaming

24 Aug 2026

Shoonya Java API Authentication (Comprehensive Shoonya Finvasia Java API Architecture and Authentication Flow)

Introduction: Algorithmic Trading with Shoonya (Finvasia) in India

Algorithmic trading in India has experienced exponential growth over recent years. Retail traders, fintech enthusiasts, and quantitative developers are moving away from manual order placement toward automated, rule-based execution systems connected directly to Indian exchanges like the NSE (National Stock Exchange), BSE (Bombay Stock Exchange), and MCX (Multi Commodity Exchange).

Among Indian stockbrokers, Shoonya by Finvasia stands out as one of the most attractive choices for retail algorithmic traders because it provides true zero-brokerage trading across all market segments (Equity Delivery, Equity Intraday, Futures & Options, and Commodities) along with free API access.

However, setting up programmatic access in Java often trips up both beginner and seasoned developers. Changes mandated by SEBI (Securities and Exchange Board of India) regarding mandatory Two-Factor Authentication (2FA) and periodic backend infrastructure upgrades by Finvasia have deprecated older authentication endpoints.

If you have tried following outdated tutorials and ran into mysterious 404 Not Found, 502 Bad Gateway, or "Access Restricted for API Only Users" errors, this guide is for you.

In this deep dive, we will build a production-ready, zero-dependency Java authentication engine from scratch using pure Java 11/17/21+ standard libraries. You will learn:

  1. How the modern Shoonya API authentication paradigm works (and why legacy endpoints fail).
  2. How to configure your developer credentials and IP whitelist in the Shoonya PRISM portal.
  3. How to fix the subtle IPv4 vs. IPv6 network mismatch that causes the dreaded "Access Restricted" error.
  4. How to execute the 3-Step OAuth 2.0 GenAcsTok Flow in standard Java.
  5. How to build a pure Java RFC 6238 TOTP 2FA generator without needing external Maven packages or Python wrappers.
  6. How to establish a live WebSocket connection (NorenWSAPI) to stream real-time tick-by-tick market data.
  7. How to implement 24-hour session token caching so you never have to re-login repeatedly during market hours (9:15 AM – 3:30 PM IST).

1. The API Architecture: Active vs. Deprecated Endpoints

Before writing any code, it is critical to understand the current state of Shoonya’s backend gateway (known under the hood as the Noren API).

In earlier versions of the API, developers could authenticate by sending a single JSON POST request containing their raw password and an OTP to endpoints like /QuickAuth. These endpoints have been decommissioned for retail API users.

Here is a summary of the endpoint status:

Method / Endpoint Path Current Status Notes
Legacy QuickAuth /NorenWST/QuickAuth ❌ 404 Not Found Permanently deprecated by Finvasia.
Legacy Web Client QuickAuth /NorenWClientAPI/QuickAuth ❌ 502 Bad Gateway Disabled for automated API logins.
Official Web Access Token /NorenWClientAPI/GenAcsTok ✅ Active & Supported The official OAuth 2.0 single-use authorization code exchange.
Live WebSocket Gateway wss://api.shoonya.com/NorenWSAPI/ ✅ Active & Supported Primary real-time market data & order streaming socket.
Fallback WebSocket Gateway wss://api.shoonya.com/NorenWSTP/ ✅ Active (Backup) Secondary failover gateway for live market ticks.
+-----------------------------------------------------------------------------------+
|                         SHOONYA JAVA AUTHENTICATION FLOW                          |
+-----------------------------------------------------------------------------------+
|                                                                                   |
|  [ 1. User Browser ]                                                              |
|        |                                                                          |
|        |-- (Open OAuth Login URL with Client ID / Vendor Code)                     |
|        |                                                                          |
|        v                                                                          |
|  [ Shoonya OAuth Gateway: https://api.shoonya.com/OAuthlogin/authorize/oauth ]   |
|        |                                                                          |
|        |-- (User enters User ID + Password + 6-digit TOTP)                        |
|        |                                                                          |
|        v                                                                          |
|  [ Browser Redirect: http://localhost/?code=<AUTH_CODE> ]                        |
|        |                                                                          |
|        |-- (Extract single-use AUTH_CODE)                                         |
|        |                                                                          |
|        v                                                                          |
|  [ 2. Java Application Engine ]                                                   |
|        |                                                                          |
|        |-- Checksum = SHA256(VendorCode + SecretKey + AuthCode)                   |
|        |-- HTTP POST /NorenWClientAPI/GenAcsTok                                   |
|        |                                                                          |
|        v                                                                          |
|  [ Shoonya REST API Server ]                                                      |
|        |                                                                          |
|        |-- Validates Checksum & returns susertoken (User Token)                   |
|        |                                                                          |
|        v                                                                          |
|  [ 3. WebSocket Real-Time Stream ]                                                |
|        |                                                                          |
|        |-- Connect to wss://api.shoonya.com/NorenWSAPI/                           |
|        |-- Send Handshake Auth Packet { t: "a", uid, actid, usertoken, source }  |
|        |-- Receive { t: "ak", s: "OK" } & Subscribe to NSE/BSE Symbols            |
|                                                                                   |
+-----------------------------------------------------------------------------------+

2. Prerequisites: Shoonya PRISM Developer Configuration

To authenticate via the official API, you need four specific pieces of information from your Shoonya account:

  1. User ID / Account ID: Your primary trading account login ID (for example: FA12345).
  2. Vendor Code (App Key): Your registered API Client ID. This is typically formatted as your User ID followed by _U (for example: FN12345_U).
  3. Secret Key: A 64-character alphanumeric secret key generated inside the Shoonya PRISM developer portal.
  4. TOTP Base32 Secret: The secret alphanumeric key used to configure Google Authenticator, Microsoft Authenticator, or an automated 2FA code generator.

Shoonya Trading Developer Portal (Shoonya Trading Developer Portal showing App Key, Secret Key creation, and IP Whitelist input field)

How to Retrieve Your Credentials

  1. Log in to the Shoonya Trading Portal.
  2. Navigate to API Keys / Developer Settings.
  3. Generate your App Key (Vendor Code) and Secret Key. Store the secret key securely; it will not be shown again.
  4. In your security settings, enable Two-Factor Authentication (TOTP). When the QR code is displayed on your screen, click on “Can’t scan QR code?” or “View secret key” to copy the raw Base32 secret string (e.g., JBSWY3DPEHPK3PXP). Save this string safely—we will use it to compute 6-digit TOTP tokens directly inside Java.

3. The #1 Gotcha: Solving “Access Restricted for API Only Users”

One of the most frustrating obstacles encountered by Indian developers is seeing an error modal stating:

“Access Restricted for API Only Users”

Shoonya Access Restricted Error (Shoonya browser error modal displaying ‘Access Restricted for API Only Users’)

You might double-check your credentials, verify that your IP is whitelisted in SHOONAY, and still get blocked when opening the OAuth login page. Why does this happen?

The Root Cause: IPv6 vs. IPv4 Dual-Stack Conflict

Most broadband and fiber internet providers in India (such as Jio Fiber, Airtel Xstream, and ACT Fibernet), as well as modern operating systems like Windows 10/11 and macOS, have IPv6 enabled by default.

  1. Happy Eyeballs Algorithm (RFC 8305): Modern web browsers and HTTP client libraries attempt to connect over IPv6 first. If an IPv6 route is available, the browser connects to api.shoonya.com using your public IPv6 address.
  2. Temporary IPv6 Addresses (RFC 4941): Windows automatically rotates your outgoing temporary IPv6 address every few hours for privacy reasons.
  3. The Firewall Mismatch: In Shoonya’s PRISM portal, you entered your static/public IPv4 address (e.g., 122.161.x.x). When your browser contacts Shoonya’s Cloudflare / AWS edge servers over an un-whitelisted dynamic IPv6 address (2405:201:...), Shoonya’s firewall immediately rejects the request.
+---------------------------------------------------------------------------------------+
|                                 THE IPV6 MISMATCH PROBLEM                             |
+---------------------------------------------------------------------------------------+
|                                                                                       |
|  Whitelisted in PRISM  : 122.161.45.10  (IPv4)                                        |
|                                                                                       |
|  Browser Outgoing Call : 2405:201:ac02:91a1:4821:e8ff:fe12:3456 (Dynamic IPv6)        |
|                                                                                       |
|  Shoonya Edge Firewall : 2405:201:... != 122.161.45.10  ===>  ❌ ACCESS RESTRICTED    |
|                                                                                       |
+---------------------------------------------------------------------------------------+

The Solution: Forcing IPv4 Traffic

To ensure your requests always use the whitelisted IPv4 address:

Step 1: Find Your Real Public IPv4 Address

Open your terminal (PowerShell or Bash) and query an IPv4-only lookup service:

curl -4 https://ifconfig.me
# or
curl https://api.ipify.org

Copy this IPv4 address into the IP Whitelist field of your Shoonya PRISM dashboard.

Step 2: Disable IPv6 on Windows (or configure Java preferIPv4Stack)

  • In Windows: Open Run (Win + R), type ncpa.cpl, right-click your active network adapter (Wi-Fi or Ethernet), select Properties, and uncheck Internet Protocol Version 6 (TCP/IPv6). Click OK.
  • In Java: Always launch your Java trading application with the JVM argument:
    java -Djava.net.preferIPv4Stack=true -jar trading-engine.jar
    

    This tells the Java Virtual Machine to bypass IPv6 sockets entirely and route all REST and WebSocket traffic exclusively through IPv4.


4. The 3-Step Modern OAuth 2.0 Authentication Flow

Once your IP address is whitelisted and IPv6 conflicts are resolved, you are ready to complete the official 3-step OAuth flow.


Step 1: Obtain the Single-Use Authorization code

Open the following URL in an Incognito / Private browser tab:

https://api.shoonya.com/OAuthlogin/authorize/oauth?client_id=<YOUR_VENDOR_CODE>

(Replace <YOUR_VENDOR_CODE> with your App Key, such as FN12345_U).

  1. The page prompts for your User ID, Password, and 6-digit TOTP.
  2. Upon successful authentication, Shoonya redirects your browser to a local loopback URL:
    http://localhost/?code=9c4a812e-4b21-487a-96e2-df147321e01w
    
  3. Copy the string value after code= (e.g. 9c4a812e-4b21-487a-96e2-df147321e01w).

Note on TTL: This authorization code has a Time-To-Live (TTL) of 2 to 3 minutes and can only be exchanged once. If an exchange fails or the token expires, you must generate a new code via the browser URL.


Step 2: Compute the SHA-256 Checksum in Java

Shoonya verifies the authenticity of your request by requiring a SHA-256 Checksum. The checksum is calculated by concatenating three values with no delimiters or spaces:

$$\text{Checksum} = \text{SHA256}(\text{VendorCode} + \text{SecretKey} + \text{AuthCode})$$

Let’s write a clean, pure Java utility method to calculate this using java.security.MessageDigest:

package com.thoughtstopen.shoonya.auth;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public final class ChecksumUtil {

    private ChecksumUtil() {}

    /**
     * Calculates SHA-256 hash for Shoonya GenAcsTok verification.
     * Concatenation rule: appKey + secretKey + authCode (no spaces).
     *
     * @param appKey    The Vendor Code (e.g. FN164668_U)
     * @param secretKey The 64-character PRISM secret key
     * @param authCode  The single-use code from OAuth redirect
     * @return Lowercase 64-character hexadecimal SHA-256 string
     */
    public static String calculateChecksum(String appKey, String secretKey, String authCode) {
        String rawInput = appKey.trim() + secretKey.trim() + authCode.trim();
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] encodedHash = digest.digest(rawInput.getBytes(StandardCharsets.UTF_8));
            
            // Convert byte array to hexadecimal string representation
            StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
            for (byte b : encodedHash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("SHA-256 algorithm not available in standard JVM", e);
        }
    }
}

Step 3: Exchange Authorization Code for susertoken (GenAcsTok)

Now we send an HTTP POST request to /NorenWClientAPI/GenAcsTok.

  • Endpoint: https://api.shoonya.com/NorenWClientAPI/GenAcsTok
  • Content-Type: application/x-www-form-urlencoded
  • Body Form Param: jData={"code":"<AUTH_CODE>", "checksum":"<SHA256_HASH>"}

Here is how to perform this request using standard java.net.http.HttpClient introduced in Java 11:

package com.thoughtstopen.shoonya.auth;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

public class ShoonyaAuthClient {

    private static final String TOKEN_ENDPOINT = "https://api.shoonya.com/NorenWClientAPI/GenAcsTok";
    private final HttpClient httpClient;

    public ShoonyaAuthClient() {
        this.httpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .connectTimeout(Duration.ofSeconds(10))
                .build();
    }

    /**
     * Exchanges single-use auth code for user session token (susertoken).
     */
    public String exchangeToken(String appKey, String secretKey, String authCode) throws Exception {
        String checksum = ChecksumUtil.calculateChecksum(appKey, secretKey, authCode);

        // Construct jData JSON payload
        String jsonPayload = String.format("{\"code\":\"%s\",\"checksum\":\"%s\"}", authCode, checksum);
        
        // Shoonya requires form URL-encoded parameter: jData=<json>
        String formBody = "jData=" + URLEncoder.encode(jsonPayload, StandardCharsets.UTF_8);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(TOKEN_ENDPOINT))
                .timeout(Duration.ofSeconds(15))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("User-Agent", "ThoughtsToPen-Java-TradingEngine/1.0")
                .POST(HttpRequest.BodyPublishers.ofString(formBody))
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println("Shoonya Server HTTP Status: " + response.statusCode());
        System.out.println("Shoonya Server Response: " + response.body());

        return response.body();
    }
}

Successful JSON Response:

{
  "stat": "Ok",
  "USERID": "FN12345",
  "actid": "FN12345",
  "uname": "RAHUL SHARMA",
  "susertoken": "457357ea9e6e01c52474045cb0f182ab0fc3752b242b293e8ae8ae615a8cf1sc",
  "lastaccesstime": "1724567890",
  "exarr": ["NSE", "BSE", "NFO", "MCX"]
}

The returned susertoken is your master session token. It remains valid for 24 hours (until the broker’s nightly batch settlement).


5. Pure Java RFC 6238 TOTP Generator (Zero Dependencies)

When building automated trading scripts, having to open your phone to check Google Authenticator defeats the purpose of automation.

Many tutorials instruct developers to install third-party dependencies or invoke Python’s pyotp via command line. However, the Time-based One-Time Password algorithm is fully specified under RFC 6238 and can be implemented in standard Java in under 70 lines of code using javax.crypto.Mac and HmacSHA1.

How TOTP Works Internally:

  1. Time Step Counter: The current UNIX epoch timestamp in seconds is divided by 30 (since TOTP tokens rotate every 30 seconds): $$\text{Counter} = \lfloor \frac{\text{CurrentEpochTime}}{30} \rfloor$$
  2. HMAC-SHA1 Hashing: The 8-byte big-endian representation of this counter is hashed with your Base32-decoded secret key using HMAC-SHA1.
  3. Dynamic Truncation: The last 4 bits of the 20-byte hash are used as an offset index (0 to 15). A 4-byte integer is extracted from that offset, masked to ignore the sign bit, and formatted with modulo $10^6$ to yield a 6-digit code.

Here is the complete zero-dependency Java class:

package com.thoughtstopen.shoonya.auth;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Locale;

/**
 * Pure Java RFC 6238 Time-based One-Time Password (TOTP) Generator.
 * Zero external libraries required.
 */
public final class TotpGenerator {

    private static final String HMAC_ALGO = "HmacSHA1";
    private static final int TIME_STEP_SECONDS = 30;
    private static final int DIGIT_MODULUS = 1_000_000; // 6 digits

    private TotpGenerator() {}

    /**
     * Generates a 6-digit TOTP for the current 30-second window.
     *
     * @param base32Secret The Base32 2FA secret from Shoonya security setup
     * @return A 6-digit formatted numeric string (e.g. "482910")
     */
    public static String generateCurrentTotp(String base32Secret) {
        long currentWindow = Instant.now().getEpochSecond() / TIME_STEP_SECONDS;
        return generateTotp(base32Secret, currentWindow);
    }

    /**
     * Calculates TOTP code for a specific time window.
     */
    public static String generateTotp(String base32Secret, long timeWindow) {
        byte[] keyBytes = decodeBase32(base32Secret);
        byte[] counterBytes = ByteBuffer.allocate(8).putLong(timeWindow).array();

        try {
            Mac mac = Mac.getInstance(HMAC_ALGO);
            mac.init(new SecretKeySpec(keyBytes, HMAC_ALGO));
            byte[] hash = mac.doFinal(counterBytes);

            // Dynamic Truncation algorithm as per RFC 4226 Section 5.3
            int offset = hash[hash.length - 1] & 0x0F;
            int binaryCode = ((hash[offset] & 0x7F) << 24)
                    | ((hash[offset + 1] & 0xFF) << 16)
                    | ((hash[offset + 2] & 0xFF) << 8)
                    | (hash[offset + 3] & 0xFF);

            int otp = binaryCode % DIGIT_MODULUS;
            return String.format(Locale.ROOT, "%06d", otp);
        } catch (Exception e) {
            throw new IllegalStateException("Failed to calculate HMAC-SHA1 hash for TOTP", e);
        }
    }

    /**
     * Standard RFC 4648 Base32 alphabet decoder.
     */
    public static byte[] decodeBase32(String base32) {
        String sanitized = base32.toUpperCase(Locale.ROOT).replaceAll("[\\s-]+", "").replaceAll("=+$", "");
        byte[] output = new byte[sanitized.length() * 5 / 8];
        int buffer = 0;
        int bitsLeft = 0;
        int writeIndex = 0;

        for (char c : sanitized.toCharArray()) {
            int val;
            if (c >= 'A' && c <= 'Z') {
                val = c - 'A';
            } else if (c >= '2' && c <= '7') {
                val = c - '2' + 26;
            } else {
                throw new IllegalArgumentException("Invalid Base32 character encountered: " + c);
            }

            buffer = (buffer << 5) | val;
            bitsLeft += 5;
            if (bitsLeft >= 8) {
                bitsLeft -= 8;
                output[writeIndex++] = (byte) ((buffer >> bitsLeft) & 0xFF);
            }
        }
        return output;
    }
}

You can test this in your code:

String secret = "JBSWQA1QPEHPK3PXP"; // Replace with your real TOTP Secret
String currentCode = TotpGenerator.generateCurrentTotp(secret);
System.out.println("Live 6-Digit 2FA Code: " + currentCode);

6. Live Market Data Streaming: WebSocket Handshake & Gotchas

Once you have received your susertoken, you can establish a high-frequency, bidirectional WebSocket connection to receive real-time price updates (LTP, Best Bid/Ask, Traded Volume) for any NSE or BSE stock, index, or option contract.

Shoonya WebSocket Authentication Protocol (Shoonya WebSocket Connect documentation table showing parameters t, uid, actid, usertoken, and source)

1. The Correct WebSocket Endpoints

  • Primary URL: wss://api.shoonya.com/NorenWSAPI/
  • Backup URL: wss://api.shoonya.com/NorenWSTP/

2. The Official Authentication Handshake Packet

Upon opening the WebSocket connection (onOpen), your client must transmit an authentication JSON packet within 2 seconds:

{
  "t": "a",
  "uid": "FN12345",
  "actid": "FN12345",
  "usertoken": "457357ea9e6e01c52474045cb0f182ab0fc3752b242b293e8ae8ae615a8cf1qc",
  "source": "API"
}
  • t: "a" indicates an Authentication request.
  • uid: Your Shoonya User ID.
  • actid: Your Account ID (matches uid).
  • usertoken: The susertoken received from /GenAcsTok.
  • source: Must be "API".

3. Server Acknowledgment and Symbol Subscription

When authentication succeeds, the server replies with:

{"t":"ak","s":"OK"}

(or {"t":"ck","s":"OK"})

Only after receiving this confirmation should you send your symbol subscription packet:

{
  "t": "t",
  "k": "NSE|22#NSE|2885"
}

(Here 22 is the token for ACC and 2885 is RELIANCE. You can look up security tokens in Shoonya’s daily downloadable scrip master CSV).

Common WebSocket Pitfalls & How to Avoid Them

Error Code / Symptom Root Cause Solution
1008 Policy Violation ({"t":"ck","s":"NOT_OK"}) User ID & Token Mismatch: The uid in your JSON packet does not match the account that generated usertoken. Ensure uid and actid strictly equal the User ID associated with the token.
Socket Closed Prematurely Subscription Race Condition: The client sent symbol subscriptions before waiting for {"t":"ak","s":"OK"}. Wait for the authentication acknowledgment packet in your listener before dispatching {t: "t"}.
502 Bad Gateway on Connect Nightly Maintenance Window: Shoonya performs database settlements between 11:30 PM and 6:30 AM IST. Handle reconnection backoff gracefully and automatically failover between NorenWSAPI and NorenWSTP.

7. Dev Productivity: 24-Hour Session Token Reuse

During development, restarting your Java application to test a strategy change can quickly become tedious if you have to log in via your browser every time.

Because Shoonya’s susertoken is valid for 24 hours (until the next morning’s market pre-open), you can store your active token in your application configuration:

# src/main/resources/application.properties
shoonya.user-id=FN12345
shoonya.vendor-code=FN12345_U
shoonya.user-token=457357ea9e6e01c52474045cb0f182ab0fc3752b242b293e8ae8ae615a8cf1qc

In your Java initialization logic:

  1. If shoonya.user-token is present, bypass Step 1–3 and connect directly to the WebSocket or REST endpoints.
  2. If an API call returns {"stat":"Not_Ok","emsg":"Session Expired"}, trigger the OAuth flow to generate a fresh token.

8. Complete Working Java Example (JDK 11+)

Here is a self-contained, working Java application demonstrating the entire lifecycle:

package com.thoughtstopen.shoonya;

import com.thoughtstopen.shoonya.auth.ChecksumUtil;
import com.thoughtstopen.shoonya.auth.ShoonyaAuthClient;
import com.thoughtstopen.shoonya.auth.TotpGenerator;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
import java.util.Scanner;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;

public class ShoonyaAlgoBootstrap {

    // Replace with your credentials
    private static final String USER_ID = "FN12345";
    private static final String VENDOR_CODE = "FN12345_U";
    private static final String SECRET_KEY = "YOUR_64_CHAR_PRISM_SECRET_KEY";
    private static final String TOTP_SECRET = "YOUR_BASE32_TOTP_SECRET";

    public static void main(String[] args) throws Exception {
        System.out.println("==================================================");
        System.out.println("    Shoonya Finvasia Java Authentication Engine   ");
        System.out.println("==================================================");

        // 1. Generate live TOTP to help the developer log in quickly
        String liveTotp = TotpGenerator.generateCurrentTotp(TOTP_SECRET);
        System.out.println("\n[Step 1] Live 2FA TOTP Code (valid 30s): " + liveTotp);
        System.out.println("Open this URL in your browser to get the single-use auth code:");
        System.out.println("https://api.shoonya.com/OAuthlogin/authorize/oauth?client_id=" + VENDOR_CODE);
        
        System.out.print("\nPaste the 'code' parameter from redirect URL: ");
        Scanner scanner = new Scanner(System.in);
        String authCode = scanner.nextLine().trim();

        // 2. Exchange Authorization Code for Session susertoken
        System.out.println("\n[Step 2] Calculating SHA-256 Checksum & Exchanging Token...");
        ShoonyaAuthClient authClient = new ShoonyaAuthClient();
        String responseJson = authClient.exchangeToken(VENDOR_CODE, SECRET_KEY, authCode);

        // Simple manual extraction of susertoken from response
        String tokenKey = "\"susertoken\":\"";
        int startIndex = responseJson.indexOf(tokenKey);
        if (startIndex == -1) {
            System.err.println("Authentication Failed. Check credentials / IP whitelist.");
            return;
        }
        startIndex += tokenKey.length();
        int endIndex = responseJson.indexOf("\"", startIndex);
        String userToken = responseJson.substring(startIndex, endIndex);

        System.out.println("\n[Success] Obtained Active susertoken:");
        System.out.println(userToken);

        // 3. Connect to Live Market Data WebSocket
        System.out.println("\n[Step 3] Connecting to Shoonya WebSocket (NorenWSAPI)...");
        CountDownLatch latch = new CountDownLatch(1);

        HttpClient.newHttpClient().newWebSocketBuilder()
                .buildAsync(URI.create("wss://api.shoonya.com/NorenWSAPI/"), new WebSocket.Listener() {
                    @Override
                    public void onOpen(WebSocket webSocket) {
                        System.out.println("Connected to Shoonya WS Gateway. Sending Auth Packet...");
                        
                        // Send authentication payload
                        String authPacket = String.format(
                                "{\"t\":\"a\",\"uid\":\"%s\",\"actid\":\"%s\",\"usertoken\":\"%s\",\"source\":\"API\"}",
                                USER_ID, USER_ID, userToken
                        );
                        webSocket.sendText(authPacket, true);
                        WebSocket.Listener.super.onOpen(webSocket);
                    }

                    @Override
                    public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
                        String message = data.toString();
                        System.out.println("[Shoonya WS Recv]: " + message);

                        // If auth acknowledged, subscribe to Reliance (Token 2885) & Nifty 50 (Token 26000)
                        if (message.contains("\"ak\"") && message.contains("\"OK\"") 
                                || message.contains("\"ck\"") && message.contains("\"OK\"")) {
                            System.out.println("Authentication Confirmed! Subscribing to NSE Live Ticks...");
                            String subPacket = "{\"t\":\"t\",\"k\":\"NSE|2885#NSE|26000\"}";
                            webSocket.sendText(subPacket, true);
                        }

                        return WebSocket.Listener.super.onText(webSocket, data, last);
                    }

                    @Override
                    public void onError(WebSocket webSocket, Throwable error) {
                        System.err.println("WebSocket Error: " + error.getMessage());
                        WebSocket.Listener.super.onError(webSocket, error);
                    }
                }).join();

        // Keep program alive to observe live ticks
        Thread.sleep(60_000);
    }
}

9. Troubleshooting & Quick Reference Guide

Symptom Probable Cause Corrective Action
404 Not Found on /QuickAuth Legacy endpoint called. Use /NorenWClientAPI/GenAcsTok with single-use OAuth code.
Access Restricted for API Only Users IPv6 / IPv4 mismatch between browser and PRISM whitelist. Whitelist public IPv4 via curl -4 ifconfig.me and disable IPv6 on Windows.
Invalid Checksum in response Checksum string formatted incorrectly. Verify format: appKey + secretKey + authCode with no spaces, trimmed, and hashed with lowercase SHA-256.
Code Expired error Auth code generated > 3 minutes ago. Re-open browser OAuth link and generate a fresh code.
1008 Policy Violation on WS User ID / Account ID mismatch. Ensure uid and actid match the trading account ID exactly.

Conclusion & Next Steps

Integrating Java with Shoonya (Finvasia) provides algorithmic traders in India with a high-throughput, low-latency, and zero-brokerage trading environment. By following the modern OAuth 2.0 flow, resolving IPv6 conflicts, and calculating RFC 6238 TOTP tokens directly in Java, you can build a resilient, automated trading infrastructure.

If you encounter any hurdles setting up your Finvasia Shoonya API with Java or have questions about algorithmic trading architectures, feel free to reach out via our About / Contact Us page.

If you found this guide helpful and it saved you debugging time, consider supporting my work via the Buy Me a Coffee link on the About page! ☕



programmingjavaalgorithmic-tradingshoonya-apifinvasiastock-market-indiansebsefintech Share Tweet Msg