AuthenticationAccess Tokens

Chapter 3: Access Tokens

Overview

Access tokens (API keys) are the primary authentication method for programmatic access to the Polysystems Backend API. This chapter provides comprehensive guidance on generating, managing, securing, and optimizing your access tokens.

What are Access Tokens?

Access tokens are long-lived credentials that authenticate API requests without requiring users to share their account passwords. Each token:

  • Is tied to a specific user account
  • Can be individually revoked or deleted
  • Has optional expiration dates
  • Can have spending limits configured
  • Tracks usage statistics independently
  • Works across all API endpoints

Benefits of Access Tokens

  1. Security: No password exposure in application code
  2. Granular Control: Different tokens for different applications
  3. Easy Revocation: Revoke compromised tokens without changing passwords
  4. Usage Tracking: Monitor which tokens are consuming resources
  5. Spending Limits: Set per-token budgets to control costs
  6. Long-Lived: No need for frequent re-authentication

Token Format

Access tokens follow a structured format:

ps_{environment}_{random_string}

Components:
├─ ps_          : Polysystems prefix
├─ {environment}: live/test environment indicator
└─ {random}     : 32+ character cryptographic random string

Examples

Production Token:
ps_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0

Development Token:
ps_test_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3h2

Generating Access Tokens

Basic Token Generation

Create a new access token using your JWT authentication:

curl -X POST https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Application Token"
  }'

Response:

{
  "id": "key-123e4567-e89b-12d3-a456-426614174000",
  "name": "My Application Token",
  "key_value": "ps_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "is_active": true,
  "created_at": "2024-01-15T12:00:00Z",
  "expires_at": null
}

⚠️ Critical: The key_value is only shown once during creation. Save it immediately in a secure location!

Token with Expiration

Create a token that automatically expires:

curl -X POST https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Temporary Access Token",
    "expires_at": "2024-12-31T23:59:59Z"
  }'

Use Cases for Expiring Tokens:

  • Short-term partner integrations
  • Time-limited demos or trials
  • Temporary contractor access
  • Testing and development
  • Security compliance requirements

Listing Access Tokens

View all access tokens associated with your account:

curl -X GET https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

[
  {
    "id": "key-123e4567-e89b-12d3-a456-426614174000",
    "name": "Production API Key",
    "key_value_preview": "ps_live_a1b2...o5p6",
    "is_active": true,
    "last_used_at": "2024-01-15T14:30:00Z",
    "created_at": "2024-01-01T10:00:00Z",
    "expires_at": null
  },
  {
    "id": "key-456e7890-e12b-34d5-a678-901234567890",
    "name": "Development Key",
    "key_value_preview": "ps_live_x9y8...k4j3",
    "is_active": true,
    "last_used_at": "2024-01-14T09:15:00Z",
    "created_at": "2024-01-05T15:30:00Z",
    "expires_at": "2024-06-30T23:59:59Z"
  },
  {
    "id": "key-789a0123-b45c-67d8-e901-234567890123",
    "name": "Old Testing Key",
    "key_value_preview": "ps_live_m3n4...b1c2",
    "is_active": false,
    "last_used_at": "2023-12-20T11:45:00Z",
    "created_at": "2023-11-10T08:00:00Z",
    "expires_at": null
  }
]

Understanding the Response

  • id: Unique identifier for the token (used for management operations)
  • name: Human-readable name you assigned
  • key_value_preview: Partial token value (first 12 and last 4 characters)
  • is_active: Whether the token is currently valid
  • last_used_at: Timestamp of most recent API call
  • created_at: When the token was generated
  • expires_at: Expiration date (null = never expires)

Revoking Access Tokens

Revoke a token to immediately invalidate it without deleting the record:

curl -X POST https://api.polysystems.ai/api/keys/{key_id}/revoke \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

{
  "success": true,
  "message": "Access key revoked successfully"
}

When to Revoke Tokens

  • Token may have been compromised
  • Employee/contractor access removal
  • Application decommissioned
  • Suspicious activity detected
  • Regular security audits
  • Rotation before deletion

Note: Revoked tokens can be viewed in your token list but cannot be reactivated. Generate a new token instead.

Deleting Access Tokens

Permanently remove a token from your account:

curl -X DELETE https://api.polysystems.ai/api/keys/{key_id} \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

{
  "success": true,
  "message": "Access key deleted successfully"
}

Revoke vs Delete

ActionResultHistory PreservedReversible
RevokeToken invalidatedYesNo
DeleteToken removedNoNo

Best Practice: Revoke tokens first, verify no issues, then delete after a grace period.

Using Access Tokens

curl -X POST https://api.polysystems.ai/api/hub/agents/chat \
  -H "X-API-Key: ps_live_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Hello, world!"}
    ]
  }'

Advantages:

  • Clear separation from OAuth/JWT Bearer tokens
  • Industry standard for API keys
  • Easy to identify in logs and monitoring

Method 2: Authorization Bearer Header

curl -X POST https://api.polysystems.ai/api/hub/agents/chat \
  -H "Authorization: Bearer ps_live_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Hello, world!"}
    ]
  }'

Advantages:

  • Compatible with OAuth 2.0 client libraries
  • Works with OpenAI-compatible clients (v1 endpoints)

Token Naming Strategy

Use descriptive names to easily identify tokens:

# Environment-based
"Production Server"
"Staging Environment"
"Development Local"
 
# Application-based
"Mobile App - iOS"
"Web Dashboard"
"Analytics Service"
 
# Purpose-based
"Data Ingestion Pipeline"
"Webhook Handler"
"Scheduled Reports"
 
# Team-based
"Engineering Team"
"Data Science Team"
"QA Automation"
 
# Combined approach (recommended)
"Production - Web Dashboard - v2"
"Staging - Mobile App - iOS"
"Dev - John's Local Environment"

Naming Best Practices

  • Include environment (prod/staging/dev)
  • Include application or service name
  • Add version numbers if applicable
  • Include team or owner if shared
  • Keep names concise but descriptive
  • Avoid sensitive information in names

Token Lifecycle Management

Complete Token Lifecycle

┌─────────────┐
│   CREATE    │ ← Generate new token
└──────┬──────┘


┌─────────────┐
│   ACTIVE    │ ← Token in use
└──────┬──────┘

       ├──────────────┐
       │              │
       ▼              ▼
┌─────────────┐  ┌──────────┐
│   EXPIRED   │  │ REVOKED  │ ← Manually deactivated
└──────┬──────┘  └────┬─────┘
       │              │
       └──────┬───────┘


       ┌─────────────┐
       │   DELETED   │ ← Permanently removed
       └─────────────┘

Rotation Strategy

Implement regular token rotation for enhanced security:

#!/bin/bash
# Token rotation script
 
# 1. Generate new token
NEW_TOKEN=$(curl -s -X POST https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"name\": \"Production - Rotated $(date +%Y-%m-%d)\"}" \
  | jq -r '.key_value')
 
# 2. Update application configuration
echo "PS_API_KEY=$NEW_TOKEN" > /secure/location/.env
 
# 3. Restart application services
systemctl restart myapp
 
# 4. Wait for verification period (24 hours)
sleep 86400
 
# 5. Revoke old token
curl -X POST https://api.polysystems.ai/api/keys/$OLD_KEY_ID/revoke \
  -H "Authorization: Bearer $JWT_TOKEN"
 
# 6. Delete old token after grace period
sleep 86400
curl -X DELETE https://api.polysystems.ai/api/keys/$OLD_KEY_ID \
  -H "Authorization: Bearer $JWT_TOKEN"
EnvironmentRotation FrequencyReason
ProductionEvery 90 daysBalance security and stability
StagingEvery 180 daysLess critical but still important
DevelopmentAs neededFrequently recreated anyway
Partner AccessEvery 30-60 daysThird-party security

Token Security Best Practices

1. Secure Storage

Environment Variables (Recommended)

# .env file (add to .gitignore!)
PS_API_KEY=ps_live_your_token_here
PS_API_URL=https://api.polysystems.ai

Secrets Management Systems

# AWS Secrets Manager
aws secretsmanager get-secret-value \
  --secret-id prod/polysystems/api-key \
  --query SecretString \
  --output text
 
# HashiCorp Vault
vault kv get -field=api_key secret/polysystems
 
# Kubernetes Secrets
kubectl get secret polysystems-api-key -o jsonpath='{.data.api-key}' | base64 -d

2. Never Commit Tokens

Bad Examples (Don’t Do This!)

# ❌ Hardcoded in source code
API_KEY = "ps_live_a1b2c3d4e5f6g7h8i9j0k1l2"
 
# ❌ In configuration files
config = {
    "api_key": "ps_live_a1b2c3d4e5f6g7h8i9j0k1l2"
}

Good Examples

# ✅ From environment variables
import os
API_KEY = os.getenv('PS_API_KEY')
 
# ✅ From secrets manager
from aws_secretsmanager import get_secret
API_KEY = get_secret('prod/polysystems/api-key')

3. Limit Token Scope

Create separate tokens for different purposes:

# Mobile app token (limited)
curl -X POST https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -d '{"name": "Mobile App Token"}'
 
# Then set spending limits
curl -X PUT https://api.polysystems.ai/api/keys/{key_id}/limits \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -d '{
    "daily_limit": 5.00,
    "monthly_limit": 100.00,
    "per_request_limit": 0.50
  }'

4. Monitor Token Usage

Regularly review token activity:

# List all tokens with usage stats
curl -X GET https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer $JWT_TOKEN" \
  | jq '.[] | {name, last_used_at, is_active}'

Red Flags to Watch For:

  • Tokens not used in 90+ days
  • Unexpected geographic access patterns
  • Unusual request volumes
  • Failed authentication attempts
  • Tokens from former employees

5. Incident Response Plan

If a token is compromised:

# IMMEDIATE: Revoke the token
curl -X POST https://api.polysystems.ai/api/keys/{key_id}/revoke \
  -H "Authorization: Bearer $JWT_TOKEN"
 
# Check recent usage
curl -X GET https://api.polysystems.ai/api/monitoring/usage?key_id={key_id} \
  -H "Authorization: Bearer $JWT_TOKEN"
 
# Review transactions
curl -X GET https://api.polysystems.ai/api/payments/transactions?limit=100 \
  -H "Authorization: Bearer $JWT_TOKEN"
 
# Generate new token
curl -X POST https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -d '{"name": "Replacement Token - Post Incident"}'

Access Token Limits and Quotas

Default Limits

  • Maximum tokens per account: 50
  • Token name length: 1-100 characters
  • Token expiration: Min 1 hour, Max 10 years (or never)

Request Limits

All tokens are subject to:

  • Rate limiting (varies by endpoint)
  • Spending limits (if configured)
  • Account balance (must have sufficient credits)

Advanced Token Management

Programmatic Token Management

Automate token lifecycle management:

import os
import requests
from datetime import datetime, timedelta
 
class TokenManager:
    def __init__(self, jwt_token):
        self.jwt_token = jwt_token
        self.base_url = "https://api.polysystems.ai"
        self.headers = {
            "Authorization": f"Bearer {jwt_token}",
            "Content-Type": "application/json"
        }
    
    def create_token(self, name, expires_days=None):
        """Create a new access token"""
        data = {"name": name}
        
        if expires_days:
            expires_at = datetime.utcnow() + timedelta(days=expires_days)
            data["expires_at"] = expires_at.isoformat() + "Z"
        
        response = requests.post(
            f"{self.base_url}/api/keys",
            headers=self.headers,
            json=data
        )
        return response.json()
    
    def list_tokens(self):
        """List all access tokens"""
        response = requests.get(
            f"{self.base_url}/api/keys",
            headers=self.headers
        )
        return response.json()
    
    def revoke_token(self, key_id):
        """Revoke an access token"""
        response = requests.post(
            f"{self.base_url}/api/keys/{key_id}/revoke",
            headers=self.headers
        )
        return response.json()
    
    def delete_old_tokens(self, days_inactive=90):
        """Delete tokens not used in X days"""
        tokens = self.list_tokens()
        cutoff = datetime.utcnow() - timedelta(days=days_inactive)
        
        for token in tokens:
            if token['last_used_at']:
                last_used = datetime.fromisoformat(
                    token['last_used_at'].replace('Z', '+00:00')
                )
                if last_used < cutoff:
                    print(f"Deleting inactive token: {token['name']}")
                    self.revoke_token(token['id'])
 
# Usage
manager = TokenManager(os.getenv('PS_JWT_TOKEN'))
new_token = manager.create_token("Automated Production Token", expires_days=90)
print(f"New token: {new_token['key_value']}")

Token Audit Script

#!/bin/bash
# audit_tokens.sh - Review and report on all access tokens
 
JWT_TOKEN=$PS_JWT_TOKEN
API_URL="https://api.polysystems.ai"
 
echo "=== Access Token Audit Report ==="
echo "Generated: $(date)"
echo ""
 
# Get all tokens
TOKENS=$(curl -s -X GET "$API_URL/api/keys" \
  -H "Authorization: Bearer $JWT_TOKEN")
 
# Count total tokens
TOTAL=$(echo "$TOKENS" | jq 'length')
ACTIVE=$(echo "$TOKENS" | jq '[.[] | select(.is_active == true)] | length')
INACTIVE=$(echo "$TOKENS" | jq '[.[] | select(.is_active == false)] | length')
 
echo "Total Tokens: $TOTAL"
echo "Active: $ACTIVE"
echo "Inactive: $INACTIVE"
echo ""
 
# Find tokens not used in 90 days
echo "=== Tokens Not Used in 90 Days ==="
CUTOFF=$(date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ)
echo "$TOKENS" | jq -r --arg cutoff "$CUTOFF" \
  '.[] | select(.last_used_at < $cutoff or .last_used_at == null) | 
  "\(.name) (Last used: \(.last_used_at // "Never"))"'
echo ""
 
# Find expiring tokens
echo "=== Tokens Expiring Soon (30 days) ==="
EXPIRE_SOON=$(date -u -d '30 days' +%Y-%m-%dT%H:%M:%SZ)
echo "$TOKENS" | jq -r --arg expire "$EXPIRE_SOON" \
  '.[] | select(.expires_at != null and .expires_at < $expire) | 
  "\(.name) (Expires: \(.expires_at))"'

Troubleshooting

Token Not Working

Problem: API returns “Invalid API key”

Solutions:

  1. Verify token is active: curl -X GET https://api.polysystems.ai/api/keys -H "Authorization: Bearer $JWT"
  2. Check for typos in token value
  3. Ensure proper header format: X-API-Key: ps_live_...
  4. Verify token hasn’t expired
  5. Check if token was revoked

Token Expired

Problem: API returns “API key expired”

Solution:

# Generate new token
curl -X POST https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -d '{"name": "Replacement Token"}'
 
# Update application configuration
# Revoke old token

Can’t Create More Tokens

Problem: “Maximum tokens limit reached”

Solution:

# List all tokens
curl -X GET https://api.polysystems.ai/api/keys \
  -H "Authorization: Bearer $JWT_TOKEN"
 
# Delete unused tokens
curl -X DELETE https://api.polysystems.ai/api/keys/{unused_key_id} \
  -H "Authorization: Bearer $JWT_TOKEN"

Token Lost or Forgotten

Problem: Can’t find saved token value

Solution:

  • Token values cannot be retrieved after creation
  • Revoke the old token and generate a new one
  • Update your application with the new token
  • Implement better secrets management going forward

Summary

In this chapter, you learned:

  • ✅ How to generate access tokens with and without expiration
  • ✅ Token format and structure
  • ✅ Listing, revoking, and deleting tokens
  • ✅ Best practices for token naming and organization
  • ✅ Security best practices for token storage and usage
  • ✅ Token lifecycle management and rotation strategies
  • ✅ Programmatic token management
  • ✅ Troubleshooting common token issues

Next Steps