Authentication
Korala uses HMAC-SHA256 signature authentication to secure API requests. This provides a secure, stateless authentication mechanism that doesn’t require tokens to be stored or refreshed.
Overview
Every API request must include three headers:
| Header | Description | Example |
|---|---|---|
X-API-Key | Your API key ID | ak_live_abc123 |
X-Timestamp | Unix timestamp in seconds | 1704067200 |
X-Signature | HMAC-SHA256 signature | a1b2c3d4... |
Computing the Signature
The signature is computed by creating an HMAC-SHA256 hash of a specific message format using your API secret.
Message Format
{timestamp}.{METHOD}.{path}.{body}Where:
timestamp- The same Unix timestamp sent in theX-TimestampheaderMETHOD- The HTTP method in uppercase (GET, POST, PUT, DELETE)path- The request path including query string (e.g.,/api/v1/documents?limit=10)body- The request body as a string (empty string for GET requests)
Examples
TypeScript
import crypto from 'crypto';
function signRequest(
apiSecret: string,
method: string,
path: string,
body: string = ''
): { timestamp: string; signature: string } {
const timestamp = Math.floor(Date.now() / 1000).toString();
const message = `${timestamp}.${method}.${path}.${body}`;
const signature = crypto
.createHmac('sha256', apiSecret)
.update(message)
.digest('hex');
return { timestamp, signature };
}
// Usage
const { timestamp, signature } = signRequest(
'your-api-secret',
'POST',
'/api/v1/documents/upload-url',
JSON.stringify({ filename: 'contract.pdf', contentType: 'application/pdf' })
);Making Authenticated Requests
TypeScript
import crypto from 'crypto';
const API_KEY = 'your-api-key-id';
const API_SECRET = 'your-api-secret';
const BASE_URL = 'https://api.korala.ai';
async function apiRequest(method: string, path: string, body?: object) {
const bodyString = body ? JSON.stringify(body) : '';
const timestamp = Math.floor(Date.now() / 1000).toString();
const message = `${timestamp}.${method}.${path}.${bodyString}`;
const signature = crypto
.createHmac('sha256', API_SECRET)
.update(message)
.digest('hex');
const response = await fetch(`${BASE_URL}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
},
body: bodyString || undefined,
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
// Usage
const documents = await apiRequest('GET', '/api/v1/documents');Timestamp Validation
Requests with timestamps older than 5 minutes are rejected to prevent replay attacks. Ensure your server’s clock is synchronized with NTP.
Error Responses
| Status | Error | Description |
|---|---|---|
| 401 | missing_api_key | X-API-Key header is missing |
| 401 | missing_timestamp | X-Timestamp header is missing |
| 401 | missing_signature | X-Signature header is missing |
| 401 | invalid_api_key | API key not found or inactive |
| 401 | expired_timestamp | Timestamp is older than 5 minutes |
| 401 | invalid_signature | Signature doesn’t match |
Security Best Practices
- Never expose your API secret - Keep it server-side only
- Use environment variables - Don’t hardcode secrets in source code
- Rotate keys regularly - Create new keys and deprecate old ones
- Use separate keys per environment - Different keys for dev/staging/production
- Monitor key usage - Review audit logs for suspicious activity
Last updated on