Artham
ARTHAM

Broker Integration API Specification

Version 1.1  ·  July 2026

Contents

  1. Overview
  2. Authentication
  3. Error Handling
  4. API 1 — Get Client Details
  5. API 2 — Consent Status Update
  6. Signature Verification — Code Samples
  7. Sandbox & Testing
Related documentation
WhatsApp Business API — Partner Integration Setup  —  steps for a partner to connect their WhatsApp Business Account for order-consent messaging.

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) API to fetch client information.

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/artham/consent-statusAPI — receive client Approve / Cancel consent decisions with transaction detailsRequired

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."" (GET has no body)
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 6 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 — Consent Status Update Required

When an order is generated for a client, Artham sends the client a consent request on WhatsApp (Approve / Cancel buttons) and email (OTP). The moment the client responds, Artham calls this API on your server — so you know which client approved and which client cancelled, along with the transaction details (scrip, quantity, price, amount).

You provide Artham with the URL where you want to receive these notifications. Authentication uses the same X-Api-Key + X-Signature headers described in Section 2 (signature computed over the raw request body).

POST /api/v1/artham/consent-status Client Approve / Cancel decision

Request Body

{
  "eventId": "consent-1042",
  "clientCode": "AB1234",
  "clientName": "Rajesh Kumar",
  "requestType": "REBALANCE",
  "referenceId": "REB#210",
  "status": "APPROVED",
  "respondedVia": "WHATSAPP",
  "respondedAt": "2026-07-15 14:32:08",
  "orders": [
    {
      "symbol": "TCS",
      "exchange": "NSE",
      "transactionType": "SELL",
      "quantity": 10,
      "price": 3450.50,
      "amount": 34505.00
    },
    {
      "symbol": "HDFCBANK",
      "exchange": "NSE",
      "transactionType": "BUY",
      "quantity": 20,
      "price": 1720.00,
      "amount": 34400.00
    }
  ]
}

Fields

FieldTypeDescription
eventIdstringUnique ID of this notification
clientCodestringThe client's trading code — which client responded
clientNamestringThe client's registered name
requestTypestringCASH_IN, CASH_OUT_PARTIAL, CASH_OUT_FULL, CASH_OUT_FEES, or REBALANCE
referenceIdstringArtham's transaction reference (CT#<id> / REB#<id>) — matches the reference tags on placed orders
statusstring"APPROVED" or "CANCELLED"the client's decision
respondedViastring"WHATSAPP" or "EMAIL_OTP"
respondedAtstringWhen the client responded, YYYY-MM-DD HH:mm:ss IST
orders[]arrayTransaction lines the client was asked to approve: symbol, exchange (NSE/BSE), transactionType (BUY/SELL), quantity, price (₹, indicative at consent generation), amount (₹, quantity × price)

Response — 200 OK

{
  "success": true
}

6. 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);
//     }
// }

7. Sandbox & Testing

Integration Steps

  1. You share your sandbox base URL — e.g. https://sandbox-api.yourbroker.com, plus the API URL where you want consent status updates (API 2).
  2. Artham shares credentials — We generate and send you an API Key + Shared Secret over email.
  3. You build the API(s) — The Get Client Details endpoint (API 1) and the Consent Status API receiver (API 2).
  4. End-to-end test — We call your API with test client codes, trigger test consent requests on WhatsApp (one Approve, one Cancel), and verify both directions.
  5. Go live — You share the production base URL and API 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
Client taps Approve on WhatsAppAPI received with status = "APPROVED", respondedVia = "WHATSAPP", and order lines (qty, price, amount)
Client taps Cancel on WhatsAppAPI received with status = "CANCELLED", respondedVia = "WHATSAPP"
Client approves via email OTP insteadAPI received with respondedVia = "EMAIL_OTP"

Support

For integration questions, contact the Artham engineering team: