Authentication
Learn how to authenticate your API requests using Bearer tokens and HMAC signatures
The API uses a two-layer authentication system combining Bearer tokens and HMAC signatures to ensure secure communication.
Authentication Methods
1. Bearer Token (Required)
The Bearer token identifies your application and user session.
2. HMAC Signature (Required for API Audience)
The HMAC signature verifies request integrity and authenticity.
Getting Your Credentials
- Log in to your dashboard
- Navigate to Settings → API Keys
- Generate a new API key
- Save your credentials:
- Access Token: For the
Authorizationheader - Secret Key: For generating HMAC signatures
- Access Token: For the
⚠️ Security Warning: Never expose your credentials in client-side code or commit them to version control.
Authentication Headers
Every API request must include:
Authorization: Bearer {access_token}
hmac: {hmac_signature}
Content-Type: application/jsonAudience Types
The API supports two audience types:
API Audience
For server-to-server communication:
- Required: Bearer token + HMAC signature
- Use Case: Backend integrations, automated processes
- Security: Highest level (two-factor authentication)
WEB Audience
For user-authenticated requests:
- Required: Bearer token + (HMAC or MFA token)
- Use Case: Web applications, mobile apps with user login
- Additional: May require MFA token for sensitive operations
Generating HMAC Signatures
HMAC Message Format
{HTTP_METHOD}{REQUEST_PATH}{JSON_BODY}Rules:
- HTTP Method: Uppercase (GET, POST, PUT, DELETE)
- Request Path: Full path including query parameters
- JSON Body: Compact JSON (no spaces) for POST/PUT requests
Examples:
GET Request:
Message: GET/v3/account/5001/balancePOST Request:
Message: POST/v3/cashout/create-cashout{"amount":100.50,"key":"user@example.com"}GET with Query Parameters:
Message: GET/v3/account/list-my-pix-keys?account_branch_identifier=0001&account_number=123456Implementation Examples
Python
import hmac
import hashlib
import json
def generate_hmac(secret_key, method, path, body=None):
"""
Generate HMAC signature for API request.
Args:
secret_key: Your API secret key
method: HTTP method (GET, POST, PUT, DELETE)
path: Request path including query parameters
body: Request body dict (for POST/PUT)
Returns:
HMAC signature (hexadecimal string)
"""
# Build message
message = f"{method.upper()}{path}"
# Add body for POST/PUT requests
if body:
# Compact JSON (no spaces)
message += json.dumps(body, separators=(',', ':'))
# Generate HMAC-SHA256
signature = hmac.new(
secret_key.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
# Example usage
secret_key = "your_secret_key"
method = "GET"
path = "/v3/account/5001/balance"
hmac_sig = generate_hmac(secret_key, method, path)
print(f"HMAC: {hmac_sig}")
# Example with POST body
method = "POST"
path = "/v3/cashout/create-cashout"
body = {
"amount": 100.50,
"key": "user@example.com",
"source_account_branch_identifier": "0001",
"source_account_number": "123456"
}
hmac_sig = generate_hmac(secret_key, method, path, body)
print(f"HMAC: {hmac_sig}")JavaScript (Node.js)
const crypto = require('crypto');
function generateHMAC(secretKey, method, path, body = null) {
/**
* Generate HMAC signature for API request.
*
* @param {string} secretKey - Your API secret key
* @param {string} method - HTTP method (GET, POST, PUT, DELETE)
* @param {string} path - Request path including query parameters
* @param {Object} body - Request body object (for POST/PUT)
* @returns {string} HMAC signature (hexadecimal string)
*/
// Build message
let message = `${method.toUpperCase()}${path}`;
// Add body for POST/PUT requests
if (body) {
// Compact JSON (no spaces)
message += JSON.stringify(body);
}
// Generate HMAC-SHA256
const signature = crypto
.createHmac('sha256', secretKey)
.update(message, 'utf8')
.digest('hex');
return signature;
}
// Example usage
const secretKey = 'your_secret_key';
const method = 'GET';
const path = '/v3/account/5001/balance';
const hmacSig = generateHMAC(secretKey, method, path);
console.log(`HMAC: ${hmacSig}`);
// Example with POST body
const postMethod = 'POST';
const postPath = '/v3/cashout/create-cashout';
const body = {
amount: 100.50,
key: 'user@example.com',
source_account_branch_identifier: '0001',
source_account_number: '123456'
};
const postHmacSig = generateHMAC(secretKey, postMethod, postPath, body);
console.log(`HMAC: ${postHmacSig}`);PHP
<?php
function generateHMAC($secretKey, $method, $path, $body = null) {
/**
* Generate HMAC signature for API request.
*
* @param string $secretKey Your API secret key
* @param string $method HTTP method (GET, POST, PUT, DELETE)
* @param string $path Request path including query parameters
* @param array $body Request body array (for POST/PUT)
* @return string HMAC signature (hexadecimal string)
*/
// Build message
$message = strtoupper($method) . $path;
// Add body for POST/PUT requests
if ($body !== null) {
// Compact JSON (no spaces)
$message .= json_encode($body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
// Generate HMAC-SHA256
$signature = hash_hmac('sha256', $message, $secretKey);
return $signature;
}
// Example usage
$secretKey = 'your_secret_key';
$method = 'GET';
$path = '/v3/account/5001/balance';
$hmacSig = generateHMAC($secretKey, $method, $path);
echo "HMAC: $hmacSig\n";
// Example with POST body
$postMethod = 'POST';
$postPath = '/v3/cashout/create-cashout';
$body = [
'amount' => 100.50,
'key' => 'user@example.com',
'source_account_branch_identifier' => '0001',
'source_account_number' => '123456'
];
$postHmacSig = generateHMAC($secretKey, $postMethod, $postPath, $body);
echo "HMAC: $postHmacSig\n";Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import com.google.gson.Gson;
import java.util.Map;
public class HMACGenerator {
public static String generateHMAC(String secretKey, String method, String path, Map<String, Object> body)
throws NoSuchAlgorithmException, InvalidKeyException {
/**
* Generate HMAC signature for API request.
*
* @param secretKey Your API secret key
* @param method HTTP method (GET, POST, PUT, DELETE)
* @param path Request path including query parameters
* @param body Request body map (for POST/PUT, null for GET/DELETE)
* @return HMAC signature (hexadecimal string)
*/
// Build message
StringBuilder message = new StringBuilder();
message.append(method.toUpperCase()).append(path);
// Add body for POST/PUT requests
if (body != null) {
Gson gson = new Gson();
message.append(gson.toJson(body));
}
// Generate HMAC-SHA256
Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8),
"HmacSHA256"
);
hmacSHA256.init(secretKeySpec);
byte[] signatureBytes = hmacSHA256.doFinal(
message.toString().getBytes(StandardCharsets.UTF_8)
);
// Convert to hex string
StringBuilder signature = new StringBuilder();
for (byte b : signatureBytes) {
signature.append(String.format("%02x", b));
}
return signature.toString();
}
// Example usage
public static void main(String[] args) {
try {
String secretKey = "your_secret_key";
String method = "GET";
String path = "/v3/account/5001/balance";
String hmacSig = generateHMAC(secretKey, method, path, null);
System.out.println("HMAC: " + hmacSig);
// Example with POST body
Map<String, Object> body = Map.of(
"amount", 100.50,
"key", "user@example.com",
"source_account_branch_identifier", "0001",
"source_account_number", "123456"
);
String postHmacSig = generateHMAC(secretKey, "POST", "/v3/cashout/create-cashout", body);
System.out.println("POST HMAC: " + postHmacSig);
} catch (Exception e) {
e.printStackTrace();
}
}
}Complete Request Examples
GET Request Example
import requests
import hmac
import hashlib
API_BASE = "https://api.qastech.com.br"
ACCESS_TOKEN = "your_access_token"
SECRET_KEY = "your_secret_key"
# Request parameters
account_id = 5001
method = "GET"
path = f"/v3/account/{account_id}/balance"
# Generate HMAC
message = f"{method}{path}"
hmac_signature = hmac.new(
SECRET_KEY.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
# Make request
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"hmac": hmac_signature,
"Content-Type": "application/json"
}
response = requests.get(f"{API_BASE}{path}", headers=headers)
print(response.json())POST Request Example
import requests
import hmac
import hashlib
import json
API_BASE = "https://api.qastech.com.br"
ACCESS_TOKEN = "your_access_token"
SECRET_KEY = "your_secret_key"
# Request parameters
method = "POST"
path = "/v3/cashout/create-cashout"
body = {
"source_account_branch_identifier": "0001",
"source_account_number": "123456",
"amount": 100.50,
"key": "recipient@example.com"
}
# Generate HMAC
message = f"{method}{path}{json.dumps(body, separators=(',', ':'))}"
hmac_signature = hmac.new(
SECRET_KEY.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
# Make request
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"hmac": hmac_signature,
"Content-Type": "application/json"
}
response = requests.post(f"{API_BASE}{path}", headers=headers, json=body)
print(response.json())Common Authentication Errors
401 Unauthorized
Cause: Invalid or expired access token
Solution:
- Verify your access token is correct
- Check if token has expired
- Generate a new token from the dashboard
403 Forbidden - Invalid HMAC
Cause: HMAC signature mismatch
Common Issues:
- Wrong message format: Ensure
{METHOD}{PATH}{BODY} - Spaces in JSON: Body must be compact JSON
- Wrong path: Include full path with query parameters
- Wrong secret key: Verify you're using the correct secret
- Encoding issues: Ensure UTF-8 encoding
Debug Steps:
# Print the message being signed
message = f"{method}{path}"
if body:
message += json.dumps(body, separators=(',', ':'))
print(f"Message to sign: {message}")
print(f"HMAC: {hmac_signature}")403 Forbidden - Insufficient Permissions
Cause: User doesn't have required permissions
Solution:
- Verify user has necessary permissions in dashboard
- Check if account belongs to your company
- Confirm policy access for the specific resource
MFA Token (WEB Audience)
For sensitive operations with WEB audience, you may need an MFA (Multi-Factor Authentication) token:
Operations Requiring MFA:
- Registering CPF/CNPJ/EVP PIX keys
- Approving cashout transactions (in some configurations)
- Modifying security settings
MFA Flow:
- User has MFA enabled in their account
- User generates TOTP code from authenticator app
- Include code in
tokenfield of request body
Example:
{
"source_account_branch_identifier": "0001",
"source_account_number": "123456",
"reason": "Register CPF key",
"type": "CPF",
"token": "123456"
}OTP Verification (EMAIL/CELL_PHONE Keys)
For EMAIL and CELL_PHONE PIX keys:
Verification Flow:
- Request OTP code: Call
/v3/key/verifications/send - Receive verification_request_id
- User receives code via email/SMS
- Include code and verification_request_id in registration request
Example:
{
"source_account_branch_identifier": "0001",
"source_account_number": "123456",
"reason": "Register email key",
"type": "EMAIL",
"verification_request_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"token": "123456"
}Best Security Practices
1. Store Credentials Securely
# ✅ Good: Use environment variables
import os
ACCESS_TOKEN = os.getenv('API_ACCESS_TOKEN')
SECRET_KEY = os.getenv('API_SECRET_KEY')
# ❌ Bad: Hardcode credentials
ACCESS_TOKEN = 'abc123...' # Never do this!2. Never Log Sensitive Data
# ✅ Good: Log without credentials
logger.info(f"API request: {method} {path}")
# ❌ Bad: Log credentials
logger.info(f"Token: {access_token}") # Never do this!3. Use HTTPS Only
# ✅ Good: HTTPS
API_BASE = "https://api.qastech.com.br"
# ❌ Bad: HTTP (insecure)
API_BASE = "http://api.example.com" # Never do this!4. Rotate Credentials Regularly
- Rotate API keys every 90 days
- Immediately rotate if compromised
- Use separate keys for different environments
5. Implement Rate Limiting
import time
from functools import wraps
def rate_limit(calls_per_second=10):
min_interval = 1.0 / calls_per_second
last_called = [0.0]
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
left_to_wait = min_interval - elapsed
if left_to_wait > 0:
time.sleep(left_to_wait)
ret = func(*args, **kwargs)
last_called[0] = time.time()
return ret
return wrapper
return decorator
@rate_limit(calls_per_second=10)
def make_api_call():
# Your API call here
passTesting Authentication
Test HMAC Generation
def test_hmac_generation():
secret_key = "test_key"
method = "GET"
path = "/v3/account/123/balance"
expected_message = "GET/v3/account/123/balance"
signature = generate_hmac(secret_key, method, path)
print(f"Message: {expected_message}")
print(f"Signature: {signature}")
# Verify signature is 64 characters (SHA-256 hex)
assert len(signature) == 64
print("✓ HMAC generation working correctly")
test_hmac_generation()Next Steps
Security Tip: If you suspect your credentials have been compromised, immediately rotate your keys from the dashboard and contact your support channel.