16 KiB
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
- Authentication
- Configuration
- API Endpoints
- Request/Response Format
- Rate Limiting
- Error Handling
- Examples
- 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:
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:
-
Obtain JWT Token:
POST /api/external/auth/validate Authorization: Bearer sk_live_your_api_key_here -
Response:
{ "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" } } -
Use JWT Token:
GET /api/external/users Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Token Validation
Validate your current authentication:
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
# 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
# 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
{
"success": true,
"data": {
// Response data here
},
"meta": {
"timestamp": "2026-02-20T10:00:00.000Z",
"requestId": "req_abc123"
}
}
Paginated Response
{
"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:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 856
X-RateLimit-Reset: 1708407600
Rate Limit Exceeded Response
{
"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
{
"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
curl -X POST https://your-domain.com/api/external/auth/validate \
-H "Authorization: Bearer sk_live_your_api_key_here"
List Users
curl -X GET "https://your-domain.com/api/external/users?page=1&perPage=10" \
-H "Authorization: Bearer your_jwt_token_here"
Update User Plan
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
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
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
// 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
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
-
Never commit API keys to version control
- Use environment variables
- Use secrets management (Docker secrets, Kubernetes secrets, AWS Secrets Manager)
-
Use different keys for different environments
sk_test_prefix for development/testingsk_live_prefix for production
-
Rotate keys periodically
- Generate new keys at least every 90 days
- Immediately revoke compromised keys
-
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
-
Short token lifetime
- Default: 1 hour
- Configure via
ADMIN_API_JWT_TTL
-
Token storage
- Store tokens in memory, not persistent storage
- Clear tokens on application shutdown
-
Token refresh
- Implement token refresh before expiration
- Handle
TOKEN_EXPIREDerrors gracefully
Network Security
-
Use HTTPS
- Never transmit API keys over HTTP
- Ensure SSL/TLS certificates are valid
-
IP Restrictions (if applicable)
- Restrict API access to known IP ranges
- Use VPN or private networks
-
Rate Limiting
- Respect rate limit headers
- Implement client-side throttling
- Handle 429 responses with exponential backoff
Audit & Monitoring
-
Monitor API Usage
- Review audit logs regularly
- Set up alerts for unusual activity
-
Log Retention
- Maintain audit logs for compliance
- Use
/system/audit-logendpoint to query logs
-
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:
export ADMIN_API_KEY=sk_live_your_secure_key_here
"INVALID_API_KEY"
Cause: API key doesn't match the configured key.
Solution:
- Verify the API key is correct
- Check for extra whitespace or encoding issues
- Ensure the key prefix is correct (
sk_live_orsk_test_)
"TOKEN_EXPIRED"
Cause: JWT token has exceeded its lifetime.
Solution: Request a new token using the API key:
POST /api/external/auth/validate
Authorization: Bearer sk_live_your_api_key
"RATE_LIMIT_EXCEEDED"
Cause: Too many requests in the current hour.
Solution:
- Wait until the reset time (check
X-RateLimit-Resetheader) - Reduce request frequency
- Increase rate limit via
ADMIN_API_RATE_LIMITenvironment variable
Debugging Tips
-
Enable verbose logging
- Check server logs for detailed error messages
- Look for
[ExternalAPI]prefixed log entries
-
Test with health endpoint
curl https://your-domain.com/api/external/system/health -
Verify authentication
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