Authentication
Every request best-system sends to your wallet API carries 2 headers — always verify them before processing; if verification fails, never touch the wallet
Headers on every request
| Header | Value |
|---|---|
X-Api-Key | The API key best-system issued to you — check it matches the one you hold |
X-Signature | HMAC-SHA256(rawBody, secret) encoded as hex |
The body is always JSON (Content-Type: application/json)
Verify from the raw bytes only
The signature is computed over the entire raw body, byte for byte, with the secret best-system issued — so your side must keep the raw request bytes, verify against them first, and only then parse the JSON
Never re-serialize
Do not JSON.parse and then JSON.stringify the body back to compute the signature — a different key order, whitespace, or number formatting changes even a single byte and the signature will mismatch on a perfectly valid request
Example middleware for every endpoint under /api (Express):
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
// Capture raw bytes first — the signature is over the raw body; never parse before verifying
app.use("/api", express.raw({ type: "application/json" }));
app.use("/api", (req, res, next) => {
if (req.headers["x-api-key"] !== process.env.BS_API_KEY) {
return res.json({ code: 40003 });
}
const expected = createHmac("sha256", process.env.BS_SECRET!)
.update(req.body) // raw bytes — never JSON.parse then stringify back
.digest("hex");
const given = String(req.headers["x-signature"] ?? "");
const ok =
given.length === expected.length &&
timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!ok) return res.status(401).json({ code: 30002 });
req.body = JSON.parse(req.body.toString("utf8"));
next();
});Use timingSafeEqual to prevent timing attacks — and compare lengths first, because the function throws when the buffers differ in length
When the signature does not match
Two accepted replies (best-system handles both):
- HTTP 200 with a body carrying
code: 30002 - HTTP 401
Either way, do not process any txns[] — see the full catalog at Seamless codes
Where the credentials come from
- best-system issues your
api_key+secretvia the bo screen: Api Management → API Credential - Your
seamless_callback_url(the base URL of all endpoints) is registered together with that credential - Keep the
secretserver-side only — never embed it in a client/mobile app; rotate it by coordinating with the best-system team