Skip to content

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}\n{method}\n{path}\n{query}\n{body}
  • timestamp — Unix epoch seconds (string), same value as X-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.