Authentication
Boursa has three auth realms. Most integrations use only the Tenant API.
| Realm | Header(s) | Used by |
|---|---|---|
Tenant API /v1/* | Authorization: Bearer <api_key> | your backend, your users' app |
Dealer console /broker/v1/* | X-Console-Key | the brokerage's dealer desk |
Back office /admin/* | X-Admin-Key | platform operations |
vs. Alpaca: Alpaca uses
APCA-API-KEY-ID+APCA-API-SECRET-KEYheaders. 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/ordersDELETE /v1/orders/{id}POST /v1/fund-ordersPOST /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.METHOD—POST/DELETE.PATH— the request path, e.g./v1/orders(no query string).idempotency_key— the same value sent in theIdempotency-Keyheader.body— the exact raw request body bytes (empty string forDELETE).
Example (Node)
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)
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.