# ALL OVER APPS - ADMIN API

**Version:** 1.0.0  
**Base URL:** https://api.alloverapps.com

Centralized administration API for all ALL OVER APPS applications. Manages multiple Shopify apps from a unified admin panel.

## Table of Contents

- [Authentication](#authentication)
- [Auth](#auth)
- [Users](#users)
- [Apps](#apps)
- [API Keys](#api-keys)
- [Shops](#shops)
- [Templates](#templates)
- [Metrics](#metrics)
- [Logs](#logs)
- [Feedback](#feedback)
- [Tickets](#tickets)
- [Magic Link](#magic-link)
- [Wants](#wants)
- [Newsletter](#newsletter)
- [Contacts](#contacts)
- [Webhooks](#webhooks)
- [Email Tracking](#email-tracking)
- [Email Events](#email-events)
- [Email Analytics](#email-analytics)
- [Email Jobs](#email-jobs)
- [Health](#health)
- [Plans](#plans)

---

## Authentication

### JWT Bearer Token

```
Authorization: Bearer <token>
```

### API Key (`apiKeyAuth`)

Used for backend-to-backend authentication. Key in header.

```
X-API-Key: <key>
```

---

## Auth

Back Office authentication

### `POST` /auth/login

**Back Office team login**

Authentication for internal team users using email and password

**Auth:** None required

**Request Body:**

```json
{
  "email": string — e.g. "admin@bannersallover.com" (required)
  "password": string — e.g. "password123" (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "token": string
  "user": {
    "id": string
    "email": string
    "name": string
    "role": string — e.g. "admin"
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /auth/refresh

**Refresh JWT token**

Generate a new JWT token from an existing one

**Auth:** Bearer Token (JWT)

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "token": string
  "user": {}
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /auth/hash-password

**[DEV] Generate password hash**

Development endpoint to generate bcrypt hashes for passwords

**Auth:** None required

**Request Body:**

```json
{
  "password": string — e.g. "mypassword123" (required)
}
```

**Response 200:**

```json
{
  "hash": string
}
```

### `GET` /auth/me

**Current user information**

Get authenticated user information

**Auth:** Bearer Token (JWT)

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "user": {
    "id": string
    "email": string
    "name": string
    "role": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Users

Admin user management

### `GET` /users/

**List users**

Get list of admin users

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `role` | string | No | Filter by role |
| `isActive` | string | No | Filter by active status |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "email": string
    "firstName": string
    "lastName": string
    "role": string
    "isActive": boolean
    "profile": {
      "avatar": string
      "bio": string
      "phone": string
      "timezone": string
      "language": string
    }
    "settings": {
      "notifications": {
        "email": boolean
        "push": boolean
      }
      "theme": string
      "defaultApp": string
    }
    "lastLoginAt": string
    "loginCount": number
    "createdAt": string
    "updatedAt": string
  }]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /users/

**Create user**

Create new admin user

**Auth:** Bearer Token (JWT)

**Request Body:**

```json
{
  "email": string (required)
  "password": string (required)
  "firstName": string (required)
  "lastName": string (required)
  "role": string
  "profile": {
    "avatar": string
    "bio": string
    "phone": string
    "timezone": string
    "language": string
  }
  "settings": {
    "notifications": {
      "email": boolean
      "push": boolean
    }
    "theme": string
    "defaultApp": string
  }
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 409:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /users/{id}

**Get user by ID**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | User ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /users/{id}

**Update user**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | - |

**Request Body:**

```json
{
  "firstName": string
  "lastName": string
  "role": string
  "isActive": boolean
  "profile": {
    "avatar": string
    "bio": string
    "phone": string
    "timezone": string
    "language": string
  }
  "settings": {
    "notifications": {
      "email": boolean
      "push": boolean
    }
    "theme": string
    "defaultApp": string
  }
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /users/{id}

**Delete user**

Soft delete user (sets isActive to false)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /users/{id}/password

**Update user password**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | - |

**Request Body:**

```json
{
  "password": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Apps

Application management

### `GET` /apps

**List of available applications**

Get the list of all Shopify applications configured in the Back Office

**Auth:** Bearer Token (JWT)

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "id": string — e.g. "banners-all-over"
    "name": string — e.g. "Banners All Over"
    "features": {
      "canEditTemplates": boolean
      "canEditUsers": boolean
      "canViewMetrics": boolean
    }
  }]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## API Keys

API key management for backend-to-backend authentication

### `GET` /api-keys/stats

**API key statistics**

Get API key statistics (total, active, inactive, by app)

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `appId` | string | No | Filter by app ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "active": integer
    "inactive": integer
    "expired": integer
    "byApp": {}
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /api-keys/

**List API keys**

Get list of API keys with pagination and filters

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `appId` | string | No | Filter by app ID |
| `isActive` | string | No | Filter by active status |
| `search` | string | No | Search in name and description |
| `sortBy` | string | No | Sort by field |
| `sortOrder` | string | No | Sort order |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "appId": string
    "name": string
    "description": string
    "allowedOrigins": string[]
    "isActive": boolean
    "lastUsed": string
    "expiresAt": string
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /api-keys/

**Create API key**

Create new API key for an app. The full key is returned only once.

**Auth:** Bearer Token (JWT)

**Request Body:**

```json
{
  "appId": string (required)
  "name": string (required)
  "description": string
  "allowedOrigins": string[]
  "expiresAt": string
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "key": string
    "appId": string
    "name": string
    "description": string
    "allowedOrigins": string[]
    "isActive": boolean
    "expiresAt": string
    "createdBy": string
    "createdAt": string
  }
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /api-keys/{keyId}

**Get API key**

Get API key details by ID (includes the full key value)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | API key ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "key": string
    "appId": string
    "name": string
    "description": string
    "allowedOrigins": string[]
    "isActive": boolean
    "lastUsed": string
    "expiresAt": string
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /api-keys/{keyId}

**Update API key**

Update API key details (name, description, expiration)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | API key ID |

**Request Body:**

```json
{
  "name": string
  "description": string
  "allowedOrigins": string[]
  "isActive": boolean
  "expiresAt": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /api-keys/{keyId}

**Delete API key**

Permanently delete an API key

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | API key ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /api-keys/{keyId}/revoke

**Revoke API key**

Revoke (deactivate) an API key

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | API key ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /api-keys/{keyId}/regenerate

**Regenerate API key**

Generate a new key value for an existing API key. The new key is returned only once.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `keyId` | string | API key ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "key": string
    "appId": string
    "name": string
    "isActive": boolean
    "createdAt": string
  }
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Shops

Shopify shop management per app

### `GET` /{appId}/shops/stats

**Shop statistics**

Get aggregated shop statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "active": integer
    "inactive": integer
    "byPlan": {}
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/shops/domains

**List of shop domains**

Get all unique shop myshopify domains

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": string[]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/shops/

**List shops**

Get paginated list of shops with filters

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `isActive` | boolean | No | Filter by active status |
| `search` | string | No | Search by email, name, or domain |
| `sortBy` | string | No | - |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": object[]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/shops/{shopId}

**Get shop by ID**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `shopId` | string | Shop ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /{appId}/shops/{shopId}

**Update shop**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `shopId` | string | - |

**Request Body:**

```json
{
  "email": string
  "name": string
  "isActive": boolean
  "settings": {}
  "plan": {
    "type": string
    "activatedAt": string
    "trialEndsAt": string
  }
  "needsDataRefresh": boolean
  "needsAuthUpdate": boolean
  "timezone": string
  "storeLocale": string
  "auth": {
    "corsEnabled": boolean
    "isApiActive": boolean
    "allowedOrigins": string[]
  }
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /{appId}/shops/{shopId}/status

**Change shop status**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `shopId` | string | - |

**Request Body:**

```json
{
  "isActive": boolean (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Templates

Email template management

### `GET` /{appId}/templates/categories

**Template languages**

Get all available template languages with counts

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `shopId` | string | No | Filter by shop ID |

### `GET` /{appId}/templates/

**List templates**

Get paginated list of email templates

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `shopId` | string | No | Filter by shop ID |
| `language` | string | No | Filter by language |
| `isActive` | boolean | No | Filter by active status |
| `search` | string | No | Search by name or subject |

### `POST` /{appId}/templates/

**Create template**

Create a new email template

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "name": string (required)
  "language": string (required)
  "subject": string (required)
  "htmlTemplate": string (required)
  "textTemplate": string (required)
  "variables": string[]
  "isActive": boolean
  "version": number
  "shopId": string
}
```

### `GET` /{appId}/templates/{templateId}

**Get template by ID**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `templateId` | string | - |

### `PUT` /{appId}/templates/{templateId}

**Update template (full)**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `templateId` | string | - |

### `PATCH` /{appId}/templates/{templateId}

**Update template (partial)**

Partial update - only updates provided fields

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `templateId` | string | - |

### `DELETE` /{appId}/templates/{templateId}

**Delete template**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `templateId` | string | - |

### `PATCH` /{appId}/templates/{templateId}/toggle

**Toggle template active status**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `templateId` | string | - |

**Request Body:**

```json
{
  "isActive": boolean (required)
}
```

### `POST` /{appId}/templates/{templateId}/duplicate

**Duplicate template**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `templateId` | string | - |

**Request Body:**

```json
{
  "name": string
}
```

---

## Metrics

Metrics and analytics

### `GET` /metrics/aggregate/dashboard

**Aggregated dashboard from all apps**

Get combined metrics from all configured applications

**Auth:** Bearer Token (JWT)

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "aggregate": {
      "shops": {
        "total": integer — e.g. 300
        "active": integer — e.g. 250
        "inactive": integer — e.g. 50
        "newToday": integer — e.g. 5
        "newThisWeek": integer — e.g. 42
        "newThisMonth": integer — e.g. 180
        "byPlan": ...
      }
    }
    "byApp": object[]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /metrics/aggregate/shops-over-time

**Shops over time (all apps)**

Get statistics of registered shops per day, aggregated from all applications

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `days` | integer | No | Number of days to query |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "aggregate": [{
      "date": string — e.g. "2024-01-15"
      "total": integer — e.g. 25
      "byApp": {}
    }]
    "byApp": object[]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /metrics/aggregate/top-shops

**Top shops (all apps)**

Get most recently active shops, aggregating data from all applications. Shops appearing in multiple apps are merged.

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Number of shops to return |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "aggregate": [{
      "shop": string — e.g. "my-store.myshopify.com"
      "name": string — e.g. "My Store"
      "domain": string — e.g. "mystore.com"
      "isActive": boolean — e.g. true
      "lastLogin": string
      "apps": [{
        "appId": string
        "appName": string
        "plan": string
        "isActive": boolean
      }]
    }]
    "byApp": object[]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /metrics/aggregate/activity

**Recent activity (all apps)**

Get recent activity from all applications, combined and sorted by timestamp

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Number of activities to return |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "type": string — e.g. "new_shop"
    "appId": string — e.g. "banners-all-over"
    "appName": string — e.g. "Banners All Over"
    "shop": {
      "name": string
      "email": string
      "domain": string
      "isActive": boolean
    }
    "timestamp": string
  }]
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /metrics/aggregate/summary

**Quick summary (all apps)**

Get a concise summary of key metrics for all applications

**Auth:** Bearer Token (JWT)

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "totals": {
      "totalShops": integer — e.g. 300
      "activeShops": integer — e.g. 250
      "inactiveShops": integer — e.g. 50
      "newToday": integer — e.g. 5
      "newThisWeek": integer — e.g. 42
      "newThisMonth": integer — e.g. 180
    }
    "byApp": [{
      "appId": string
      "appName": string
      "totalShops": integer
      "activeShops": integer
      "inactiveShops": integer
      "newToday": integer
      "newThisWeek": integer
      "newThisMonth": integer
    }]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/metrics/dashboard

**General dashboard**

Get aggregated metrics for main dashboard

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

### `GET` /{appId}/metrics/shops-over-time

**Shops over time**

Get statistics of registered shops per day

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `days` | integer | No | Number of days to query |

### `GET` /{appId}/metrics/top-shops

**Top shops**

Get most recently active shops

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Number of shops to return |

### `GET` /{appId}/metrics/activity

**Recent activity**

Get recent activity in the application

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | - |

### `GET` /{appId}/metrics/custom

**Custom metrics**

Get stored custom metrics with filters

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | No | Metric type |
| `shop` | string | No | - |
| `startDate` | string | No | - |
| `endDate` | string | No | - |
| `limit` | integer | No | - |

---

## Logs

Application logs and debugging

### `GET` /{appId}/logs/stats

**Log statistics**

Get aggregated log statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `shopDomain` | string | No | Filter by shop domain |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "byLevel": {
      "debug": integer
      "info": integer
      "warn": integer
      "error": integer
    }
    "topModules": [{
      "module": string
      "count": integer
    }]
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/logs/shop-domains

**List of shop domains**

Get all unique shop domains from logs

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": string[]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/logs/modules

**List of modules**

Get all unique module names from logs

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": string[]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/logs/over-time

**Logs over time**

Get log statistics over time for charts

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `days` | integer | No | Number of days to query |
| `level` | string | No | Filter by log level |
| `module` | string | No | Filter by module |
| `shopDomain` | string | No | Filter by shop domain |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "date": string — e.g. "2024-01-15"
    "debug": integer
    "info": integer
    "warn": integer
    "error": integer
    "total": integer
  }]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/logs/

**List logs**

Get paginated list of logs with filters

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `level` | string | No | Filter by log level |
| `module` | string | No | Filter by module |
| `shopDomain` | string | No | Filter by shop domain |
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `search` | string | No | Search in message |
| `sortBy` | string | No | - |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "shopDomain": string
    "module": string
    "level": string
    "message": string
    "data": {}
    "error": {
      "name": string
      "message": string
      "stack": string
      "type": string
      "isErrorInstance": boolean
    }
    "createdAtTimestamp": string
    "createdAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /{appId}/logs/

**Delete logs**

Delete logs based on filters (cleanup operation)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "olderThanDays": integer
  "level": string
  "module": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "deletedCount": integer
  }
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/logs/{logId}

**Get log by ID**

Get detailed information about a specific log entry

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `logId` | string | Log ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /{appId}/logs/{logId}

**Delete log by ID**

Delete a specific log entry by its ID

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `logId` | string | Log ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Feedback

User feedback management

### `GET` /feedback/

**List all feedback**

Get paginated list of feedback from all apps (optionally filter by appId)

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `appId` | string | No | Optional: Filter by app ID |
| `status` | string | No | Filter by status |
| `type` | string | No | Filter by type |
| `priority` | string | No | Filter by priority |
| `shopId` | string | No | Filter by shop ID |
| `rating` | string | No | Filter by rating |
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `search` | string | No | Search in subject and message |
| `sortBy` | string | No | - |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": object[]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /feedback/{feedbackId}

**Get feedback by ID**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `feedbackId` | string | Feedback ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /feedback/{feedbackId}

**Update feedback**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `feedbackId` | string | - |

**Request Body:**

```json
{
  "status": string
  "priority": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /feedback/{feedbackId}

**Delete feedback**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `feedbackId` | string | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /feedback/{feedbackId}/status

**Update feedback status**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `feedbackId` | string | - |

**Request Body:**

```json
{
  "status": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/feedback/stats

**Feedback statistics**

Get aggregated feedback statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `shopId` | string | No | Filter by shop ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "byStatus": {}
    "byType": {}
    "byPriority": {}
    "byRating": {}
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /{appId}/feedback/

**Create feedback**

Create new feedback entry

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "shopId": string (required)
  "type": string (required)
  "subject": string (required)
  "message": string (required)
  "rating": string
  "ratingReason": string
  "canContact": boolean
  "priority": string
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/feedback/

**List feedback**

Get paginated list of feedback for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `status` | string | No | Filter by status |
| `type` | string | No | Filter by type |
| `priority` | string | No | Filter by priority |
| `shopId` | string | No | Filter by shop ID |
| `rating` | string | No | Filter by rating |
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `search` | string | No | Search in subject and message |
| `sortBy` | string | No | - |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": object[]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Tickets

Support tickets management

### `GET` /tickets/

**List all tickets**

Get paginated list of tickets from all apps (optionally filter by appId)

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `appId` | string | No | Optional: Filter by app ID |
| `status` | string | No | Filter by status |
| `topic` | string | No | Filter by topic |
| `priority` | string | No | Filter by priority |
| `shopId` | string | No | Filter by shop ID |
| `email` | string | No | Filter by email |
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `search` | string | No | Search in subject, message, and email |
| `sortBy` | string | No | - |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": object[]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /tickets/{messageId}

**Get ticket by ID**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `messageId` | string | Ticket ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /tickets/{messageId}

**Update ticket**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `messageId` | string | - |

**Request Body:**

```json
{
  "status": string
  "priority": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /tickets/{messageId}

**Delete ticket**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `messageId` | string | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /tickets/{messageId}/status

**Update ticket status**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `messageId` | string | - |

**Request Body:**

```json
{
  "status": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/tickets/stats

**Ticket statistics**

Get aggregated ticket statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `shopId` | string | No | Filter by shop ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "byStatus": {}
    "byTopic": {}
    "byPriority": {}
    "recentActivity": [{
      "_id": string
      "subject": string
      "email": string
      "status": string
      "priority": string
      "createdAt": string
    }]
    "volumeOverTime": [{
      "date": string
      "count": integer
    }]
    "averageResolutionTime": number
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /{appId}/tickets/

**Create ticket with attachments**

Create new support ticket with optional file attachments.

**Content Types:**
- `application/json`: For tickets without attachments
- `multipart/form-data`: For tickets with file attachments

**File Constraints:**
- Max file size: 50MB per file
- Max files: 5 per ticket
- Allowed types: images (jpeg, png, gif, webp, svg), videos (mp4, webm, quicktime), PDF

**Multipart Fields:**
- `shopId`: string (required)
- `topic`: enum (required)
- `email`: string (required)
- `subject`: string (required)
- `message`: string (required)
- `priority`: enum (optional, default: normal)
- `attachments`: file(s) (optional, can send multiple)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "shopId": string
    "topic": string
    "email": string
    "subject": string
    "message": string
    "status": string
    "priority": string
    "attachments": [{
      "url": string
      "key": string
      "filename": string
      "mimetype": string
      "size": number
      "uploadedAt": string
    }]
    "createdAt": string
    "updatedAt": string
  }
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/tickets/

**List tickets**

Get paginated list of tickets for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `status` | string | No | Filter by status |
| `topic` | string | No | Filter by topic |
| `priority` | string | No | Filter by priority |
| `shopId` | string | No | Filter by shop ID |
| `email` | string | No | Filter by email |
| `startDate` | string | No | Start date filter |
| `endDate` | string | No | End date filter |
| `search` | string | No | Search in subject, message, and email |
| `sortBy` | string | No | - |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": object[]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /{appId}/tickets/

**Bulk delete tickets**

Delete multiple tickets by IDs

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "ids": string[] (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
  "deletedCount": integer
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/tickets/{messageId}

**Get ticket by ID**

Get a single ticket by its ID

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `messageId` | string | Ticket ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /{appId}/tickets/{messageId}

**Update ticket**

Update ticket fields (status, priority, etc.)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `messageId` | string | Ticket ID |

**Request Body:**

```json
{
  "status": string
  "priority": string
  "topic": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /{appId}/tickets/{messageId}

**Delete ticket**

Delete a ticket by ID

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `messageId` | string | Ticket ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /{appId}/tickets/{messageId}/status

**Update ticket status**

Update only the status of a ticket

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `messageId` | string | Ticket ID |

**Request Body:**

```json
{
  "status": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /{appId}/tickets/{messageId}/messages

**Add message to ticket**

Add a reply/message to an existing ticket

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `messageId` | string | Ticket ID |

**Request Body:**

```json
{
  "content": string (required)
  "isCustomer": boolean
  "authorEmail": string
  "authorName": string
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/tickets/{messageId}/messages

**Get ticket messages**

Get all messages/replies for a ticket

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `messageId` | string | Ticket ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "content": string
    "isCustomer": boolean
    "authorEmail": string
    "authorName": string
    "createdAt": string
  }]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Magic Link

Passwordless authentication for end users

### `POST` /magic-link/

**Send magic link**

Send a passwordless authentication link to the given email

**Auth:** None required

**Request Body:**

```json
{
  "email": string (required)
  "appId": string — e.g. "banners-all-over" (required)
}
```

**Response 200:**

```json
{
  "success": boolean
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 429:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /magic-link/verify

**Verify magic link token**

Verify a magic link token and receive an end-user JWT

**Auth:** None required

**Request Body:**

```json
{
  "token": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean
  "token": string
  "user": {
    "id": string
    "email": string
    "appId": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Wants

Feature requests with voting system

### `GET` /{appId}/wants/

**List wants**

Get paginated list of wants for an app. If authenticated, includes hasVoted status.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `status` | string | No | Filter by status |
| `category` | string | No | Filter by category |
| `sort` | string | No | Sort order |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "voteCount": integer
    "hasVoted": boolean
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /{appId}/wants/

**Submit a want**

Submit a new want. Requires end-user authentication. Rate limited to 10 per hour.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "title": string (required)
  "description": string (required)
  "category": string (required)
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "voteCount": integer
    "hasVoted": boolean
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 429:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/wants/{id}

**Get want by ID**

Get a single want. If authenticated, includes hasVoted status.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "voteCount": integer
    "hasVoted": boolean
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /{appId}/wants/{id}/vote

**Vote on a want**

Toggle vote on a want. Requires end-user authentication. Rate limited to 30 per hour.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "voteCount": integer
    "hasVoted": boolean
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 429:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/wants/stats

**Feature request statistics**

Get aggregated want statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "byStatus": {}
    "byCategory": {}
    "byPriority": {}
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/wants/

**List wants (admin)**

Get paginated list of wants with full admin data

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `status` | string | No | Filter by status |
| `category` | string | No | Filter by category |
| `sort` | string | No | Sort order |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "priority": string
    "voteCount": integer
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/wants/{id}

**Get want by ID (admin)**

Get a single want with full admin data

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "priority": string
    "voteCount": integer
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /admin/{appId}/wants/{id}

**Update want**

Update want fields (title, description, category)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Request Body:**

```json
{
  "title": string
  "description": string
  "category": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "priority": string
    "voteCount": integer
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /admin/{appId}/wants/{id}

**Delete want**

Permanently delete a want

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /admin/{appId}/wants/{id}/status

**Update want status**

Update only the status of a want

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Request Body:**

```json
{
  "status": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "priority": string
    "voteCount": integer
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /admin/{appId}/wants/{id}/priority

**Update want priority**

Update only the priority of a want

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Feature request ID |

**Request Body:**

```json
{
  "priority": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string
    "appId": string
    "title": string
    "description": string
    "category": string
    "status": string
    "priority": string
    "voteCount": integer
    "authorEmail": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Newsletter

Newsletter subscribers, campaigns, and templates

### `POST` /{appId}/newsletter/subscribe

**Subscribe to newsletter**

Subscribe an email to the newsletter for an app. Requires API Key. Rate limited to 10 per hour.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "email": string (required)
  "name": string
  "language": string — e.g. "en"
  "referralUrl": string — e.g. "https://bannersallover.com"
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "email": string
    "name": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 429:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/newsletter/unsubscribe/{token}

**Unsubscribe from newsletter**

One-click unsubscribe via token. Requires API Key. Returns an HTML confirmation page.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `token` | string | Unsubscribe token |

**Response 200:**

```json
string
```

### `POST` /{appId}/newsletter/unsubscribe/{token}

**Unsubscribe from newsletter (POST)**

Unsubscribe via POST request for programmatic use. Requires API Key.

**Auth:** API Key (header: X-API-Key)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `token` | string | Unsubscribe token |

**Response 200:**

```json
string
```

### `GET` /admin/{appId}/newsletter/subscribers/stats

**Subscriber statistics**

Get aggregated subscriber statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "active": integer
    "unsubscribed": integer
    "bounced": integer
    "bySource": {}
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/newsletter/subscribers

**List subscribers**

Get paginated list of newsletter subscribers

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `status` | string | No | Filter by status |
| `search` | string | No | Search by email or name |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "appId": string
    "email": string
    "name": string
    "language": string
    "referralUrl": string
    "status": string
    "source": string
    "subscribedAt": string
    "unsubscribedAt": string
    "createdAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/{appId}/newsletter/subscribers

**Add subscriber manually**

Manually add a subscriber to the newsletter

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "email": string (required)
  "name": string
}
```

**Response 201:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "appId": string
    "email": string
    "name": string
    "language": string
    "referralUrl": string
    "status": string
    "source": string
    "subscribedAt": string
    "unsubscribedAt": string
    "createdAt": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/{appId}/newsletter/subscribers/import

**Import subscribers**

Bulk import subscribers from a JSON array

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "subscribers": [{
    "email": string (required)
    "name": string
  }] (required)
}
```

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "imported": integer
    "skipped": integer
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /admin/{appId}/newsletter/subscribers/{id}

**Delete subscriber**

Permanently remove a subscriber

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Subscriber ID |

**Response 200:**

```json
{
  "success": boolean
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/newsletter/campaigns

**List campaigns**

Get paginated list of newsletter campaigns

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `status` | string | No | Filter by status |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string
    "appId": string
    "name": string
    "subject": string
    "status": string
    "stats": {
      "total": integer
      "sent": integer
      "failed": integer
    }
    "sentAt": string
    "completedAt": string
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/{appId}/newsletter/campaigns

**Create campaign**

Create a new newsletter campaign (draft)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "name": string (required)
  "subject": string (required)
  "htmlContent": string (required)
  "textContent": string
  "templateId": string
}
```

**Response 201:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "appId": string
    "name": string
    "subject": string
    "status": string
    "stats": {
      "total": integer
      "sent": integer
      "failed": integer
    }
    "sentAt": string
    "completedAt": string
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/newsletter/campaigns/{id}

**Get campaign**

Get campaign details including content and stats

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "appId": string
    "name": string
    "subject": string
    "status": string
    "stats": {
      "total": integer
      "sent": integer
      "failed": integer
    }
    "sentAt": string
    "completedAt": string
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /admin/{appId}/newsletter/campaigns/{id}

**Update campaign**

Update campaign fields (only draft campaigns can be updated)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Campaign ID |

**Request Body:**

```json
{
  "name": string
  "subject": string
  "htmlContent": string
  "textContent": string
}
```

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "appId": string
    "name": string
    "subject": string
    "status": string
    "stats": {
      "total": integer
      "sent": integer
      "failed": integer
    }
    "sentAt": string
    "completedAt": string
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /admin/{appId}/newsletter/campaigns/{id}

**Delete campaign**

Delete a campaign (only draft campaigns can be deleted)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/{appId}/newsletter/campaigns/{id}/send

**Send campaign**

Send the campaign to all active subscribers. Uses batch sending via Resend.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "status": string — e.g. "queued"
    "jobId": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/{appId}/newsletter/campaigns/{id}/test

**Send test email**

Send a test email to a single address to preview the campaign

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `id` | string | Campaign ID |

**Request Body:**

```json
{
  "email": string (required)
}
```

**Response 200:**

```json
{
  "success": boolean
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/{appId}/newsletter/templates

**List newsletter templates**

Get all newsletter templates

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | - |

**Response 200:**

```json
{
  "success": boolean
  "data": [{
    "_id": string
    "name": string
    "subject": string
    "htmlContent": string
    "textContent": string
    "isActive": boolean
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/{appId}/newsletter/templates

**Create newsletter template**

Create a reusable newsletter template

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | - |

**Request Body:**

```json
{
  "name": string (required)
  "subject": string (required)
  "htmlContent": string (required)
  "textContent": string
}
```

**Response 201:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "name": string
    "subject": string
    "htmlContent": string
    "textContent": string
    "isActive": boolean
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /admin/{appId}/newsletter/templates/{id}

**Update newsletter template**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Template ID |

**Request Body:**

```json
{
  "name": string
  "subject": string
  "htmlContent": string
  "textContent": string
  "isActive": boolean
}
```

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "name": string
    "subject": string
    "htmlContent": string
    "textContent": string
    "isActive": boolean
    "createdBy": string
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /admin/{appId}/newsletter/templates/{id}

**Delete newsletter template**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Template ID |

**Response 200:**

```json
{
  "success": boolean
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Contacts

Shared contact model with per-category consent and GDPR compliance

### `GET` /admin/contacts/

**List contacts**

Get paginated list of contacts with optional filters by email, consent category, or tag

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `search` | string | No | Search by email, first name, or last name |
| `category` | string | No | Filter by consent category |
| `categoryStatus` | string | No | Filter by consent status (requires category) |
| `tag` | string | No | Filter by tag |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": object[]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/contacts/{id}

**Get contact by ID**

Get contact details including full consent change history

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Contact ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /admin/contacts/{id}

**GDPR delete contact**

Soft-delete and anonymize a contact (right-to-be-forgotten). PII is removed but consent audit log is retained anonymized.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Contact ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /admin/contacts/{id}/consent

**Update consent for a category**

Update consent status for a specific category. Creates an immutable consent_event log entry.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Contact ID |

**Request Body:**

```json
{
  "category": string (required)
  "status": boolean (required)
  "reason": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /admin/contacts/{id}/opt-out

**Global opt-out**

Opt out contact from all email categories. No emails will be sent to this contact.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Contact ID |

**Request Body:**

```json
{
  "reason": string
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/contacts/{id}/export

**GDPR data export**

Export all data for a contact (right-to-access). Returns contact details and full consent history.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Contact ID |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {}
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Webhooks

Email provider webhook receivers (Resend, cold-email)

### `POST` /webhooks/resend

**Resend email webhook receiver**

Receives email lifecycle events from Resend (sent, delivered, bounced, etc.). Verified by HMAC signature.

**Auth:** None required

**Request Body:**

```json
{}
```

**Response 200:**

```json
{
  "success": boolean
  "processed": boolean
  "eventId": string
}
```

### `POST` /webhooks/cold-provider

**Cold email provider webhook (stub)**

Placeholder endpoint for future cold-email provider integration.

**Auth:** None required

**Request Body:**

```json
{}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

---

## Email Tracking

Own tracking pixel and click redirect endpoints

### `GET` /t/o/{token}

**Open tracking pixel**

Returns a 1x1 transparent GIF and records an "opened" event. Always returns 200.

**Auth:** None required

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `token` | string | Signed tracking token (may end with .gif) |

**Response 200:**

```json
string
```

### `GET` /t/c/{token}

**Click tracking redirect**

Records a "clicked" event and 302-redirects to the original URL.

**Auth:** None required

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `token` | string | Signed tracking token |

**Response 302:**

```json
string
```

**Response 400:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Email Events

Email lifecycle event log and analytics

### `GET` /admin/email-events/

**List email events**

List email lifecycle events with filters and pagination.

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `contactId` | string | No | Filter by contact ID |
| `campaignId` | string | No | Filter by campaign ID |
| `messageId` | string | No | Filter by message ID |
| `type` | string | No | Filter by event type |
| `provider` | string | No | Filter by provider |
| `appId` | string | No | Filter by app ID |

**Response 200:**

```json
{
  "success": boolean
  "data": [{
    "_id": string
    "contactId": string
    "messageId": string
    "campaignId": string
    "provider": string
    "type": string
    "createdAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email-events/message/{messageId}

**Get events for a message**

Get all email lifecycle events for a specific message ID.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `messageId` | string | Resend message ID |

**Response 200:**

```json
{
  "success": boolean
  "data": [{
    "_id": string
    "type": string
    "provider": string
    "createdAt": string
  }]
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email-events/campaign/{campaignId}/stats

**Campaign event statistics**

Get event counts by type for a specific campaign.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `campaignId` | string | Campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "data": {}
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Email Analytics

Per-campaign, per-contact, and global email engagement analytics

### `GET` /admin/email/analytics/campaigns/{campaignId}

**Campaign analytics**

Full campaign performance: sent, delivered, opened, clicked, bounced, unique counts, open rate (unique opens / delivered), CTR (unique clicks / unique opens), top clicked links, and event timeline.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `campaignId` | string | Campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "campaignId": string
    "sent": integer
    "delivered": integer
    "opened": integer
    "uniqueOpens": integer
    "clicked": integer
    "uniqueClicks": integer
    "bounced": integer
    "complained": integer
    "unsubscribed": integer
    "openRate": number
    "clickThroughRate": number
    "topLinks": [{
      "url": string
      "clicks": integer
    }]
    "timeline": [{
      "date": string
      "type": string
      "count": integer
    }]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email/analytics/campaigns/{campaignId}/recipients

**Campaign recipients breakdown**

Per-recipient status breakdown for a campaign. Shows each contact and which event types they triggered.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `campaignId` | string | Campaign ID |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |

**Response 200:**

```json
{
  "success": boolean
  "data": [{
    "contactId": string
    "events": string[]
    "lastEvent": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email/analytics/contacts/{contactId}

**Contact engagement history**

Full engagement history for a contact across all campaigns. Includes summary counts and paginated event timeline.

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `contactId` | string | Contact ID |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "contactId": string
    "totalSent": integer
    "totalOpened": integer
    "totalClicked": integer
    "totalBounced": integer
    "totalComplained": integer
    "events": [{
      "_id": string
      "campaignId": string
      "messageId": string
      "type": string
      "createdAt": string
    }]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email/analytics/overview

**Global email analytics overview**

Dashboard KPIs for a time range. Aggregate numbers across all campaigns: totals, rates, and daily timeline.

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `from` | string | No | Start of time range (ISO 8601, UTC) |
| `to` | string | No | End of time range (ISO 8601, UTC) |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "totalSent": integer
    "totalDelivered": integer
    "totalOpened": integer
    "totalUniqueOpens": integer
    "totalClicked": integer
    "totalUniqueClicks": integer
    "totalBounced": integer
    "totalComplained": integer
    "totalUnsubscribed": integer
    "openRate": number
    "clickThroughRate": number
    "bounceRate": number
    "complaintRate": number
    "timeline": [{
      "date": string
      "sent": integer
      "delivered": integer
      "opened": integer
      "clicked": integer
    }]
  }
}
```

**Response 500:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Email Jobs

Job queue monitoring and management

### `GET` /admin/email-jobs/

**List email jobs**

List email jobs with filtering and pagination

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | No | Filter by job status |
| `type` | string | No | Filter by job type |
| `campaignId` | string | No | Filter by campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "data": [{
    "_id": string
    "type": string
    "status": string
    "priority": integer
    "payload": {}
    "result": {}
    "attempts": integer
    "maxAttempts": integer
    "lastError": string
    "scheduledAt": string
    "startedAt": string
    "completedAt": string
    "createdAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email-jobs/stats

**Email job statistics**

Get aggregate job queue statistics, optionally filtered by campaign

**Auth:** Bearer Token (JWT)

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `campaignId` | string | No | Filter stats by campaign ID |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "pending": integer
    "processing": integer
    "completed": integer
    "failed": integer
    "dead": integer
    "total": integer
    "progress": {
      "total": integer
      "completed": integer
      "failed": integer
      "pending": integer
      "processing": integer
      "dead": integer
      "percentage": integer
    }
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /admin/email-jobs/{id}

**Get email job details**

Get a single email job by ID with full payload, result, and error details

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Job ID |

**Response 200:**

```json
{
  "success": boolean
  "data": {
    "_id": string
    "type": string
    "status": string
    "priority": integer
    "payload": {}
    "result": {}
    "attempts": integer
    "maxAttempts": integer
    "lastError": string
    "scheduledAt": string
    "startedAt": string
    "completedAt": string
    "createdAt": string
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

---

## Health

Health checks

### `GET` /health

**Server health check**

Check server status, database connections, and memory usage

**Auth:** None required

**Response 200:**

```json
{
  "status": string — e.g. "ok"
  "timestamp": string
  "uptime": number
  "memory": {
    "rss": string — e.g. "128MB"
    "heapUsed": string — e.g. "64MB"
    "heapTotal": string — e.g. "128MB"
    "external": string — e.g. "2MB"
    "arrayBuffers": string — e.g. "1MB"
  }
  "databases": {
    "connected": integer
    "total": integer
    "failed": integer
  }
}
```

---

## Plans

### `GET` /{appId}/plans/stats

**Plan statistics**

Get aggregated plan statistics for an app

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "total": integer
    "active": integer
    "inactive": integer
    "byInterval": {}
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/plans/active

**List active plans**

Get all active plans sorted by price (ascending)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string — e.g. "6901449da196a70c326622f8"
    "id": string — e.g. "pro_monthly"
    "name": string — e.g. "Pro"
    "price": number — e.g. 40
    "interval": string — e.g. "EVERY_30_DAYS"
    "trialDays": integer — e.g. 14
    "features": string[]
    "isActive": boolean — e.g. true
    "createdAt": string
    "updatedAt": string
  }]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/plans/features

**List all features**

Get all unique features used across all plans

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": string[]
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/plans/

**List plans**

Get paginated list of plans with filters

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `limit` | integer | No | Items per page |
| `isActive` | boolean | No | Filter by active status |
| `search` | string | No | Search by id or name |
| `sortBy` | string | No | Field to sort by |
| `sortOrder` | string | No | - |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": [{
    "_id": string — e.g. "6901449da196a70c326622f8"
    "id": string — e.g. "pro_monthly"
    "name": string — e.g. "Pro"
    "price": number — e.g. 40
    "interval": string — e.g. "EVERY_30_DAYS"
    "trialDays": integer — e.g. 14
    "features": string[]
    "isActive": boolean — e.g. true
    "createdAt": string
    "updatedAt": string
  }]
  "pagination": {
    "page": integer
    "limit": integer
    "total": integer
    "pages": integer
  }
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `POST` /{appId}/plans/

**Create plan**

Create a new subscription plan

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |

**Request Body:**

```json
{
  "id": string (required)
  "name": string (required)
  "price": number (required)
  "interval": string (required)
  "trialDays": integer
  "features": string[]
  "isActive": boolean
}
```

**Response 201:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string — e.g. "6901449da196a70c326622f8"
    "id": string — e.g. "pro_monthly"
    "name": string — e.g. "Pro"
    "price": number — e.g. 40
    "interval": string — e.g. "EVERY_30_DAYS"
    "trialDays": integer — e.g. 14
    "features": string[]
    "isActive": boolean — e.g. true
    "createdAt": string
    "updatedAt": string
  }
  "message": string
}
```

**Response 401:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

**Response 409:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `GET` /{appId}/plans/{planId}

**Get plan by ID**

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `planId` | string | Plan MongoDB _id |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string — e.g. "6901449da196a70c326622f8"
    "id": string — e.g. "pro_monthly"
    "name": string — e.g. "Pro"
    "price": number — e.g. 40
    "interval": string — e.g. "EVERY_30_DAYS"
    "trialDays": integer — e.g. 14
    "features": string[]
    "isActive": boolean — e.g. true
    "createdAt": string
    "updatedAt": string
  }
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /{appId}/plans/{planId}

**Update plan**

Update an existing plan (cannot change plan id)

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `planId` | string | Plan MongoDB _id |

**Request Body:**

```json
{
  "name": string
  "price": number
  "interval": string
  "trialDays": integer
  "features": string[]
  "isActive": boolean
}
```

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string — e.g. "6901449da196a70c326622f8"
    "id": string — e.g. "pro_monthly"
    "name": string — e.g. "Pro"
    "price": number — e.g. 40
    "interval": string — e.g. "EVERY_30_DAYS"
    "trialDays": integer — e.g. 14
    "features": string[]
    "isActive": boolean — e.g. true
    "createdAt": string
    "updatedAt": string
  }
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `DELETE` /{appId}/plans/{planId}

**Delete plan**

Permanently delete a plan

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `planId` | string | Plan MongoDB _id |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```

### `PATCH` /{appId}/plans/{planId}/toggle

**Toggle plan status**

Toggle plan active/inactive status

**Auth:** Bearer Token (JWT)

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | string | Application ID (e.g.: banners-all-over) |
| `planId` | string | Plan MongoDB _id |

**Response 200:**

```json
{
  "success": boolean — e.g. true
  "data": {
    "_id": string — e.g. "6901449da196a70c326622f8"
    "id": string — e.g. "pro_monthly"
    "name": string — e.g. "Pro"
    "price": number — e.g. 40
    "interval": string — e.g. "EVERY_30_DAYS"
    "trialDays": integer — e.g. 14
    "features": string[]
    "isActive": boolean — e.g. true
    "createdAt": string
    "updatedAt": string
  }
  "message": string
}
```

**Response 404:**

```json
{
  "success": boolean — e.g. false
  "error": string
}
```
