Skip to main content

Authentication & Security

Authentication & Security

keshflippay partner APIs use API-key based HMAC signatures. All authenticated routes require the headers shown below. Some routes additionally enforce partner scopes and rate limits.


Request Headers

Send these headers with every authenticated request:

HeaderDescription
X-API-KeyYour partner API key.
X-TimestampUNIX timestamp in seconds.
X-SignatureHMAC-SHA256 of the string `METHOD
  • BODY must be the exact JSON string sent with the request (empty string for GET).
  • PATH is the request path with version prefix, e.g. /api/v1/crypto/withdrawals.

Signature Algorithm

Build the message and compute the signature as follows:

  1. Construct
    stringToSign = METHOD + '|' + PATH + '|' + TIMESTAMP + '|' + BODY
  2. Compute
    signature = HMAC_SHA256(apiSecret, stringToSign).hex()
  3. Send the result in the X-Signature header.
sign.ts
import crypto from "crypto";

export function sign({
method,
path,
body = "",
apiSecret,
timestamp = Math.floor(Date.now() / 1000).toString(),
}: {
method: string;
path: string; // e.g. "/api/v1/crypto/withdrawals"
body?: string; // exact JSON string ("" for GET)
apiSecret: string;
timestamp?: string;
}) {
const stringToSign = `${method}|${path}|${timestamp}|${body}`;
const signature = crypto
.createHmac("sha256", apiSecret)
.update(stringToSign)
.digest("hex");

return { timestamp, signature };
}

Scopes

Some endpoints require specific partner scopes. Examples:

  • address:create — create crypto deposit addresses
  • address:read — list partner addresses
  • withdrawal:create — initiate withdrawals
  • transaction:read — verify transactions

If a request is missing the required scope, the API returns 403 Forbidden.


Rate Limits

Per-partner hourly limits are enforced. Responses include rate-limit headers:

HeaderMeaning
X-RateLimit-LimitMaximum requests allowed in the window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetSeconds until the window resets.
X-RateLimit-WindowWindow size in seconds.

Exceeding limits returns 429 Too Many Requests with machine-readable details.


Replay Protection and Timestamps

  • Requests with timestamps outside the allowed skew window are rejected.
  • Use a fresh timestamp per request.
  • If you implement nonces, ensure nonce uniqueness per request and avoid reusing signed payloads.
  • Keep server clocks in sync (e.g., via NTP) to prevent timestamp out of range errors.

Key Rotation

  • New secrets can temporarily coexist with old secrets during a rotation window.
  • Rotate secrets regularly and update your deployment environments atomically.

Webhook Security

Outbound webhooks are signed. Validate all incoming webhooks before processing.

HeaderDescription
X-Webhook-EventEvent name (e.g., crypto.deposit.updated).
X-Webhook-TimestampEvent timestamp.
X-Webhook-SignatureHMAC-SHA256 over the raw JSON payload using your webhookSecret.

Always verify against the raw request body and use a constant-time compare.

webhooks.ts
import crypto from "crypto";
import express from "express";

const app = express();

app.post("/webhooks/keshflippay",
express.json({ verify: (req, _res, buf) => { (req as any).rawBody = buf; } }),
(req, res) => {
const signature = req.headers["x-webhook-signature"] as string | undefined;
const payload = (req as any).rawBody?.toString("utf8") ?? JSON.stringify(req.body);

const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET!)
.update(payload)
.digest("hex");

const valid = crypto.timingSafeEqual(
Buffer.from(signature ?? "", "utf8"),
Buffer.from(expected, "utf8")
);

if (!valid) return res.status(401).json({ success: false, message: "Invalid signature" });
return res.json({ success: true });
}
);

app.listen(3000);

Error Responses (Summary)

Common HTTP status codes:

CodeMeaning
400Validation error (malformed payload or missing fields).
401Authentication failed (invalid signature/headers).
403Forbidden (inactive partner, missing scope, or KYC not verified).
404Not found.
409Conflict (idempotency or duplicate request).
429Rate limit exceeded.
5xxServer errors.

See Errors & Troubleshooting for details.


Next Steps