Artham
ARTHAM

Broker Integration API Specification

Version 1.0  ·  February 2026

Contents

  1. Overview
  2. Authentication
  3. Error Handling
  4. API 1 — Get Client Details
  5. API 2 — Create Sub Account
  6. API 3 — Open Limit
  7. Signature Verification — Code Samples
  8. Sandbox & Testing

1. Overview

Artham is a marketplace platform that connects fund managers (Investment Advisors registered with SEBI) with clients through distribution partners. As part of this integration, Artham's servers call your (the broker's) APIs to fetch client information, create sub accounts for portfolio segregation, and open trading limits.

How It Works

  1. Artham calls your APIs — you build the endpoints described in this document.
  2. You verify our identity — using the API key + HMAC signature we send with every request.
  3. You return JSON responses — in the standard format described below.

API Summary

#MethodEndpointPurpose
1GET/api/v1/clients/{clientCode}Fetch client KYC and account detailsRequired
2POST/api/v1/clients/{clientCode}/sub-accountsCreate a sub account for a schemeRequired
3POST/api/v1/clients/{clientCode}/limit-openOpen (release) trading limitOptional

Base URL

You provide Artham with your base URL. All endpoint paths below are appended to it.

Production:  https://api.yourbroker.com
Sandbox:     https://sandbox-api.yourbroker.com

2. Authentication

Every request from Artham includes two headers for authentication:

HeaderDescriptionExample
X-Api-Key Static API key that identifies Artham as the caller. artham_live_k8Fj2mNp...
X-Signature HMAC-SHA256 signature of the request, computed using a shared secret. Proves the request hasn't been tampered with. a1b2c3d4e5f6... (64 hex chars)

Credential Setup

  1. Artham generates an API Key (public identifier) and a Shared Secret (private, used for HMAC) for each broker.
  2. Artham will share both credentials with you over email.
  3. You store the API Key and Shared Secret on your server. Use the API Key to identify incoming requests from Artham, and the Shared Secret to verify the signature.
One credential pair per broker. You will receive a single API Key + Shared Secret. This is used across all advisors and clients managed through Artham. You do not need per-advisor or per-client keys.

Signature Computation

Artham computes the signature as follows:

message = "{HTTP_METHOD}\n{path}\n{body}"

signature = HMAC-SHA256(key: SHARED_SECRET, message: message)
            → output as lowercase hex string
ComponentDescriptionExample
HTTP_METHODUppercase HTTP methodGET
pathRequest path (no query string, no host)/api/v1/clients/AB1234
bodyRaw JSON request body. Empty string "" for GET requests.{"subAccountCode":"AB1234-EQ001","amount":100000}
Note: For GET requests with no body, the message ends with an empty string: "GET\n/api/v1/clients/AB1234\n"

Verification Steps (Your Side)

  1. Check API key — look up the X-Api-Key header value. Reject with 401 if it doesn't match the key Artham shared with you.
  2. Recompute signature — using the Shared Secret, compute the HMAC-SHA256 of the same message format shown above.
  3. Compare — use a constant-time comparison. Reject with 401 if signatures don't match.

See Section 7 for complete verification code in Python, Java, and C#.

3. Error Handling

Response Envelope

All responses must follow this structure:

Success

{
  "success": true,
  "data": { ... },
  "error": null
}

Error

{
  "success": false,
  "data": null,
  "error": {
    "code": "CLIENT_NOT_FOUND",
    "message": "No client found with code AB9999."
  }
}

HTTP Status Codes

StatusWhen to Use
200Success
400Bad request (invalid params, missing fields)
401Invalid API key or signature
404Client not found
422Business rule violation (insufficient limit, etc.)
500Internal server error

Error Codes

Use these standard error codes in the error.code field:

CodeDescription
CLIENT_NOT_FOUNDThe given clientCode does not exist in your system
CLIENT_INACTIVEClient account is deactivated or suspended
INSUFFICIENT_LIMITClient does not have enough available limit/balance
INVALID_REQUESTMissing or malformed request parameters
INTERNAL_ERRORUnexpected server error on your side

4. API 1 — Get Client Details Required

This is the primary API. Artham calls this to fetch a client's KYC information, personal details, and bank account details when a client is being onboarded onto the advisory platform.

GET /api/v1/clients/{clientCode} Fetch client KYC and account details

Path Parameters

ParameterTypeDescription
clientCodestringThe client's trading code at your brokerage

Response — 200 OK

{
  "success": true,
  "data": {
    "clientCode": "AB1234",
    "rmCode": "RM001",
    "name": "Rajesh Kumar",
    "pan": "ABCDE1234F",
    "dateOfBirth": "1990-05-15",
    "address1": "123 MG Road",
    "address2": "Andheri West",
    "address3": "",
    "city": "Mumbai",
    "state": "Maharashtra",
    "pinCode": "400058",
    "emailAddress": "rajesh@example.com",
    "mobileNumber": "9876543210",
    "accountNo": "1234567890",
    "ifscCode": "HDFC0001234",
    "accountType": "S",
    "clientTypeCode": 1,
    "isNRI": false,
    "isPoliticallyExposedPerson": false
  },
  "error": null
}

Response Fields

FieldTypeRequiredDescription
clientCodestringYesClient's unique trading code at your brokerage
rmCodestringYesRelationship Manager code assigned to this client
namestringYesFull name as registered in your system
panstringYesPAN number (10 characters, e.g. ABCDE1234F)
dateOfBirthstringYesDate of birth in YYYY-MM-DD format
address1stringNoAddress line 1
address2stringNoAddress line 2
address3stringNoAddress line 3
citystringNoCity
statestringNoState
pinCodestringNoPIN code (6 digits)
emailAddressstringYesRegistered email address
mobileNumberstringYesRegistered mobile number (10 digits)
accountNostringYesClient's primary bank account number
ifscCodestringYesIFSC code of the bank branch (11 characters)
accountTypestringYesBank account type: "S" (Savings), "C" (Current), or "O" (Other)
clientTypeCodeintegerYesClient constitution code (e.g. 1 = Individual, 2 = HUF, etc.)
isNRIbooleanYestrue if the client is a Non-Resident Indian
isPoliticallyExposedPersonbooleanYestrue if the client is a Politically Exposed Person (PEP)
Data accuracy: The data returned by this API is used for KYC verification, agreement generation, and regulatory compliance. Please ensure all fields — especially pan, name, dateOfBirth, and bank account details — are accurate and match your KYC records.

Field Details

accountType values:

ValueDescription
"S"Savings account
"C"Current account
"O"Other

clientTypeCode values:

ValueDescription
1Individual
2HUF (Hindu Undivided Family)
3Corporate
4Partnership / LLP
5Trust

If your system uses different codes, please share your mapping and we will align.

Error Responses

HTTPError CodeWhen
401Invalid API key or signature
404CLIENT_NOT_FOUNDNo client exists with this clientCode
422CLIENT_INACTIVEClient account is deactivated or suspended

5. API 2 — Create Sub Account Required

When a client is allocated to a portfolio/scheme, Artham calls this API to create a sub account under the client's main trading account. The sub account is used for all subsequent operations (limit open, order placement) on that scheme.

POST /api/v1/clients/{clientCode}/sub-accounts Create a sub account for a scheme

Path Parameters

ParameterTypeDescription
clientCodestringThe client's trading code at your brokerage

Request Body

{
  "schemeCode": "ARTHAM-EQ-001"
}
FieldTypeRequiredDescription
schemeCodestringYesThe Artham scheme/portfolio code. This identifies which portfolio the sub account is being created for.

Response — 200 OK

{
  "success": true,
  "data": {
    "subAccountCode": "AB1234-EQ001",
    "clientCode": "AB1234",
    "schemeCode": "ARTHAM-EQ-001"
  },
  "error": null
}

Response Fields

FieldTypeRequiredDescription
subAccountCodestringYesThe unique sub account code generated by your system. Artham will use this in subsequent API calls (e.g. limit open, order placement).
clientCodestringYesThe client's trading code (echoed back)
schemeCodestringYesThe scheme code (echoed back)
Idempotency: If Artham calls this API again with the same clientCode + schemeCode combination, return the existing sub account code instead of creating a duplicate. This allows safe retries.
Using ODIN? If your brokerage uses the ODIN OMS, this API maps to the Create Retail User Request flow described in Section 4.5 of the ODIN Unified User Management API documentation.
Download ODIN API Documentation (PDF)

Error Responses

HTTPError CodeWhen
401Invalid API key or signature
404CLIENT_NOT_FOUNDNo client exists with this clientCode
422CLIENT_INACTIVEClient account is deactivated or suspended

6. API 3 — Open Limit Optional

Optional API. If your system does not support programmatic limit opening, you do not need to build this endpoint. Artham will fall back to a manual workflow where the dealer confirms the limit before proceeding.
POST /api/v1/clients/{clientCode}/limit-open Open (release) trading limit

Path Parameters

ParameterTypeDescription
clientCodestringThe client's trading code

Request Body

{
  "subAccountCode": "AB1234-EQ001",
  "amount": 100000.00,
  "segment": "EQUITY"
}
FieldTypeRequiredDescription
subAccountCodestringYesThe sub account code returned by the Create Sub Account API (API 2).
amountnumberYesAmount to open/release (INR). Two decimal places.
segmentstringNoTrading segment. Default: "EQUITY"

Response — 200 OK (Sufficient)

{
  "success": true,
  "data": {
    "sufficient": true,
    "availableLimit": 250000.00,
    "usedLimit": 50000.00
  },
  "error": null
}

Response — 422 (Insufficient)

{
  "success": false,
  "data": {
    "sufficient": false,
    "availableLimit": 45000.00,
    "usedLimit": 255000.00
  },
  "error": {
    "code": "INSUFFICIENT_LIMIT",
    "message": "Available limit is 45000.00. Required: 100000.00."
  }
}

Response Fields

FieldTypeRequiredDescription
sufficientbooleanYestrue if available limit ≥ requested amount
availableLimitnumberYesCurrent available trading limit (INR)
usedLimitnumberNoCurrently utilized limit (INR)

Error Responses

HTTPError CodeWhen
401Invalid API key or signature
404CLIENT_NOT_FOUNDNo client exists with this clientCode
422INSUFFICIENT_LIMITAvailable limit is less than the requested amount

7. Signature Verification — Code Samples

Below are complete examples showing how to verify the X-Signature header on your server. Use the approach that matches your tech stack.

Python

import hmac
import hashlib

def verify_signature(shared_secret, method, path, body, received_signature):
    """
    Verify the HMAC-SHA256 signature sent by Artham.

    Args:
        shared_secret: The secret shared by Artham for your broker
        method: HTTP method, e.g. "GET"
        path: Request path, e.g. "/api/v1/clients/AB1234"
        body: Raw request body as string ("" for GET requests)
        received_signature: The X-Signature header value

    Returns:
        True if signature is valid
    """
    message = f"{method}\n{path}\n{body}"

    expected = hmac.new(
        shared_secret.encode('utf-8'),
        message.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    # Constant-time comparison to prevent timing attacks
    return hmac.compare_digest(expected, received_signature)


# ─── Example: Flask middleware ───────────────────────────────
from flask import request, abort

# The credentials Artham shared with you
ARTHAM_API_KEY = "artham_live_k8Fj2mNp..."
ARTHAM_SHARED_SECRET = "your_shared_secret_here"

@app.before_request
def check_artham_signature():
    api_key = request.headers.get('X-Api-Key')
    signature = request.headers.get('X-Signature')

    if not api_key or not signature:
        abort(401, 'Missing authentication headers')

    if api_key != ARTHAM_API_KEY:
        abort(401, 'Unknown API key')

    body = request.get_data(as_text=True) or ""

    if not verify_signature(
        ARTHAM_SHARED_SECRET,
        request.method, request.path, body, signature
    ):
        abort(401, 'Invalid signature')

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

public class ArthamSignatureVerifier {

    /**
     * Verify the HMAC-SHA256 signature sent by Artham.
     *
     * @param sharedSecret  The secret shared by Artham for your broker
     * @param method        HTTP method, e.g. "GET"
     * @param path          Request path, e.g. "/api/v1/clients/AB1234"
     * @param body          Raw request body ("" for GET requests)
     * @param receivedSig   The X-Signature header value
     */
    public static boolean verifySignature(
            String sharedSecret,
            String method,
            String path,
            String body,
            String receivedSig
    ) throws Exception {

        String message = method + "\n" + path + "\n" + (body != null ? body : "");

        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec keySpec = new SecretKeySpec(
            sharedSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"
        );
        mac.init(keySpec);

        byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
        String expected = bytesToHex(hash);

        // Constant-time comparison
        return MessageDigest.isEqual(
            expected.getBytes(StandardCharsets.UTF_8),
            receivedSig.getBytes(StandardCharsets.UTF_8)
        );
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}


// ─── Example: Spring Boot filter ────────────────────────────
// @Component
// public class ArthamAuthFilter extends OncePerRequestFilter {
//     @Value("${artham.api-key}") private String arthamApiKey;
//     @Value("${artham.shared-secret}") private String arthamSecret;
//
//     @Override
//     protected void doFilterInternal(HttpServletRequest req,
//             HttpServletResponse res, FilterChain chain) throws ... {
//         String apiKey = req.getHeader("X-Api-Key");
//         String signature = req.getHeader("X-Signature");
//
//         if (!arthamApiKey.equals(apiKey)) {
//             res.sendError(401, "Unknown API key"); return;
//         }
//
//         String body = new String(req.getInputStream().readAllBytes());
//         if (!ArthamSignatureVerifier.verifySignature(
//                 arthamSecret, req.getMethod(),
//                 req.getRequestURI(), body, signature)) {
//             res.sendError(401, "Invalid signature"); return;
//         }
//         chain.doFilter(req, res);
//     }
// }

C# (.NET)

using System.Security.Cryptography;
using System.Text;

public static class ArthamSignatureVerifier
{
    /// <summary>
    /// Verify the HMAC-SHA256 signature sent by Artham.
    /// </summary>
    /// <param name="sharedSecret">The secret shared by Artham</param>
    /// <param name="method">HTTP method, e.g. "GET"</param>
    /// <param name="path">Request path, e.g. "/api/v1/clients/AB1234"</param>
    /// <param name="body">Raw request body ("" for GET)</param>
    /// <param name="receivedSignature">X-Signature header value</param>
    public static bool VerifySignature(
        string sharedSecret,
        string method,
        string path,
        string body,
        string receivedSignature)
    {
        string message = $"{method}\n{path}\n{body ?? ""}";

        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(sharedSecret));
        byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
        string expected = BitConverter.ToString(hash).Replace("-", "").ToLower();

        // Constant-time comparison
        return CryptographicOperations.FixedTimeEquals(
            Encoding.UTF8.GetBytes(expected),
            Encoding.UTF8.GetBytes(receivedSignature)
        );
    }
}


// ─── Example: ASP.NET Core middleware ───────────────────────
// public class ArthamAuthMiddleware {
//     private readonly RequestDelegate _next;
//     private readonly string _apiKey;     // from appsettings.json
//     private readonly string _secret;     // from appsettings.json
//
//     public async Task InvokeAsync(HttpContext context) {
//         var apiKey = context.Request.Headers["X-Api-Key"].FirstOrDefault();
//         var signature = context.Request.Headers["X-Signature"].FirstOrDefault();
//
//         if (apiKey != _apiKey) {
//             context.Response.StatusCode = 401; return;
//         }
//
//         context.Request.EnableBuffering();
//         using var reader = new StreamReader(context.Request.Body, leaveOpen: true);
//         var body = await reader.ReadToEndAsync();
//         context.Request.Body.Position = 0;
//
//         if (!ArthamSignatureVerifier.VerifySignature(
//                 _secret, context.Request.Method,
//                 context.Request.Path, body, signature)) {
//             context.Response.StatusCode = 401; return;
//         }
//         await _next(context);
//     }
// }

8. Sandbox & Testing

Integration Steps

  1. You share your sandbox base URL — e.g. https://sandbox-api.yourbroker.com
  2. Artham shares credentials — We generate and send you an API Key + Shared Secret over email.
  3. You build the API(s) — At minimum, the Get Client Details endpoint (API 1).
  4. End-to-end test — We call your API with test client codes and verify the responses.
  5. Go live — You share the production base URL. We generate a new set of production credentials.

Test Checklist

Test CaseExpected Result
Valid API key + correct signature200 with client data
Valid API key + wrong signature401 Unauthorized
Invalid / unknown API key401 Unauthorized
Missing X-Api-Key or X-Signature headers401 Unauthorized
Get details for an existing client200 with all required fields populated
Get details for a non-existent client code404 with CLIENT_NOT_FOUND
Get details for an inactive / suspended client422 with CLIENT_INACTIVE
Create sub account for a valid client + scheme200 with subAccountCode
Create sub account — duplicate (same client + scheme)200 returning the existing sub account code (idempotent)
Create sub account — non-existent client404 with CLIENT_NOT_FOUND
Open limit — sufficient balance (if implemented)200 with sufficient: true
Open limit — insufficient balance (if implemented)422 with INSUFFICIENT_LIMIT

Support

For integration questions, contact the Artham engineering team: