ARTHAM
Broker Integration API Specification
Version 1.0 · February 2026
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
- Artham calls your APIs — you build the endpoints described in this document.
- You verify our identity — using the API key + HMAC signature we send with every request.
- You return JSON responses — in the standard format described below.
API Summary
| # | Method | Endpoint | Purpose | |
| 1 | GET | /api/v1/clients/{clientCode} | Fetch client KYC and account details | Required |
| 2 | POST | /api/v1/clients/{clientCode}/sub-accounts | Create a sub account for a scheme | Required |
| 3 | POST | /api/v1/clients/{clientCode}/limit-open | Open (release) trading limit | Optional |
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:
| Header | Description | Example |
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
- Artham generates an API Key (public identifier) and a Shared Secret (private, used for HMAC) for each broker.
- Artham will share both credentials with you over email.
- 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
| Component | Description | Example |
HTTP_METHOD | Uppercase HTTP method | GET |
path | Request path (no query string, no host) | /api/v1/clients/AB1234 |
body | Raw 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)
- 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.
- Recompute signature — using the Shared Secret, compute the HMAC-SHA256 of the same message format shown above.
- 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
| Status | When to Use |
200 | Success |
400 | Bad request (invalid params, missing fields) |
401 | Invalid API key or signature |
404 | Client not found |
422 | Business rule violation (insufficient limit, etc.) |
500 | Internal server error |
Error Codes
Use these standard error codes in the error.code field:
| Code | Description |
CLIENT_NOT_FOUND | The given clientCode does not exist in your system |
CLIENT_INACTIVE | Client account is deactivated or suspended |
INSUFFICIENT_LIMIT | Client does not have enough available limit/balance |
INVALID_REQUEST | Missing or malformed request parameters |
INTERNAL_ERROR | Unexpected 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.
Path Parameters
| Parameter | Type | Description |
clientCode | string | The 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
| Field | Type | Required | Description |
clientCode | string | Yes | Client's unique trading code at your brokerage |
rmCode | string | Yes | Relationship Manager code assigned to this client |
name | string | Yes | Full name as registered in your system |
pan | string | Yes | PAN number (10 characters, e.g. ABCDE1234F) |
dateOfBirth | string | Yes | Date of birth in YYYY-MM-DD format |
address1 | string | No | Address line 1 |
address2 | string | No | Address line 2 |
address3 | string | No | Address line 3 |
city | string | No | City |
state | string | No | State |
pinCode | string | No | PIN code (6 digits) |
emailAddress | string | Yes | Registered email address |
mobileNumber | string | Yes | Registered mobile number (10 digits) |
accountNo | string | Yes | Client's primary bank account number |
ifscCode | string | Yes | IFSC code of the bank branch (11 characters) |
accountType | string | Yes | Bank account type: "S" (Savings), "C" (Current), or "O" (Other) |
clientTypeCode | integer | Yes | Client constitution code (e.g. 1 = Individual, 2 = HUF, etc.) |
isNRI | boolean | Yes | true if the client is a Non-Resident Indian |
isPoliticallyExposedPerson | boolean | Yes | true 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:
| Value | Description |
"S" | Savings account |
"C" | Current account |
"O" | Other |
clientTypeCode values:
| Value | Description |
1 | Individual |
2 | HUF (Hindu Undivided Family) |
3 | Corporate |
4 | Partnership / LLP |
5 | Trust |
If your system uses different codes, please share your mapping and we will align.
Error Responses
| HTTP | Error Code | When |
401 | — | Invalid API key or signature |
404 | CLIENT_NOT_FOUND | No client exists with this clientCode |
422 | CLIENT_INACTIVE | Client 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.
Path Parameters
| Parameter | Type | Description |
clientCode | string | The client's trading code at your brokerage |
Request Body
{
"schemeCode": "ARTHAM-EQ-001"
}
| Field | Type | Required | Description |
schemeCode | string | Yes | The 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
| Field | Type | Required | Description |
subAccountCode | string | Yes | The unique sub account code generated by your system. Artham will use this in subsequent API calls (e.g. limit open, order placement). |
clientCode | string | Yes | The client's trading code (echoed back) |
schemeCode | string | Yes | The 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
| HTTP | Error Code | When |
401 | — | Invalid API key or signature |
404 | CLIENT_NOT_FOUND | No client exists with this clientCode |
422 | CLIENT_INACTIVE | Client 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.
Path Parameters
| Parameter | Type | Description |
clientCode | string | The client's trading code |
Request Body
{
"subAccountCode": "AB1234-EQ001",
"amount": 100000.00,
"segment": "EQUITY"
}
| Field | Type | Required | Description |
subAccountCode | string | Yes | The sub account code returned by the Create Sub Account API (API 2). |
amount | number | Yes | Amount to open/release (INR). Two decimal places. |
segment | string | No | Trading 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
| Field | Type | Required | Description |
sufficient | boolean | Yes | true if available limit ≥ requested amount |
availableLimit | number | Yes | Current available trading limit (INR) |
usedLimit | number | No | Currently utilized limit (INR) |
Error Responses
| HTTP | Error Code | When |
401 | — | Invalid API key or signature |
404 | CLIENT_NOT_FOUND | No client exists with this clientCode |
422 | INSUFFICIENT_LIMIT | Available 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
- You share your sandbox base URL — e.g.
https://sandbox-api.yourbroker.com
- Artham shares credentials — We generate and send you an API Key + Shared Secret over email.
- You build the API(s) — At minimum, the Get Client Details endpoint (API 1).
- End-to-end test — We call your API with test client codes and verify the responses.
- Go live — You share the production base URL. We generate a new set of production credentials.
Test Checklist
| Test Case | Expected Result |
| Valid API key + correct signature | 200 with client data |
| Valid API key + wrong signature | 401 Unauthorized |
| Invalid / unknown API key | 401 Unauthorized |
Missing X-Api-Key or X-Signature headers | 401 Unauthorized |
| Get details for an existing client | 200 with all required fields populated |
| Get details for a non-existent client code | 404 with CLIENT_NOT_FOUND |
| Get details for an inactive / suspended client | 422 with CLIENT_INACTIVE |
| Create sub account for a valid client + scheme | 200 with subAccountCode |
| Create sub account — duplicate (same client + scheme) | 200 returning the existing sub account code (idempotent) |
| Create sub account — non-existent client | 404 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:
- Email: rahul@artham.co
- We are available for joint debugging calls during integration.