Authentication
Wakapay uses API keys and Bearer tokens to authenticate requests to the API. This guide explains how to securely authenticate your API calls.
Overview
Wakapay supports two authentication methods:
- API Key Authentication - Used to obtain access tokens
- Bearer Token Authentication - Used for all API requests
Keep your credentials secure! Never commit API keys to version control or share them publicly. Treat them like passwords.
Step 1: Obtain an Access Token
Use your API credentials to obtain an access token:
Request
POST /business/auth
Content-Type: application/json
{
"apiKey": "pk_test_****123",
"apiSecret": "sk_test_****456"
}Token Request Example
curl -X POST https://api.test.wakapay.io/business/auth \
-H "Content-Type: application/json" \
-d '{
"apiKey": "pk_test_****123",
"apiSecret": "sk_test_****456"
}'Response
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.****...****",
"expiresIn": 3600
}Step 2: Use the Access Token
Include the access token in the Authorization header of all API requests:
Authorization: Bearer YOUR_ACCESS_TOKENAuthenticated Request Example
curl https://api.test.wakapay.io/business/balance \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."Token Management
Token Expiration
Access tokens expire after 1 hour (3600 seconds). When a token expires, you’ll receive a 401 Unauthorized response:
{
"message": "token is malformed: token contains an invalid number of segments"
}Refresh Tokens
When your token expires, simply request a new one using your API credentials. There’s no separate refresh token mechanism.
Best Practices
- Cache tokens: Store tokens in memory and reuse them until they expire
- Handle expiration: Implement automatic token refresh when you receive a 401 error
- Don’t request unnecessarily: Request new tokens only when needed to avoid rate limits
Security Best Practices
Secure Storage
- Environment variables: Store credentials in environment variables, not in code
- Secret management: Use secret management services (AWS Secrets Manager, HashiCorp Vault)
- Never log: Don’t log credentials or tokens in application logs
// Good
const apiKey = process.env.WAKAPAY_API_KEY;
// Bad
const apiKey = "pk_test_****123";API Key Types
Wakapay provides two types of API keys:
| Type | Prefix | Environment | Use Case |
|---|---|---|---|
| Test | yeuc... | Test/Sandbox | Development and testing |
| Live | Custom | Production | Real transactions |
Example Test API Key: yeuc****...****bbe3
Always use test keys during development. Switch to live keys only when you’re ready for production.
Authentication Errors
Invalid Credentials
{
"code": 0,
"error": "invalid credentials"
}HTTP Status: 401
Causes:
- Wrong
apiKey - Wrong
apiSecret - Credentials have been revoked
Missing Credentials
{
"code": 0,
"error": "apiKey and apiSecret required"
}HTTP Status: 400
Causes:
- Missing
apiKeyfield - Missing
apiSecretfield - Empty
apiKeyorapiSecret
Missing Authorization Header
{
"message": "missing value in request header"
}HTTP Status: 401
Cause: No Authorization header in request
Malformed Token
{
"message": "token is malformed: token contains an invalid number of segments"
}HTTP Status: 401
Causes:
- Invalid JWT format
- Corrupted token
- Expired token
Invalid Payload
{
"code": 0,
"error": "invalid payload"
}HTTP Status: 400
Causes:
- Malformed JSON in request body
- Wrong
Content-Typeheader (must beapplication/json)
Implementation Examples
Node.js
const axios = require("axios");
class WakapayClient {
constructor(apiKey, apiSecret, environment = "test") {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseURL =
environment === "production"
? "https://api.wakapay.io"
: "https://api.test.wakapay.io";
this.token = null;
this.tokenExpiry = null;
}
async authenticate() {
const response = await axios.post(`${this.baseURL}/business/auth`, {
apiKey: this.apiKey,
apiSecret: this.apiSecret,
});
this.token = response.data.data.accessToken;
this.tokenExpiry = Date.now() + response.data.data.expiresIn * 1000;
return this.token;
}
async getToken() {
// Return cached token if still valid
if (this.token && Date.now() < this.tokenExpiry - 60000) {
return this.token;
}
// Otherwise, get a new token
return await this.authenticate();
}
async request(method, endpoint, data = null) {
const token = await this.getToken();
const config = {
method,
url: `${this.baseURL}${endpoint}`,
headers: {
Authorization: "Bearer " + token,
"Content-Type": "application/json",
},
};
if (data) {
config.data = data;
}
try {
const response = await axios(config);
return response.data;
} catch (error) {
if (error.response?.status === 401) {
// Token expired, retry once with new token
this.token = null;
return await this.request(method, endpoint, data);
}
throw error;
}
}
}
// Usage
const client = new WakapayClient(
process.env.WAKAPAY_API_KEY,
process.env.WAKAPAY_API_SECRET,
"test",
);
// Make authenticated requests
const balance = await client.request("GET", "/business/balance");Python
import os
import time
import requests
from datetime import datetime, timedelta
class WakapayClient:
def __init__(self, api_key, api_secret, environment='test'):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = (
'https://api.wakapay.io' if environment == 'production'
else 'https://api.test.wakapay.io'
)
self.token = None
self.token_expiry = None
def authenticate(self):
response = requests.post(
f'{self.base_url}/business/auth',
json={
'apiKey': self.api_key,
'apiSecret': self.api_secret
}
)
response.raise_for_status()
data = response.json()['data']
self.token = data['accessToken']
self.token_expiry = datetime.now() + timedelta(seconds=data['expiresIn'])
return self.token
def get_token(self):
# Return cached token if still valid (with 60s buffer)
if self.token and datetime.now() < self.token_expiry - timedelta(seconds=60):
return self.token
# Otherwise, get a new token
return self.authenticate()
def request(self, method, endpoint, data=None):
token = self.get_token()
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
url = f'{self.base_url}{endpoint}'
try:
if method == 'GET':
response = requests.get(url, headers=headers)
elif method == 'POST':
response = requests.post(url, json=data, headers=headers)
elif method == 'PUT':
response = requests.put(url, json=data, headers=headers)
elif method == 'DELETE':
response = requests.delete(url, headers=headers)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
# Token expired, retry once with new token
self.token = None
return self.request(method, endpoint, data)
raise
# Usage
client = WakapayClient(
os.getenv('WAKAPAY_API_KEY'),
os.getenv('WAKAPAY_API_SECRET'),
'test'
)
# Make authenticated requests
balance = client.request('GET', '/business/balance')Testing Authentication
Test your authentication implementation:
# 1. Get a token
TOKEN=$(curl -s -X POST https://api.test.wakapay.io/business/auth \
-H "Content-Type: application/json" \
-d '{
"apiKey": "YOUR_API_KEY",
"apiSecret": "YOUR_API_SECRET"
}' | jq -r '.data.accessToken')
# 2. Use the token
curl https://api.test.wakapay.io/business/balance \
-H "Authorization: Bearer $TOKEN"Next Steps
Now that you understand authentication, explore:
- Wallet & Balance - Check your account balance
- Payouts - Send payments
- Transactions - Track transaction status