REST API Specification
Status: Reference Last Updated: 2025-11-02 Related Docs: API Integration, System Architecture Code Location: Future implementation - Cloudflare Workers
Project: EPGOAT SaaS Platform Component: REST API & EPG Access Endpoints Version: 1.0.0 Original Date: 2025-10-30 Planning Status: Planning Owner: Backend Team Format: OpenAPI 3.0
Table of Contents
- Executive Summary
- API Overview
- Authentication
- Rate Limiting
- Error Handling
- Consumer API
- Reseller API
- Admin API
- EPG Access API
- Webhook Endpoints
- OpenAPI Specification
Executive Summary
The EPGOAT API is a RESTful service built on Cloudflare Workers, providing:
- Consumer API: Subscription management, EPG key access, profile settings
- Reseller API: Bulk key generation, usage analytics, customer management
- Admin API: Provider management, user administration, system monitoring
- EPG Access API: Public endpoint for serving XMLTV files via unique keys
- Webhook API: Stripe payment webhooks, system notifications
Base URLs
| Environment | Base URL |
|---|---|
| Production | https://api.epgoat.tv |
| Staging | https://api-staging.epgoat.tv |
| EPG Access | https://epgo.at |
Key Features
- JWT Authentication: Auth0-issued tokens for all authenticated endpoints
- Key-Based Access: UUID keys for EPG file access (no auth required)
- Rate Limiting: 60 req/min (authenticated), 1 req/min (EPG keys)
- CORS: Enabled for
app.epgoat.tvandadmin.epgoat.tv - Versioning: URL-based (
/v1/...) - Content Type:
application/json(except XMLTV:application/xml)
API Overview
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client Applications β
β (Consumer Portal, Reseller Portal, Admin Dashboard) β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
β HTTPS + JWT
β
βΌ
ββββββββββββββββββββββββββββββ
β Cloudflare Workers β
β (api.epgoat.tv) β
β β
β ββββββββββββββββββββββββ β
β β API Router β β
β β - /v1/auth/* β β
β β - /v1/subscriptions/*β β
β β - /v1/keys/* β β
β β - /v1/admin/* β β
β β - /webhooks/* β β
β ββββββββββββ¬ββββββββββββ β
β β β
β ββββββββββββΌββββββββββββ β
β β Middleware Layer β β
β β - JWT Validation β β
β β - CORS Handling β β
β β - Rate Limiting β β
β β - Error Handling β β
β ββββββββββββ¬ββββββββββββ β
βββββββββββββββΌβββββββββββββββ
β
βββββββββββββΌββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββ βββββββββββ βββββββββββ
βCloudflareβ β Stripe β β Auth0 β
β D1 β β API β β API β
βββββββββββ βββββββββββ βββββββββββ
Endpoint Categories
| Category | Base Path | Auth Required | Rate Limit |
|---|---|---|---|
| Authentication | /v1/auth |
No (registration), Yes (profile) | 10/min |
| Subscriptions | /v1/subscriptions |
Yes | 60/min |
| Keys (Consumer) | /v1/keys |
Yes | 60/min |
| Keys (Reseller) | /v1/reseller/keys |
Yes (reseller role) | 120/min |
| Admin | /v1/admin |
Yes (admin role) | 120/min |
| EPG Access | /<key>/<provider>.xml |
No (key-based) | 1/min per key |
| Webhooks | /webhooks |
No (signature verification) | N/A |
Authentication
JWT Token Structure
Header:
{
"alg": "RS256",
"typ": "JWT",
"kid": "auth0_key_id"
}
Payload:
{
"iss": "https://epgoat.us.auth0.com/",
"sub": "auth0|123456",
"aud": "https://api.epgoat.tv",
"iat": 1698710400,
"exp": 1698796800,
"azp": "client_id",
"scope": "openid profile email",
"https://epgoat.tv/role": "consumer",
"https://epgoat.tv/epgoat_user_id": "uuid",
"https://epgoat.tv/subscription_status": "active"
}
Authorization Header
All authenticated requests must include:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Token Validation (Cloudflare Worker)
import jwt from '@tsndr/cloudflare-worker-jwt';
async function validateJWT(request) {
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return { valid: false, error: 'Missing authorization header' };
}
const token = authHeader.substring(7);
try {
const isValid = await jwt.verify(token, AUTH0_PUBLIC_KEY, {
algorithm: 'RS256',
issuer: 'https://epgoat.us.auth0.com/',
audience: 'https://api.epgoat.tv'
});
if (!isValid) {
return { valid: false, error: 'Invalid token' };
}
const decoded = jwt.decode(token);
return {
valid: true,
user: {
auth0_id: decoded.payload.sub,
epgoat_user_id: decoded.payload['https://epgoat.tv/epgoat_user_id'],
role: decoded.payload['https://epgoat.tv/role'],
subscription_status: decoded.payload['https://epgoat.tv/subscription_status']
}
};
} catch (error) {
return { valid: false, error: error.message };
}
}
Rate Limiting
Rate Limit Headers
All responses include:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1698710460
Rate Limit Implementation
class RateLimiter {
constructor(limit, window) {
this.limit = limit;
this.window = window; // seconds
}
async check(key, kv) {
const now = Math.floor(Date.now() / 1000);
const windowStart = now - this.window;
// Get request count from KV
const data = await kv.get(`ratelimit:${key}`, { type: 'json' }) || { count: 0, reset: now + this.window };
if (data.reset < now) {
// Window expired, reset
data.count = 1;
data.reset = now + this.window;
} else {
data.count += 1;
}
// Store updated count
await kv.put(`ratelimit:${key}`, JSON.stringify(data), {
expirationTtl: this.window
});
return {
allowed: data.count <= this.limit,
limit: this.limit,
remaining: Math.max(0, this.limit - data.count),
reset: data.reset
};
}
}
Rate Limit Tiers
| User Type | Limit | Window |
|---|---|---|
| Anonymous | 10 req | 1 minute |
| Consumer | 60 req | 1 minute |
| Reseller | 120 req | 1 minute |
| Admin | 120 req | 1 minute |
| EPG Key | 1 req | 1 minute |
Error Handling
Standard Error Response
{
"success": false,
"error": {
"code": "SUBSCRIPTION_NOT_FOUND",
"message": "Subscription not found",
"details": {
"user_id": "uuid"
},
"timestamp": "2025-10-30T12:00:00Z",
"request_id": "req_abc123"
}
}
HTTP Status Codes
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, DELETE |
| 201 | Created | Successful POST creating new resource |
| 204 | No Content | Successful DELETE with no response body |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Missing or invalid JWT token |
| 403 | Forbidden | Valid token but insufficient permissions |
| 404 | Not Found | Resource not found |
| 409 | Conflict | Duplicate resource (e.g., email already exists) |
| 422 | Unprocessable Entity | Validation failed |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error |
| 503 | Service Unavailable | Temporary outage |
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED |
401 | Missing or invalid token |
FORBIDDEN |
403 | Insufficient permissions |
NOT_FOUND |
404 | Resource not found |
VALIDATION_ERROR |
422 | Input validation failed |
RATE_LIMIT_EXCEEDED |
429 | Too many requests |
SUBSCRIPTION_NOT_FOUND |
404 | User has no subscription |
SUBSCRIPTION_INACTIVE |
403 | Subscription not active |
PAYMENT_REQUIRED |
402 | Payment method required |
KEY_NOT_FOUND |
404 | EPG key not found |
KEY_EXPIRED |
403 | EPG key has expired |
KEY_SUSPENDED |
403 | EPG key suspended for abuse |
PROVIDER_NOT_FOUND |
404 | Provider not found |
INTERNAL_ERROR |
500 | Internal server error |
Consumer API
Base Path: /v1
Authentication Endpoints
POST /v1/auth/register
Description: Create new user after Auth0 signup (called by Auth0 Action)
Authentication: Auth0 Management API token
Request:
{
"auth0_user_id": "auth0|123456",
"email": "user@example.com",
"name": "John Doe",
"role": "consumer"
}
Response (201):
{
"success": true,
"data": {
"user_id": "uuid",
"access_key": "uuid",
"epg_url": "https://epgo.at/uuid/tps.xml",
"trial_end": "2025-11-29T23:59:59Z"
}
}
GET /v1/auth/me
Description: Get current user profile
Authentication: Required
Response (200):
{
"success": true,
"data": {
"user_id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"role": "consumer",
"subscription_status": "active",
"created_at": "2025-10-30T12:00:00Z"
}
}
PUT /v1/auth/me
Description: Update user profile
Authentication: Required
Request:
{
"name": "John Smith",
"timezone": "America/New_York"
}
Response (200):
{
"success": true,
"data": {
"user_id": "uuid",
"name": "John Smith",
"timezone": "America/New_York",
"updated_at": "2025-10-30T12:00:00Z"
}
}
Subscription Endpoints
GET /v1/subscriptions/current
Description: Get current user's subscription
Authentication: Required
Response (200):
{
"success": true,
"data": {
"subscription_id": "uuid",
"plan_type": "consumer_annual",
"status": "active",
"trial_start": null,
"trial_end": null,
"current_period_start": "2025-10-30",
"current_period_end": "2026-10-30",
"will_auto_charge": false,
"will_cancel_at": null,
"stripe_subscription_id": "sub_123",
"last_payment": {
"amount": 1000,
"currency": "usd",
"paid_at": "2025-10-30T12:00:00Z"
}
}
}
POST /v1/subscriptions/create
Description: Create new subscription (via Stripe Checkout)
Authentication: Required
Request:
{
"plan": "consumer_annual",
"success_url": "https://app.epgoat.tv/subscription/success",
"cancel_url": "https://app.epgoat.tv/subscription"
}
Response (201):
{
"success": true,
"data": {
"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_abc123"
}
}
POST /v1/subscriptions/cancel
Description: Cancel subscription (effective at period end)
Authentication: Required
Response (200):
{
"success": true,
"data": {
"subscription_id": "uuid",
"status": "cancel_at_end",
"will_cancel_at": "2026-10-30",
"message": "Your subscription will remain active until 2026-10-30"
}
}
POST /v1/subscriptions/reactivate
Description: Reactivate canceled subscription
Authentication: Required
Response (200):
{
"success": true,
"data": {
"subscription_id": "uuid",
"status": "active",
"message": "Your subscription has been reactivated"
}
}
POST /v1/subscriptions/upgrade
Description: Upgrade subscription plan
Authentication: Required
Request:
{
"new_plan": "consumer_annual"
}
Response (200):
{
"success": true,
"data": {
"subscription_id": "uuid",
"old_plan": "consumer_monthly",
"new_plan": "consumer_annual",
"proration_credit": 350,
"next_billing_date": "2026-10-30"
}
}
EPG Key Endpoints (Consumer)
GET /v1/keys/my
Description: Get consumer's EPG access key
Authentication: Required
Response (200):
{
"success": true,
"data": {
"key_id": "uuid",
"epg_url": "https://epgo.at/uuid/tps.xml",
"status": "active",
"created_at": "2025-10-30T12:00:00Z",
"expires_at": "2026-10-30T23:59:59Z",
"provider": {
"id": 1,
"name": "TPS",
"logo_url": "https://files.epgoat.tv/logos/tps.png"
}
}
}
GET /v1/keys/my/stats
Description: Get usage statistics for consumer's key
Authentication: Required
Response (200):
{
"success": true,
"data": {
"key_id": "uuid",
"access_count": 1234,
"last_accessed_at": "2025-10-30T11:45:00Z",
"last_ip": "192.168.1.1",
"unique_ips": 2,
"access_history": [
{
"date": "2025-10-30",
"count": 24
},
{
"date": "2025-10-29",
"count": 24
}
]
}
}
POST /v1/keys/my/regenerate
Description: Regenerate EPG key (invalidates old key)
Authentication: Required
Response (200):
{
"success": true,
"data": {
"old_key_id": "old-uuid",
"new_key_id": "new-uuid",
"epg_url": "https://epgo.at/new-uuid/tps.xml",
"warning": "Your old EPG URL will stop working immediately"
}
}
Invoice Endpoints
GET /v1/invoices
Description: List user's invoices
Authentication: Required
Query Parameters:
- limit (number, default: 10, max: 100)
- offset (number, default: 0)
Response (200):
{
"success": true,
"data": {
"invoices": [
{
"invoice_id": "in_123",
"amount_due": 1000,
"amount_paid": 1000,
"currency": "usd",
"status": "paid",
"period_start": "2025-10-30",
"period_end": "2026-10-30",
"invoice_url": "https://invoice.stripe.com/i/...",
"invoice_pdf": "https://pay.stripe.com/invoice/.../pdf",
"created_at": "2025-10-30T12:00:00Z"
}
],
"total_count": 1,
"has_more": false
}
}
Reseller API
Base Path: /v1/reseller
Key Management
GET /v1/reseller/keys
Description: List all generated keys
Authentication: Required (reseller role)
Query Parameters:
- provider_id (number, optional)
- status (string, optional: "active", "inactive", "expired")
- search (string, optional: search by customer email/name)
- limit (number, default: 50, max: 200)
- offset (number, default: 0)
Response (200):
{
"success": true,
"data": {
"keys": [
{
"key_id": "uuid",
"customer_name": "Customer Name",
"customer_email": "customer@example.com",
"provider": {
"id": 1,
"name": "TPS"
},
"status": "active",
"created_at": "2025-10-30T12:00:00Z",
"expires_at": "2026-10-30T23:59:59Z",
"last_accessed_at": "2025-10-30T11:45:00Z",
"access_count": 456,
"notes": "VIP customer"
}
],
"total_count": 25,
"keys_remaining": 25
}
}
POST /v1/reseller/keys
Description: Generate new EPG key
Authentication: Required (reseller role)
Request:
{
"provider_id": 1,
"customer_name": "Customer Name",
"customer_email": "customer@example.com",
"expires_at": "2026-10-30",
"notes": "VIP customer"
}
Response (201):
{
"success": true,
"data": {
"key_id": "uuid",
"epg_url": "https://epgo.at/uuid/tps.xml",
"customer_name": "Customer Name",
"customer_email": "customer@example.com",
"status": "active",
"created_at": "2025-10-30T12:00:00Z",
"expires_at": "2026-10-30T23:59:59Z"
}
}
GET /v1/reseller/keys/:id
Description: Get key details
Authentication: Required (reseller role)
Response (200):
{
"success": true,
"data": {
"key_id": "uuid",
"epg_url": "https://epgo.at/uuid/tps.xml",
"customer_name": "Customer Name",
"customer_email": "customer@example.com",
"provider": {
"id": 1,
"name": "TPS"
},
"status": "active",
"created_at": "2025-10-30T12:00:00Z",
"expires_at": "2026-10-30T23:59:59Z",
"last_accessed_at": "2025-10-30T11:45:00Z",
"access_count": 456,
"unique_ips": 3,
"abuse_flagged": false,
"notes": "VIP customer"
}
}
PUT /v1/reseller/keys/:id
Description: Update key (deactivate, add notes, etc.)
Authentication: Required (reseller role)
Request:
{
"status": "inactive",
"notes": "Customer canceled"
}
Response (200):
{
"success": true,
"data": {
"key_id": "uuid",
"status": "inactive",
"notes": "Customer canceled",
"updated_at": "2025-10-30T12:00:00Z"
}
}
GET /v1/reseller/keys/:id/logs
Description: Get access logs for key
Authentication: Required (reseller role)
Query Parameters:
- limit (number, default: 100, max: 500)
- offset (number, default: 0)
Response (200):
{
"success": true,
"data": {
"logs": [
{
"accessed_at": "2025-10-30T11:45:00Z",
"ip_address": "192.168.1.1",
"user_agent": "VLC/3.0.16",
"provider": "TPS",
"file_requested": "tps.xml",
"status": 200,
"bytes_transferred": 123456
}
],
"total_count": 456
}
}
POST /v1/reseller/keys/export
Description: Export all keys to CSV
Authentication: Required (reseller role)
Request:
{
"provider_id": 1,
"status": "active"
}
Response (200):
key_id,customer_name,customer_email,provider,status,created_at,expires_at,access_count
uuid-1,Customer 1,customer1@example.com,TPS,active,2025-10-30,2026-10-30,456
uuid-2,Customer 2,customer2@example.com,TPS,active,2025-10-29,2026-10-29,789
Analytics
GET /v1/reseller/analytics/usage
Description: Get usage analytics
Authentication: Required (reseller role)
Query Parameters:
- provider_id (number, optional)
- start_date (string, YYYY-MM-DD)
- end_date (string, YYYY-MM-DD)
Response (200):
{
"success": true,
"data": {
"total_keys": 50,
"active_keys": 45,
"total_accesses": 45678,
"usage_by_day": [
{
"date": "2025-10-30",
"accesses": 1234
}
],
"top_keys": [
{
"key_id": "uuid",
"customer_name": "Customer Name",
"accesses": 456
}
]
}
}
Admin API
Base Path: /v1/admin
Provider Management
GET /v1/admin/providers
Description: List all providers
Authentication: Required (admin role)
Response (200):
{
"success": true,
"data": {
"providers": [
{
"id": 1,
"name": "TPS",
"description": "Premium sports provider",
"type": "sports",
"status": "active",
"logo_url": "https://files.epgoat.tv/logos/tps.png",
"channel_count": 1100,
"match_rate": 96.5,
"last_updated_at": "2025-10-30T06:00:00Z",
"created_at": "2025-10-01T12:00:00Z"
}
]
}
}
POST /v1/admin/providers
Description: Create new provider
Authentication: Required (admin role)
Request:
{
"name": "New Provider",
"description": "Description",
"type": "sports",
"status": "active",
"m3u_url": "https://provider.com/playlist.m3u",
"xmltv_filename": "newprovider.xml",
"config": {
"timezone": "America/New_York",
"update_frequency": "4x",
"family_mappings_file": "universal.yml"
}
}
Response (201):
{
"success": true,
"data": {
"id": 11,
"name": "New Provider",
"status": "active",
"created_at": "2025-10-30T12:00:00Z"
}
}
User Management
GET /v1/admin/users
Description: List all users
Authentication: Required (admin role)
Query Parameters:
- role (string, optional: "consumer", "reseller", "admin")
- status (string, optional: "trial", "active", "canceled", "expired")
- search (string, optional)
- limit (number, default: 50, max: 200)
- offset (number, default: 0)
Response (200):
{
"success": true,
"data": {
"users": [
{
"user_id": "uuid",
"email": "user@example.com",
"name": "User Name",
"role": "consumer",
"subscription_status": "active",
"keys_count": 1,
"created_at": "2025-10-01T12:00:00Z"
}
],
"total_count": 620
}
}
Key Management (Admin)
GET /v1/admin/keys
Description: List all keys (across all users)
Authentication: Required (admin role)
Query Parameters:
- user_id (string, optional)
- provider_id (number, optional)
- status (string, optional)
- abuse_flagged (boolean, optional)
- limit (number, default: 100, max: 500)
- offset (number, default: 0)
Response (200):
{
"success": true,
"data": {
"keys": [
{
"key_id": "uuid",
"user_email": "user@example.com",
"user_role": "consumer",
"provider": "TPS",
"status": "active",
"access_count": 1234,
"abuse_flagged": false,
"created_at": "2025-10-01T12:00:00Z"
}
],
"total_count": 620
}
}
PUT /v1/admin/keys/:id/suspend
Description: Suspend key for abuse
Authentication: Required (admin role)
Request:
{
"reason": "Detected sharing across >3 IPs"
}
Response (200):
{
"success": true,
"data": {
"key_id": "uuid",
"status": "suspended",
"reason": "Detected sharing across >3 IPs",
"suspended_at": "2025-10-30T12:00:00Z"
}
}
System Metrics
GET /v1/admin/metrics
Description: Get system metrics
Authentication: Required (admin role)
Response (200):
{
"success": true,
"data": {
"total_users": 620,
"active_subscriptions": 465,
"total_keys": 670,
"match_rate": 96.5,
"llm_cost_mtd": 8.45,
"user_growth": [
{ "date": "2025-10-30", "count": 620 }
],
"revenue": [
{ "date": "2025-10", "mrr": 582 }
]
}
}
EPG Access API
Base Path: https://epgo.at
GET /:key/:provider.xml
Description: Serve EPG XMLTV file for valid key
Authentication: None (key-based access)
Rate Limit: 1 request/minute per key
Parameters:
- key (path): UUID access key
- provider (path): Provider name (e.g., "tps", "sportztv")
Example:
GET https://epgo.at/550e8400-e29b-41d4-a716-446655440000/tps.xml
Response (200):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv generator-info-name="EPGOAT" generator-info-url="https://epgoat.tv">
<channel id="nba1">
<display-name>NBA 01</display-name>
<icon src="https://files.epgoat.tv/logos/nba.png" />
</channel>
<programme start="20251030190000 -0400" stop="20251030220000 -0400" channel="nba1">
<title lang="en">Los Angeles Lakers vs Boston Celtics</title>
<sub-title lang="en">NBA Regular Season</sub-title>
<desc lang="en">Lakers take on Celtics in Eastern Conference showdown</desc>
<category lang="en">Sports</category>
<category lang="en">Basketball</category>
<icon src="https://files.epgoat.tv/events/12345.jpg" />
</programme>
</tv>
Response Headers:
Content-Type: application/xml
Cache-Control: max-age=3600
X-EPG-Generated: 2025-10-30T06:00:00Z
X-EPG-Version: 1.0
X-Key-Status: active
X-RateLimit-Limit: 1
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1698710520
Error Responses:
404 - Key Not Found:
<?xml version="1.0" encoding="UTF-8"?>
<error>
<code>KEY_NOT_FOUND</code>
<message>EPG access key not found</message>
</error>
403 - Key Expired:
<?xml version="1.0" encoding="UTF-8"?>
<error>
<code>KEY_EXPIRED</code>
<message>Your EPG access has expired. Please renew your subscription at https://app.epgoat.tv</message>
<expired_at>2025-10-23T23:59:59Z</expired_at>
</error>
403 - Key Suspended:
<?xml version="1.0" encoding="UTF-8"?>
<error>
<code>KEY_SUSPENDED</code>
<message>This EPG key has been suspended due to abuse detection</message>
<contact>support@epgoat.tv</contact>
</error>
429 - Rate Limit Exceeded:
<?xml version="1.0" encoding="UTF-8"?>
<error>
<code>RATE_LIMIT_EXCEEDED</code>
<message>Rate limit exceeded. Please try again in 60 seconds.</message>
<retry_after>60</retry_after>
</error>
Webhook Endpoints
POST /webhooks/stripe
Description: Handle Stripe webhook events
Authentication: Stripe signature verification
Headers:
Stripe-Signature: t=1698710400,v1=abc123...
Request Body: (varies by event type)
Event Types Handled:
- invoice.paid
- invoice.payment_failed
- customer.subscription.created
- customer.subscription.updated
- customer.subscription.deleted
- checkout.session.completed
- charge.refunded
Response (200):
{
"received": true
}
OpenAPI Specification
File: openapi.yaml
openapi: 3.0.3
info:
title: EPGOAT API
description: REST API for EPGOAT SaaS Platform
version: 1.0.0
contact:
name: EPGOAT Support
email: support@epgoat.tv
url: https://epgoat.tv
servers:
- url: https://api.epgoat.tv/v1
description: Production
- url: https://api-staging.epgoat.tv/v1
description: Staging
security:
- BearerAuth: []
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: Auth0 JWT token
schemas:
Error:
type: object
required:
- success
- error
properties:
success:
type: boolean
example: false
error:
type: object
required:
- code
- message
properties:
code:
type: string
example: SUBSCRIPTION_NOT_FOUND
message:
type: string
example: Subscription not found
details:
type: object
timestamp:
type: string
format: date-time
request_id:
type: string
User:
type: object
properties:
user_id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
role:
type: string
enum: [consumer, reseller, admin]
subscription_status:
type: string
enum: [trial, trial_with_cc, active, past_due, canceled, expired]
created_at:
type: string
format: date-time
Subscription:
type: object
properties:
subscription_id:
type: string
format: uuid
plan_type:
type: string
enum: [consumer_trial, consumer_monthly, consumer_annual, reseller_annual]
status:
type: string
enum: [trial, trial_with_cc, active, past_due, cancel_at_end, canceled, expired]
current_period_start:
type: string
format: date
current_period_end:
type: string
format: date
will_cancel_at:
type: string
format: date
nullable: true
stripe_subscription_id:
type: string
nullable: true
AccessKey:
type: object
properties:
key_id:
type: string
format: uuid
epg_url:
type: string
format: uri
status:
type: string
enum: [active, inactive, expired, suspended]
provider:
$ref: '#/components/schemas/Provider'
created_at:
type: string
format: date-time
expires_at:
type: string
format: date-time
Provider:
type: object
properties:
id:
type: integer
name:
type: string
logo_url:
type: string
format: uri
paths:
/auth/me:
get:
summary: Get current user profile
tags: [Authentication]
security:
- BearerAuth: []
responses:
'200':
description: User profile
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
$ref: '#/components/schemas/User'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/subscriptions/current:
get:
summary: Get current subscription
tags: [Subscriptions]
security:
- BearerAuth: []
responses:
'200':
description: Current subscription
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
$ref: '#/components/schemas/Subscription'
'404':
description: No subscription found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/subscriptions/create:
post:
summary: Create subscription (Stripe Checkout)
tags: [Subscriptions]
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- plan
- success_url
- cancel_url
properties:
plan:
type: string
enum: [consumer_monthly, consumer_annual, reseller_annual]
success_url:
type: string
format: uri
cancel_url:
type: string
format: uri
responses:
'201':
description: Checkout session created
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
type: object
properties:
checkout_url:
type: string
format: uri
/keys/my:
get:
summary: Get my EPG key
tags: [Keys]
security:
- BearerAuth: []
responses:
'200':
description: EPG access key
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
$ref: '#/components/schemas/AccessKey'
/reseller/keys:
get:
summary: List all generated keys
tags: [Reseller]
security:
- BearerAuth: []
parameters:
- name: provider_id
in: query
schema:
type: integer
- name: status
in: query
schema:
type: string
enum: [active, inactive, expired]
- name: limit
in: query
schema:
type: integer
default: 50
maximum: 200
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: List of keys
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
type: object
properties:
keys:
type: array
items:
$ref: '#/components/schemas/AccessKey'
total_count:
type: integer
keys_remaining:
type: integer
post:
summary: Generate new EPG key
tags: [Reseller]
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- provider_id
properties:
provider_id:
type: integer
customer_name:
type: string
customer_email:
type: string
format: email
expires_at:
type: string
format: date
notes:
type: string
responses:
'201':
description: Key created
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
$ref: '#/components/schemas/AccessKey'
/admin/users:
get:
summary: List all users
tags: [Admin]
security:
- BearerAuth: []
parameters:
- name: role
in: query
schema:
type: string
enum: [consumer, reseller, admin]
- name: limit
in: query
schema:
type: integer
default: 50
responses:
'200':
description: List of users
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
type: object
properties:
users:
type: array
items:
$ref: '#/components/schemas/User'
total_count:
type: integer
Success Criteria
- [x] OpenAPI 3.0 specification complete
- [x] All consumer endpoints documented
- [x] All reseller endpoints documented
- [x] All admin endpoints documented
- [x] EPG access endpoint documented
- [x] Webhook endpoints documented
- [x] Authentication flow documented
- [x] Rate limiting specified
- [x] Error codes standardized
- [x] Request/response examples provided
END OF SPECIFICATION