Skip to main content

Authentication

All API requests require authentication using a Bearer token in the Authorization header.

API Keys

You’ll receive an API key when you register as a partner. API keys look like:
pk_live_a1b2c3d4e5f6g7h8i9j0...
Keep your API key secure. Never expose it in client-side code or public repositories.

Making Authenticated Requests

Include your API key in the Authorization header:
curl https://api.stablepay.global/v2/users \
  -H "Authorization: Bearer pk_live_your_api_key"

Environments

EnvironmentBase URLKey Prefix
Productionhttps://api.stablepay.globalpk_live_
Sandboxhttps://sandbox.api.stablepay.globalpk_sandbox_
Use sandbox keys for testing. Sandbox transactions don’t result in real payouts.

Rate Limits

Endpoint TypeLimit
General API100 requests/minute
Transaction Creation10 requests/minute
Rate limit headers are included in every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704456000

Error Responses

Authentication errors return 401 Unauthorized:
{
  "error": "Unauthorized",
  "message": "Invalid or missing API key"
}
Rate limit errors return 429 Too Many Requests:
{
  "error": "Rate Limit Exceeded",
  "message": "Too many requests. Please try again later.",
  "retryAfter": 60
}

IP Whitelisting (Optional)

For additional security, you can whitelist IP addresses in the Dashboard. When enabled, requests from non-whitelisted IPs will be rejected.

Webhook Signature Verification

Webhooks are signed using HMAC-SHA256. Verify signatures to ensure webhooks are from StablePay:
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  // Parse signature header: t=timestamp,v1=signature
  const parts = signature.split(',');
  const timestamp = parts[0].replace('t=', '');
  const receivedSig = parts[1].replace('v1=', '');

  // Check timestamp tolerance (5 minutes)
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false;
  }

  // Compute expected signature
  const message = `${timestamp}.${payload}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');

  // Timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(expectedSig),
    Buffer.from(receivedSig)
  );
}