Files
shopify-ai-backup/EXTERNAL_ADMIN_API.md
2026-02-20 18:40:54 +00:00

678 lines
16 KiB
Markdown

# 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 <your_token>
```
---
## 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