Authentication

Skakio uses a two-step authentication flow:

  1. API Key → Exchange for JWT token
  2. JWT Token → Use for API requests

This provides security (short-lived tokens) while being simple to implement.

API Key Types

TypePrefixUse CasePermissions
Publicpk_live_Client-side apps, browsersRead-only catalog access
Secretsk_live_Server-side apps, backendsFull read/write access
Testpk_test_, sk_test_Development/testingSame 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
}
ClaimDescription
api_key_idID of the API key used
account_idYour account ID
key_typepublic or secret
storesStore IDs this key can access
permissionsGranted permissions
expExpiration timestamp
iatIssued 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

EnvironmentStorage Method
BrowserEnvironment variables at build time
Node.jsEnvironment variables
MobileSecure storage (Keychain/Keystore)
ServerlessSecrets manager (AWS Secrets, etc.)

Never commit API keys to source control.

Error Responses

StatusError CodeMeaning
401MISSING_API_KEYX-API-Key header required
401INVALID_API_KEYAPI key not found or invalid
401EXPIRED_API_KEYAPI key has expired
401REVOKED_API_KEYAPI key was revoked
401INVALID_TOKENToken is malformed
401TOKEN_EXPIREDToken has expired