# External Admin API Documentation ## Overview The External Admin API provides programmatic access to all admin functionality via a RESTful API. This enables automation, integrations with external tools, and custom admin dashboards. ## Table of Contents 1. [Authentication](#authentication) 2. [Configuration](#configuration) 3. [API Endpoints](#api-endpoints) 4. [Request/Response Format](#requestresponse-format) 5. [Rate Limiting](#rate-limiting) 6. [Error Handling](#error-handling) 7. [Examples](#examples) 8. [Security Best Practices](#security-best-practices) --- ## Authentication The External Admin API supports two authentication methods: ### Option 1: API Key (Direct) Include your API key in the `Authorization` header with the `Bearer` scheme: ```http Authorization: Bearer sk_live_your_api_key_here ``` **Key Format:** - Production keys: `sk_live_` prefix - Test keys: `sk_test_` prefix ### Option 2: API Key → JWT Token (Recommended) For better performance, exchange your API key for a short-lived JWT token: 1. **Obtain JWT Token:** ```http POST /api/external/auth/validate Authorization: Bearer sk_live_your_api_key_here ``` 2. **Response:** ```json { "success": true, "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "tokenType": "Bearer", "expiresIn": 3600, "expiresAt": "2026-02-20T11:00:00.000Z" }, "meta": { "timestamp": "2026-02-20T10:00:00.000Z", "requestId": "req_abc123" } } ``` 3. **Use JWT Token:** ```http GET /api/external/users Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` ### Token Validation Validate your current authentication: ```http GET /api/external/auth/me Authorization: Bearer ``` --- ## Configuration ### Environment Variables Add these to your `.env` file or Docker environment: | Variable | Description | Default | Required | |----------|-------------|---------|----------| | `ADMIN_API_KEY` | Your secret API key | - | Yes (to enable API) | | `ADMIN_API_JWT_TTL` | JWT token lifetime in seconds | 3600 (1 hour) | No | | `ADMIN_API_RATE_LIMIT` | Requests per hour per key | 1000 | No | | `JWT_SECRET` | Secret for JWT signing | - | Yes | ### Docker Compose Setup ```yaml # docker-compose.yml services: shopify-ai-builder: environment: - ADMIN_API_KEY=${ADMIN_API_KEY:-} - ADMIN_API_JWT_TTL=${ADMIN_API_JWT_TTL:-3600} - ADMIN_API_RATE_LIMIT=${ADMIN_API_RATE_LIMIT:-1000} - JWT_SECRET=${JWT_SECRET:-} ``` ### Generating a Secure API Key ```bash # Generate a 32-byte hex key openssl rand -hex 32 # Example output: a1b2c3d4e5f6... (64 hex characters) # Set in .env: ADMIN_API_KEY=sk_live_a1b2c3d4e5f6... ``` --- ## API Endpoints ### Base URL All external API endpoints are prefixed with `/api/external`. ### Authentication | Method | Endpoint | Description | |--------|----------|-------------| | POST | `/auth/validate` | Exchange API key for JWT token | | GET | `/auth/me` | Get current authentication info | ### User Management | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/users` | List all users (paginated) | | GET | `/users/:id` | Get user by ID | | PATCH | `/users/:id/plan` | Update user's subscription plan | | PATCH | `/users/:id/tokens` | Adjust user's token allocation | | DELETE | `/users/:id` | Delete a user | | GET | `/users/:id/sessions` | List user's active sessions | | DELETE | `/users/:id/sessions/:sessionId` | Revoke a user session | ### Model Management | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/models` | List all configured models | | POST | `/models` | Create a new model configuration | | GET | `/models/:id` | Get model by ID | | PATCH | `/models/:id` | Update model configuration | | DELETE | `/models/:id` | Delete a model | | POST | `/models/reorder` | Reorder model display priority | ### Affiliate Management | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/affiliates` | List all affiliates (paginated) | | GET | `/affiliates/:id` | Get affiliate by ID | | DELETE | `/affiliates/:id` | Delete an affiliate | ### Withdrawal Management | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/withdrawals` | List withdrawal requests (paginated) | | PUT | `/withdrawals` | Update withdrawal status | ### Analytics & Monitoring | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/analytics/overview` | Get dashboard overview stats | | GET | `/analytics/tracking` | Get visitor tracking stats | | GET | `/analytics/resources` | Get resource monitoring data | | GET | `/analytics/usage` | Get token usage statistics | ### Content Management | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/blogs` | List all blogs (paginated) | | POST | `/blogs` | Create a new blog post | | GET | `/blogs/:id` | Get blog by ID | | PATCH | `/blogs/:id` | Update blog post | | DELETE | `/blogs/:id` | Delete a blog post | | GET | `/feature-requests` | List feature requests (paginated) | | PATCH | `/feature-requests/:id` | Update feature request status | | GET | `/contact-messages` | List contact messages (paginated) | | DELETE | `/contact-messages/:id` | Delete a contact message | ### System Operations | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/system/health` | Health check (no auth required) | | GET | `/system/tests` | Run system tests | | POST | `/system/tests` | Run specific system tests | | POST | `/system/cache/clear` | Clear server cache | | GET | `/system/audit-log` | Query audit log (paginated) | --- ## Request/Response Format ### Standard Response ```json { "success": true, "data": { // Response data here }, "meta": { "timestamp": "2026-02-20T10:00:00.000Z", "requestId": "req_abc123" } } ``` ### Paginated Response ```json { "success": true, "data": [ // Array of items ], "meta": { "timestamp": "2026-02-20T10:00:00.000Z", "requestId": "req_abc123" }, "pagination": { "page": 1, "perPage": 20, "total": 150, "totalPages": 8, "hasNext": true, "hasPrev": false } } ``` ### Query Parameters | Parameter | Description | Default | Max | |-----------|-------------|---------|-----| | `page` | Page number | 1 | - | | `perPage` | Items per page | 20 | 100 | | `search` | Search query | - | - | | `status` | Filter by status | - | - | | `plan` | Filter by plan | - | - | --- ## Rate Limiting All authenticated requests are rate-limited per API key. ### Default Limits - **Requests per hour:** 1000 (configurable via `ADMIN_API_RATE_LIMIT`) - **Burst:** Not applicable (smooth rate limiting) ### Rate Limit Headers Every response includes rate limit information: ```http X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 856 X-RateLimit-Reset: 1708407600 ``` ### Rate Limit Exceeded Response ```json { "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Please retry after the reset time.", "details": { "resetAt": "2026-02-20T11:00:00.000Z" } }, "meta": { "timestamp": "2026-02-20T10:45:00.000Z", "requestId": "req_xyz789" } } ``` --- ## Error Handling ### Error Response Format ```json { "success": false, "error": { "code": "ERROR_CODE", "message": "Human-readable error message", "details": { // Additional context } }, "meta": { "timestamp": "2026-02-20T10:00:00.000Z", "requestId": "req_abc123" } } ``` ### Error Codes | Code | HTTP Status | Description | |------|-------------|-------------| | `MISSING_AUTH_HEADER` | 401 | No Authorization header provided | | `INVALID_AUTH_SCHEME` | 401 | Invalid authorization scheme (use Bearer) | | `INVALID_API_KEY` | 401 | API key is invalid | | `TOKEN_EXPIRED` | 401 | JWT token has expired | | `INVALID_TOKEN` | 401 | JWT token is malformed or invalid | | `RATE_LIMIT_EXCEEDED` | 429 | Rate limit exceeded | | `ENDPOINT_NOT_FOUND` | 404 | Endpoint does not exist | | `METHOD_NOT_ALLOWED` | 405 | HTTP method not supported | | `VALIDATION_ERROR` | 400 | Request validation failed | | `NOT_FOUND` | 404 | Resource not found | | `INTERNAL_ERROR` | 500 | Internal server error | --- ## Examples ### cURL Examples #### Get JWT Token ```bash curl -X POST https://your-domain.com/api/external/auth/validate \ -H "Authorization: Bearer sk_live_your_api_key_here" ``` #### List Users ```bash curl -X GET "https://your-domain.com/api/external/users?page=1&perPage=10" \ -H "Authorization: Bearer your_jwt_token_here" ``` #### Update User Plan ```bash curl -X PATCH https://your-domain.com/api/external/users/user_123/plan \ -H "Authorization: Bearer your_jwt_token_here" \ -H "Content-Type: application/json" \ -d '{"plan": "professional"}' ``` #### Create Model ```bash curl -X POST https://your-domain.com/api/external/models \ -H "Authorization: Bearer your_jwt_token_here" \ -H "Content-Type: application/json" \ -d '{ "name": "gpt-4-turbo", "label": "GPT-4 Turbo", "tier": "premium", "supportsMedia": true }' ``` ### JavaScript/Node.js Examples #### Setup ```javascript const API_BASE = 'https://your-domain.com/api/external'; const API_KEY = 'sk_live_your_api_key_here'; let jwtToken = null; let tokenExpiresAt = null; async function getJwtToken() { if (jwtToken && tokenExpiresAt && Date.now() < tokenExpiresAt) { return jwtToken; } const response = await fetch(`${API_BASE}/auth/validate`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}` } }); const data = await response.json(); if (!data.success) { throw new Error(data.error.message); } jwtToken = data.data.token; tokenExpiresAt = new Date(data.data.expiresAt).getTime(); return jwtToken; } async function apiRequest(method, path, body = null) { const token = await getJwtToken(); const options = { method, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }; if (body) { options.body = JSON.stringify(body); } const response = await fetch(`${API_BASE}${path}`, options); return response.json(); } ``` #### Usage Examples ```javascript // List users const users = await apiRequest('GET', '/users?page=1&perPage=20'); console.log(users); // Get specific user const user = await apiRequest('GET', '/users/user_123'); console.log(user); // Update user plan const result = await apiRequest('PATCH', '/users/user_123/plan', { plan: 'professional' }); console.log(result); // Adjust user tokens const tokensResult = await apiRequest('PATCH', '/users/user_123/tokens', { tokens: 1000000, operation: 'set' }); console.log(tokensResult); // List models const models = await apiRequest('GET', '/models'); console.log(models); // Create model const newModel = await apiRequest('POST', '/models', { name: 'claude-3-opus', label: 'Claude 3 Opus', tier: 'premium', supportsMedia: true }); console.log(newModel); // Get analytics const analytics = await apiRequest('GET', '/analytics/overview'); console.log(analytics); ``` ### Python Examples ```python import requests from datetime import datetime API_BASE = 'https://your-domain.com/api/external' API_KEY = 'sk_live_your_api_key_here' class ExternalAdminAPI: def __init__(self, api_base, api_key): self.api_base = api_base self.api_key = api_key self.jwt_token = None self.token_expires_at = None def get_jwt_token(self): if self.jwt_token and self.token_expires_at: if datetime.now() < self.token_expires_at: return self.jwt_token response = requests.post( f'{self.api_base}/auth/validate', headers={'Authorization': f'Bearer {self.api_key}'} ) data = response.json() if not data['success']: raise Exception(data['error']['message']) self.jwt_token = data['data']['token'] self.token_expires_at = datetime.fromisoformat( data['data']['expiresAt'].replace('Z', '+00:00') ) return self.jwt_token def request(self, method, path, body=None): token = self.get_jwt_token() headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } response = requests.request( method, f'{self.api_base}{path}', headers=headers, json=body ) return response.json() # Usage api = ExternalAdminAPI(API_BASE, API_KEY) # List users users = api.request('GET', '/users?page=1&perPage=20') print(users) # Update user plan result = api.request('PATCH', '/users/user_123/plan', {'plan': 'professional'}) print(result) ``` --- ## Security Best Practices ### API Key Management 1. **Never commit API keys to version control** - Use environment variables - Use secrets management (Docker secrets, Kubernetes secrets, AWS Secrets Manager) 2. **Use different keys for different environments** - `sk_test_` prefix for development/testing - `sk_live_` prefix for production 3. **Rotate keys periodically** - Generate new keys at least every 90 days - Immediately revoke compromised keys 4. **Limit key exposure** - Only store keys on secure servers - Never expose keys in client-side code - Use IP whitelisting if possible (future feature) ### JWT Token Security 1. **Short token lifetime** - Default: 1 hour - Configure via `ADMIN_API_JWT_TTL` 2. **Token storage** - Store tokens in memory, not persistent storage - Clear tokens on application shutdown 3. **Token refresh** - Implement token refresh before expiration - Handle `TOKEN_EXPIRED` errors gracefully ### Network Security 1. **Use HTTPS** - Never transmit API keys over HTTP - Ensure SSL/TLS certificates are valid 2. **IP Restrictions** (if applicable) - Restrict API access to known IP ranges - Use VPN or private networks 3. **Rate Limiting** - Respect rate limit headers - Implement client-side throttling - Handle 429 responses with exponential backoff ### Audit & Monitoring 1. **Monitor API Usage** - Review audit logs regularly - Set up alerts for unusual activity 2. **Log Retention** - Maintain audit logs for compliance - Use `/system/audit-log` endpoint to query logs 3. **Anomaly Detection** - Monitor for unusual request patterns - Alert on failed authentication attempts --- ## Troubleshooting ### Common Issues #### "API_KEY_NOT_CONFIGURED" **Cause:** `ADMIN_API_KEY` environment variable not set. **Solution:** Set the environment variable and restart the server: ```bash export ADMIN_API_KEY=sk_live_your_secure_key_here ``` #### "INVALID_API_KEY" **Cause:** API key doesn't match the configured key. **Solution:** 1. Verify the API key is correct 2. Check for extra whitespace or encoding issues 3. Ensure the key prefix is correct (`sk_live_` or `sk_test_`) #### "TOKEN_EXPIRED" **Cause:** JWT token has exceeded its lifetime. **Solution:** Request a new token using the API key: ```bash POST /api/external/auth/validate Authorization: Bearer sk_live_your_api_key ``` #### "RATE_LIMIT_EXCEEDED" **Cause:** Too many requests in the current hour. **Solution:** 1. Wait until the reset time (check `X-RateLimit-Reset` header) 2. Reduce request frequency 3. Increase rate limit via `ADMIN_API_RATE_LIMIT` environment variable ### Debugging Tips 1. **Enable verbose logging** - Check server logs for detailed error messages - Look for `[ExternalAPI]` prefixed log entries 2. **Test with health endpoint** ```bash curl https://your-domain.com/api/external/system/health ``` 3. **Verify authentication** ```bash curl -X GET https://your-domain.com/api/external/auth/me \ -H "Authorization: Bearer your_token" ``` --- ## Version History | Version | Date | Changes | |---------|------|---------| | 1.0.0 | 2026-02-20 | Initial release | --- ## Support For issues or questions: - GitHub Issues: [project-repo]/issues - Documentation: This file - Server Logs: Check container logs for `[ExternalAPI]` entries