Authentication
Skakio uses a two-step authentication flow:
- API Key → Exchange for JWT token
- JWT Token → Use for API requests
This provides security (short-lived tokens) while being simple to implement.
API Key Types
| Type | Prefix | Use Case | Permissions |
|---|---|---|---|
| Public | pk_live_ | Client-side apps, browsers | Read-only catalog access |
| Secret | sk_live_ | Server-side apps, backends | Full read/write access |
| Test | pk_test_, sk_test_ | Development/testing | Same as live, isolated data |
Security Rule: Never expose secret keys (sk_) in client-side code. Use public keys for browsers and mobile apps.
Token Exchange Flow
┌─────────────────────────────────────────────────────────────────┐
│ AUTHENTICATION FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Client Skakio API │
│ │ │ │
│ │ 1. POST /auth/token │ │
│ │ X-API-Key: pk_live_xxx │ │
│ │ ────────────────────────────────>│ │
│ │ │ │
│ │ 2. Response: │ │
│ │ { token: "eyJ...", expires_in: 900 } │
│ │ <────────────────────────────────│ │
│ │ │ │
│ │ 3. GET /api/store/123 │ │
│ │ Authorization: Bearer eyJ... │ │
│ │ ────────────────────────────────>│ │
│ │ │ │
│ │ 4. Response: { store data } │ │
│ │ <────────────────────────────────│ │
│ │ │ │
│ │ [After 15 min, token expires] │ │
│ │ [Refresh or get new token] │ │
│ │ │ │
└─────────────────────────────────────────────────────────────────┘
Getting a Token
Request:
curl -X POST https://api.skakio.com/auth/token \
-H "X-API-Key: pk_live_a1b2c3d4e5f6g7h8"
Response:
{
"code": 200,
"status": "OK",
"data": {
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 900
}
}
Custom TTL
Request a longer or shorter token lifetime (1-60 minutes):
curl -X POST https://api.skakio.com/auth/token \
-H "X-API-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "Content-Type: application/json" \
-d '{"ttl_minutes": 30}'
Token Contents (JWT Claims)
The JWT token contains claims about your API key:
{
"api_key_id": "key_abc123",
"account_id": "acc_xyz789",
"key_type": "public",
"stores": ["store_1", "store_2"],
"permissions": ["read:publications", "read:listings"],
"exp": 1707440100,
"iat": 1707439200
}
| Claim | Description |
|---|---|
api_key_id | ID of the API key used |
account_id | Your account ID |
key_type | public or secret |
stores | Store IDs this key can access |
permissions | Granted permissions |
exp | Expiration timestamp |
iat | Issued at timestamp |
Refreshing Tokens
Use /auth/refresh to get a new token without the API key:
curl -X POST https://api.skakio.com/auth/refresh \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Important: The token must still be valid (not expired) to refresh. Implement proactive refresh before expiry.
Token Validation
Check if a token is valid and get its claims:
curl -X POST https://api.skakio.com/auth/validate \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response:
{
"code": 200,
"status": "OK",
"data": {
"valid": true,
"api_key_id": "key_abc123",
"account_id": "acc_xyz789",
"key_type": "public",
"stores": ["store_1"],
"permissions": ["read:publications"],
"expires_at": "2026-02-09T10:15:00Z"
}
}
Best Practices
1. Cache Tokens
Don’t request a new token for every API call. Cache and reuse:
let cachedToken = null;
let tokenExpiry = 0;
async function getToken() {
const now = Date.now();
const buffer = 60 * 1000; // 1 minute buffer
if (!cachedToken || now > tokenExpiry - buffer) {
const res = await fetch('/auth/token', {
method: 'POST',
headers: { 'X-API-Key': API_KEY }
});
const { data } = await res.json();
cachedToken = data.token;
tokenExpiry = now + (data.expires_in * 1000);
}
return cachedToken;
}
2. Handle 401 Errors
When you receive a 401, refresh your token:
async function apiRequest(path) {
let res = await fetch(path, {
headers: { 'Authorization': `Bearer ${await getToken()}` }
});
if (res.status === 401) {
// Force token refresh
cachedToken = null;
res = await fetch(path, {
headers: { 'Authorization': `Bearer ${await getToken()}` }
});
}
return res.json();
}
3. Secure Key Storage
| Environment | Storage Method |
|---|---|
| Browser | Environment variables at build time |
| Node.js | Environment variables |
| Mobile | Secure storage (Keychain/Keystore) |
| Serverless | Secrets manager (AWS Secrets, etc.) |
Never commit API keys to source control.
Error Responses
| Status | Error Code | Meaning |
|---|---|---|
| 401 | MISSING_API_KEY | X-API-Key header required |
| 401 | INVALID_API_KEY | API key not found or invalid |
| 401 | EXPIRED_API_KEY | API key has expired |
| 401 | REVOKED_API_KEY | API key was revoked |
| 401 | INVALID_TOKEN | Token is malformed |
| 401 | TOKEN_EXPIRED | Token has expired |