Authentication¶
All API requests to the Bitlipa Settlement API require authentication. Requests without valid authentication return 401 Unauthorized.
API key¶
Include your API key in the Authorization header of every request.
| Header | Type | Required | Description |
|---|---|---|---|
Authorization |
string | Yes | API key used to authenticate the request. |
Content-Type |
string | Yes | Must be application/json. |
Request signing (HMAC-SHA256)¶
Bitlipa also provides an API secret used to generate HMAC signatures. All requests must be signed in addition to providing the API key.
Additional headers¶
| Header | Type | Required | Description |
|---|---|---|---|
X-Bitlipa-Timestamp |
string | Yes | Unix epoch seconds. Must be within 5 minutes of server time. |
X-Bitlipa-Nonce |
string | Yes | UUIDv4, unique per request to prevent replay. |
X-Bitlipa-Signature |
string | Yes | Hex-encoded HMAC-SHA256 signature of the signing string. |
Signing string¶
Components are joined by a literal newline character (ASCII 0x0a):
timestamp— Unix epoch seconds (string), same value asX-Bitlipa-Timestamp.method— HTTP method in uppercase (e.g.,POST).path— request path (e.g.,/api/v1/settlements).query— raw URL-encoded query string exactly as sent, without the leading?(empty string if none). Parameter order must match the request URL.body— raw request body bytes interpreted as a UTF-8 string. For JSON requests, this is the exact JSON string sent on the wire (do not reorder keys or change whitespace after signing). Use empty string for requests with no body (e.g.,GET).
Compute HMAC_SHA256(api_secret, signing_string) and hex-encode the result for X-Bitlipa-Signature.
Example¶
import hashlib
import hmac
import json
import time
import uuid
import requests
API_KEY = "pk_live_xxx"
API_SECRET = b"sk_live_xxx"
BASE_URL = "https://api.bitlipa.com"
method = "POST"
path = "/api/v1/settlements"
query = ""
body_obj = {
"source_amount": 100000,
"source_currency": "KES",
"destination_currency": "USDT",
"external_merchant_id": "merchant_123",
"chain": "eip155:137",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
}
body = json.dumps(body_obj, separators=(",", ":"))
timestamp = str(int(time.time()))
nonce = str(uuid.uuid4())
signing_string = f"{timestamp}\n{method}\n{path}\n{query}\n{body}"
signature = hmac.new(API_SECRET, signing_string.encode(), hashlib.sha256).hexdigest()
resp = requests.post(
BASE_URL + path,
data=body,
headers={
"Authorization": API_KEY,
"Content-Type": "application/json",
"X-Bitlipa-Timestamp": timestamp,
"X-Bitlipa-Nonce": nonce,
"X-Bitlipa-Signature": signature,
},
timeout=10,
)
print(resp.json())
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen)
BODY='{"source_amount":100000,"source_currency":"KES","destination_currency":"USDT","external_merchant_id":"merchant_123","chain":"eip155:137","wallet_address":"0x742d35Cc6634C0532925a3b844Bc454e4438f44e"}'
SIGNING_STRING=$(printf '%s\n%s\n%s\n%s\n%s' "$TIMESTAMP" "POST" "/api/v1/settlements" "" "$BODY")
SIG=$(printf '%s' "$SIGNING_STRING" | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $2}')
curl -X POST https://api.bitlipa.com/api/v1/settlements \
-H "Authorization: $API_KEY" \
-H "Content-Type: application/json" \
-H "X-Bitlipa-Timestamp: $TIMESTAMP" \
-H "X-Bitlipa-Nonce: $NONCE" \
-H "X-Bitlipa-Signature: $SIG" \
-d "$BODY"
Sign the exact bytes you send
Do not reorder JSON keys or alter whitespace after signing. The signature is computed over the exact body string transmitted on the wire.