HTTP API Authentication
Every HTTP request to https://openapi.abetterchoice.ai/abc/... carries three headers that prove the caller has a valid API token. The signature is computed once per request from the token, the key name, and the current Unix timestamp.
Headers
| Header | Description |
|---|---|
X-Ak | The name of the API key you created in Settings → SDK&Key. Not the token itself. |
X-Et | The current Unix timestamp in seconds, e.g. 1748520000 for 2025-05-29 11:20:00 UTC. |
X-Es | MD5 hex string of the concatenation token + ak + et — the signature. |
The token never appears in the request. Only the key name (ak), the timestamp (et), and the signature (es) travel over the wire, so a leaked request line cannot be replayed once the token is rotated.
Computing the signature
signature = lowercase_hex(MD5(token || ak || et))Where:
tokenis the value from theTokencolumn inSettings → SDK&Key.akis the value from theNamecolumn for the same row.etis the current Unix timestamp in seconds.
Bash
bash
ak="your_secret_key_name"
token="your_api_token"
et=$(date +%s)
signature=$(echo -n "${token}${ak}${et}" | md5)
echo "X-Ak: $ak"
echo "X-Et: $et"
echo "X-Es: $signature"macOS uses
md5; Linux usesmd5sum | awk '{print $1}'. Both produce the same hex string.
Python
python
import hashlib, time
ak = "your_secret_key_name"
token = "your_api_token"
et = str(int(time.time()))
sig = hashlib.md5(f"{token}{ak}{et}".encode()).hexdigest()
headers = { "X-Ak": ak, "X-Et": et, "X-Es": sig }Go
go
import (
"crypto/md5"
"encoding/hex"
"fmt"
"time"
)
ak := "your_secret_key_name"
token := "your_api_token"
et := fmt.Sprintf("%d", time.Now().Unix())
sum := md5.Sum([]byte(token + ak + et))
sig := hex.EncodeToString(sum[:])Node.js
javascript
const crypto = require("node:crypto");
const ak = "your_secret_key_name";
const token = "your_api_token";
const et = Math.floor(Date.now() / 1000).toString();
const sig = crypto.createHash("md5").update(token + ak + et).digest("hex");Putting it together
bash
curl -X POST 'https://openapi.abetterchoice.ai/abc/get_experiments' \
-H "Content-Type: application/json" \
-H "X-Ak: $ak" -H "X-Et: $et" -H "X-Es: $signature" \
-d '{"project_id":"6666","unit_id":"user_id_1"}'If the headers are valid, you get ret_code: 100 and the data — see Endpoints.
Common authentication errors
| Symptom | Likely cause |
|---|---|
ret_code: 101 (no permission) | X-Ak does not match a key in this project, or the key is Deactivated. |
| Stale-timestamp rejection | Server clock and your machine differ by more than the allowed window. Sync via NTP and re-sign. |
| Signature mismatch | Concatenation order is wrong (must be token + ak + et), or you signed the wrong token. |
Security best practices
- Never commit a token to a repository. Pair every key with a secret manager (Vault, AWS Secrets Manager, Tencent Cloud SSM, etc.) and inject at deploy time.
- One key per consumer —
server_prod,server_staging,http_batch. You can deactivate exactly the consumer that leaked without breaking the rest of the fleet. - Rotate before, then deactivate. Deactivation is immediate; new keys take effect immediately. Roll out the new key first, drain traffic, then deactivate the old one. Full procedure on API keys.
- Treat the timestamp like a freshness token — sign and send each request from the calling process, do not pre-compute signatures and ship them around.