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
| Environment | Base URL | Key Prefix |
|---|
| Production | https://api.stablepay.global | pk_live_ |
| Sandbox | https://sandbox.api.stablepay.global | pk_sandbox_ |
Use sandbox keys for testing. Sandbox transactions don’t result in real payouts.
Rate Limits
| Endpoint Type | Limit |
|---|
| General API | 100 requests/minute |
| Transaction Creation | 10 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)
);
}