Skip to content

Authentication

Boursa has three auth realms. Most integrations use only the Tenant API.

RealmHeader(s)Used by
Tenant API /v1/*Authorization: Bearer <api_key>your backend, your users' app
Dealer console /broker/v1/*X-Console-Keythe brokerage's dealer desk
Back office /admin/*X-Admin-Keyplatform operations

vs. Alpaca: Alpaca uses APCA-API-KEY-ID + APCA-API-SECRET-KEY headers. Boursa uses a Bearer key plus an HMAC signature on money endpoints — a stricter posture appropriate to broker-of-record infrastructure serving many accounts rather than a single self-trading account.

Bearer key (all /v1 endpoints)

Every Tenant API request carries the tenant api_key:

Authorization: Bearer bsk_…

Read endpoints need nothing more.

HMAC signature (money endpoints)

State-changing money endpoints additionally require a timestamp, a signature, and an idempotency key:

  • POST /v1/orders
  • DELETE /v1/orders/{id}
  • POST /v1/fund-orders
  • POST /v1/transfers

Headers:

Idempotency-Key: <uuid>
X-Boursa-Timestamp: <unix seconds>
X-Boursa-Signature: <hex hmac-sha256>

The signature is HMAC-SHA256 over a canonical request, keyed by your signing_secret:

message = ts + "\n" + METHOD + "\n" + PATH + "\n" + idempotency_key + "\n" + body
signature = hex( HMAC_SHA256(signing_secret, message) )
  • ts — unix seconds, must be within ±300s of server time.
  • METHODPOST / DELETE.
  • PATH — the request path, e.g. /v1/orders (no query string).
  • idempotency_key — the same value sent in the Idempotency-Key header.
  • body — the exact raw request body bytes (empty string for DELETE).

Example (Node)

js
import { createHmac, randomUUID } from "node:crypto";

function signedHeaders(method, path, body, apiKey, signingSecret) {
  const ts = Math.floor(Date.now() / 1000).toString();
  const idem = randomUUID();
  const msg = [ts, method, path, idem, body].join("\n");
  const sig = createHmac("sha256", signingSecret).update(msg).digest("hex");
  return {
    Authorization: `Bearer ${apiKey}`,
    "Idempotency-Key": idem,
    "X-Boursa-Timestamp": ts,
    "X-Boursa-Signature": sig,
    "Content-Type": "application/json",
  };
}

Example (Python)

python
import hmac, hashlib, time, uuid

def signed_headers(method, path, body, api_key, signing_secret):
    ts = str(int(time.time()))
    idem = str(uuid.uuid4())
    msg = "\n".join([ts, method, path, idem, body]).encode()
    sig = hmac.new(signing_secret.encode(), msg, hashlib.sha256).hexdigest()
    return {
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": idem,
        "X-Boursa-Timestamp": ts,
        "X-Boursa-Signature": sig,
        "Content-Type": "application/json",
    }

Idempotency

Every money endpoint requires an Idempotency-Key. Reusing the same key with the same request safely returns the original result instead of acting twice — make it a fresh UUID per logical attempt, and reuse it verbatim on a retry.

Orders also accept an optional client_order_id in the body (Alpaca's mechanism): a tenant-unique string you control, returned on the order object and usable to look the order back up. A reused client_order_id is rejected with CLIENT_ORDER_ID_EXISTS.

Errors

A bad key returns 401 UNAUTHENTICATED; a bad or stale signature returns 401 SIGNATURE_INVALID / SIGNATURE_EXPIRED. See Errors.

Embedded investing infrastructure for the Egyptian Exchange. Sandbox runs on simulated money.