Authentication Methods
JWT Bearer Token
http
Authorization: Bearer <access_token>
Access tokens expire in 1 hour. Refresh tokens expire in 30 days.
API Key
http
X-API-Key: pa_your_api_key_here
Use for server-to-server integrations. Keys are shown only once on creation.
Tenant Context
http
X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000
Optional. Scopes requests to a specific tenant workspace.
Authentication Endpoints
POST
/api/v1/auth/registerRegister a new userRequest
json
{
"email": "user@example.com",
"password": "securePassword123",
"username": "johndoe",
"full_name": "John Doe"
}Response
json
// 201 Created
{
"message": "User registered successfully",
"user": { "id": 1, "email": "user@example.com", ... },
"access_token": "eyJhbGci...",
"refresh_token": "eyJhbGci...",
"token_type": "Bearer"
}Notes: Without X-Tenant-ID creates a platform admin user. With X-Tenant-ID creates a tenant-scoped user. Password must be at least 8 characters.
POST
/api/v1/auth/loginAuthenticate and get tokensRequest
json
{
"email": "user@example.com",
"password": "securePassword123"
}Response
json
// 200 OK
{
"message": "Login successful",
"user": { "id": 1, "email": "user@example.com", ... },
"access_token": "eyJhbGci...",
"refresh_token": "eyJhbGci...",
"token_type": "Bearer"
}POST
/api/v1/auth/refreshRefresh access tokenRequest
json
{
"refresh_token": "eyJhbGci..."
}Response
json
// 200 OK
{
"access_token": "eyJhbGci...",
"token_type": "Bearer"
}GET
/api/v1/auth/meGet current authenticated userResponse
json
// 200 OK
{
"user": {
"id": 1,
"email": "user@example.com",
"username": "johndoe",
"full_name": "John Doe",
"role": "Admin",
"is_platform_admin": false,
"tenant_id": "550e8400-...",
"created_at": "2025-01-01T00:00:00.000000"
}
}OAuth Endpoints
Supported Providers:
googlegithublinkedinfacebookGET
/api/v1/auth/oauth/{provider}Initiate OAuth flow — redirects to providerGET
/api/v1/auth/oauth/{provider}/callbackOAuth callback — handled automaticallyNotes: On success redirects to: {FRONTEND_URL}/oauth/callback?access_token=...&refresh_token=... — Creates new user if email doesn't exist; links to existing account if it does.
API Key Management
GET
/api/v1/auth/api-keysList all API keysResponse
json
// 200 OK
{
"api_keys": [
{
"id": 1,
"name": "My Production Key",
"prefix": "pa_123456",
"is_active": true,
"request_count": 42,
"last_used_at": "2025-01-01T12:00:00.000000"
}
]
}POST
/api/v1/auth/api-keysCreate a new API keyRequest
json
{ "name": "My Production Key" }Response
json
// 201 Created
{
"message": "API key created successfully",
"api_key": "pa_1234567890abcdef...",
"prefix": "pa_123456",
"warning": "Save this key securely. You will not be able to see it again."
}Notes: The full API key is only shown once on creation. Store it securely.
DELETE
/api/v1/auth/api-keys/{key_id}Delete an API keyResponse
json
// 200 OK
{ "message": "API key deleted successfully" }Profile Management
PATCH
/api/v1/auth/profileUpdate user profile (all fields optional)Request
json
{
"full_name": "John Doe",
"username": "johndoe",
"profile_image": "https://example.com/profile.jpg"
}Response
json
// 200 OK
{
"message": "Profile updated successfully",
"user": { "id": 1, "email": "...", "username": "johndoe", ... }
}POST
/api/v1/auth/change-passwordChange user passwordRequest
json
{
"current_password": "oldPassword123",
"new_password": "newPassword456"
}Response
json
// 200 OK
{ "message": "Password changed successfully" }Message / Contact Endpoints
POST
/api/v1/contactSubmit a contact messageRequest
json
{
"name": "John Doe",
"email": "user@example.com",
"subject": "Question about API",
"message": "How do I get an API key?"
}Response
json
// 201 Created
{ "message": "Message sent successfully" }GET
/api/v1/messagesList messages (authenticated)Response
json
// 200 OK
{
"messages": [
{
"id": 1,
"subject": "Welcome",
"body": "...",
"sender": { "id": 2, "full_name": "Admin" },
"read": false,
"created_at": "2025-01-01T00:00:00.000000"
}
]
}Article Endpoints
GET
/api/v1/tenants/{tenant_id}/articlesList articles for a tenantResponse
json
// 200 OK
{
"articles": [
{
"id": 1,
"title": "My Article",
"slug": "my-article",
"status": "published",
"category_id": 2,
"created_at": "2025-01-01T00:00:00.000000"
}
],
"total": 1, "page": 1, "per_page": 20
}POST
/api/v1/tenants/{tenant_id}/articlesCreate a new articleRequest
json
{
"title": "My Article",
"content": "Article body in markdown...",
"category_id": 1,
"status": "draft"
}Response
json
// 201 Created
{ "article": { "id": 1, "title": "My Article", ... } }GET
/api/v1/tenants/{tenant_id}/articles/{article_id}Get a single articlePATCH
/api/v1/tenants/{tenant_id}/articles/{article_id}Update an articleRequest
json
{ "title": "Updated Title", "status": "published" }DELETE
/api/v1/tenants/{tenant_id}/articles/{article_id}Delete an articlePOST
/api/v1/tenants/{tenant_id}/articles/generateGenerate article with AIRequest
json
{
"prompt": "Write about the future of AI in content creation",
"category_id": 1,
"tone": "professional",
"length": "medium"
}Response
json
// 201 Created
{
"article": {
"id": 5,
"title": "The Future of AI in Content Creation",
"content": "...",
"status": "draft"
}
}GET
/api/v1/tenants/{tenant_id}/articles/searchSearch articlesRequest
json
// Query params: ?q=keyword&status=published&category_id=1
Category Endpoints
GET
/api/v1/tenants/{tenant_id}/categoriesList all categoriesResponse
json
// 200 OK
{ "categories": [{ "id": 1, "name": "Tech", "slug": "tech", "description": "...", "article_count": 5 }] }POST
/api/v1/tenants/{tenant_id}/categoriesCreate a categoryRequest
json
{ "name": "Technology", "slug": "technology", "description": "Tech articles" }PATCH
/api/v1/tenants/{tenant_id}/categories/{category_id}Update a categoryDELETE
/api/v1/tenants/{tenant_id}/categories/{category_id}Delete a categoryTeam Management
GET
/api/v1/tenants/{tenant_id}/teamList team membersResponse
json
// 200 OK
{ "members": [{ "id": 1, "full_name": "Jane", "role": "Editor", "email": "..." }] }PATCH
/api/v1/tenants/{tenant_id}/team/{user_id}Update member roleRequest
json
{ "role": "Editor" }DELETE
/api/v1/tenants/{tenant_id}/team/{user_id}Remove a team memberTenant Management
GET
/api/v1/tenantsList tenants for current userPOST
/api/v1/tenantsCreate a new tenantRequest
json
{
"name": "My Blog",
"slug": "my-blog",
"domain": "myblog.com",
"description": "A personal tech blog"
}GET
/api/v1/tenants/{tenant_id}Get tenant detailsPATCH
/api/v1/tenants/{tenant_id}Update tenantDELETE
/api/v1/tenants/{tenant_id}Delete tenantInvitation Endpoints
POST
/api/v1/tenants/{tenant_id}/invitationsInvite a user to a tenantRequest
json
{ "email": "newuser@example.com", "role": "Editor" }Response
json
// 201 Created
{ "message": "Invitation sent", "invitation_token": "abc123..." }POST
/api/v1/invitations/acceptAccept an invitationRequest
json
{ "token": "abc123..." }GET
/api/v1/tenants/{tenant_id}/invitationsList pending invitationsArticle Scheduling
POST
/api/v1/tenants/{tenant_id}/articles/{article_id}/scheduleSchedule article for publishingRequest
json
{
"scheduled_at": "2025-06-01T09:00:00Z",
"timezone": "America/New_York"
}Response
json
// 200 OK
{ "message": "Article scheduled", "scheduled_at": "2025-06-01T09:00:00Z" }DELETE
/api/v1/tenants/{tenant_id}/articles/{article_id}/scheduleCancel scheduled publishingGET
/api/v1/tenants/{tenant_id}/scheduledList all scheduled articlesSocial Media Posting
GET
/api/v1/tenants/{tenant_id}/social-accountsList connected social media accountsPOST
/api/v1/tenants/{tenant_id}/articles/{article_id}/publish-socialPost article to social mediaRequest
json
{
"platforms": ["twitter", "linkedin"],
"custom_message": "Check out our latest article!",
"schedule_at": null
}Subscription Endpoints
GET
/api/v1/subscriptions/plansList available subscription plansResponse
json
// 200 OK
{ "plans": [{ "id": "pro", "name": "Pro", "price": 29, "features": [...] }] }GET
/api/v1/subscriptions/currentGet current subscriptionPOST
/api/v1/subscriptionsCreate / upgrade subscriptionRequest
json
{ "plan_id": "pro", "payment_method_id": "pm_xxx" }DELETE
/api/v1/subscriptions/cancelCancel subscriptionAffiliate Link Management
GET
/api/v1/tenants/{tenant_id}/affiliate-linksList affiliate linksPOST
/api/v1/tenants/{tenant_id}/affiliate-linksCreate an affiliate linkRequest
json
{
"name": "Amazon Affiliate",
"original_url": "https://amazon.com/product/123",
"slug": "amz-product"
}GET
/r/{slug}Affiliate redirect — tracks click and redirectsSystem Administration (Global Admin)
All admin endpoints require
is_platform_admin: true on the user account.GET
/api/v1/admin/dashboardPlatform overview — user counts, revenue, active tenantsGET
/api/v1/admin/insightsPlatform insights and growth metricsGET
/api/v1/admin/analytics/contentContent analytics across all tenantsGET
/api/v1/admin/analytics/revenueRevenue analytics and subscription breakdownGET
/api/v1/admin/tenantsList all tenantsPATCH
/api/v1/admin/tenants/{tenant_id}Update tenant settingsGET
/api/v1/admin/usersList all usersPATCH
/api/v1/admin/users/{user_id}/roleUpdate user platform roleRequest
json
{ "is_platform_admin": true }GET
/api/v1/admin/audit-logsGet audit logsResponse
json
// 200 OK
{ "logs": [{ "id": 1, "action": "user.login", "user_id": 1, "ip": "127.0.0.1", "created_at": "..." }] }Health Check
GET
/healthServer health checkResponse
json
// 200 OK
{ "status": "healthy", "version": "1.0.0", "db": "connected" }Error Responses
400
Bad Request
Invalid request body or parameters
401
Unauthorized
Missing or invalid authentication token
403
Forbidden
Insufficient permissions for this action
404
Not Found
Resource does not exist
409
Conflict
Resource already exists (e.g. duplicate email)
422
Unprocessable
Validation failed for request data
429
Rate Limited
Too many requests — slow down
500
Server Error
Internal server error
Standard Error Response Format
json
{
"error": "Unauthorized",
"message": "Invalid or expired token",
"status": 401
}
Comment Endpoints
/api/v1/tenants/{tenant_id}/articles/{article_id}/commentsList comments on an article/api/v1/tenants/{tenant_id}/articles/{article_id}/commentsPost a commentRequest
{ "content": "Great article!" }/api/v1/tenants/{tenant_id}/comments/{comment_id}Delete a comment