526 lines
15 KiB
Markdown
526 lines
15 KiB
Markdown
# TGEX Backend API v2
|
||
|
||
## Аутентификация
|
||
|
||
**JWT Bearer Token** в заголовке `Authorization: Bearer <token>`
|
||
|
||
### Получение токена через Telegram Bot
|
||
|
||
Логика точно такая же как и раньше
|
||
|
||
### Эндпоинты
|
||
|
||
| Метод | Эндпоинт | Описание | Auth |
|
||
|-------|----------|----------|------|
|
||
| `GET` | `/auth/complete` | Обмен временного токена на JWT | ❌ |
|
||
| `GET` | `/auth/me` | Получить текущего пользователя | ✅ |
|
||
|
||
**Response** `/auth/me`:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"telegram_id": 123456789,
|
||
"username": "string | null"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Workspaces (Рабочие пространства)
|
||
|
||
Воркспейсы - изолированные пространства для команд. У пользователя может быть несколько воркспейсов.
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces` | Список воркспейсов пользователя (пагинация) |
|
||
| `POST` | `/workspaces` | Создать воркспейс |
|
||
| `PATCH` | `/workspaces/{workspace_id}` | Обновить название |
|
||
| `DELETE` | `/workspaces/{workspace_id}` | Удалить воркспейс |
|
||
|
||
**Body** `POST /workspaces`:
|
||
```json
|
||
{
|
||
"name": "string"
|
||
}
|
||
```
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"name": "string"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Workspace Members (Участники воркспейса)
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{workspace_id}/members` | Список участников (пагинация) |
|
||
| `PUT` | `/workspaces/{workspace_id}/members/{user_id}/permissions` | Обновить права участника |
|
||
|
||
**Права доступа** (`PermissionKey`):
|
||
- `admin_full` - полный доступ
|
||
- `projects_read` - чтение проектов
|
||
- `projects_write` - запись проектов
|
||
- `placements_read` - чтение размещений
|
||
- `placements_write` - запись размещений
|
||
- `analytics_read` - чтение аналитики
|
||
|
||
**Scope types**: `project` (ограничение прав на конкретный проект)
|
||
|
||
**Body** `PUT permissions`:
|
||
```json
|
||
{
|
||
"permissions": [
|
||
{
|
||
"key": "admin_full"
|
||
},
|
||
{
|
||
"key": "projects_write",
|
||
"scopes": [
|
||
{"type": "project", "id": "project-uuid"}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Workspace Invites (Приглашения)
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{workspace_id}/invites` | Список приглашений (пагинация) |
|
||
| `POST` | `/workspaces/{workspace_id}/invites` | Создать приглашение |
|
||
|
||
**Body** `POST /invites`:
|
||
```json
|
||
{
|
||
"username": "telegram_username" // без @
|
||
}
|
||
```
|
||
|
||
**Статусы приглашения**: `PENDING`, `ACCEPTED`, `REJECTED`
|
||
|
||
---
|
||
|
||
## Projects (Проекты / Целевые каналы)
|
||
|
||
Проекты = целевые Telegram каналы, которые продвигаются. Создаются через Telegram бота.
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{workspace_id}/projects` | Список проектов (пагинация) |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"telegram_id": -1001234567890,
|
||
"title": "string",
|
||
"username": "string | null",
|
||
"status": "ACTIVE | INACTIVE | ARCHIVED"
|
||
}
|
||
```
|
||
|
||
**Управление проектами**: через Telegram бота (добавить/удалить канал)
|
||
|
||
---
|
||
|
||
## Channels (Каналы для размещений)
|
||
|
||
Каталог Telegram каналов, где можно размещать рекламу.
|
||
|
||
| Метод | Эндпоинт | Описание | Фильтры |
|
||
|-------|----------|----------|---------|
|
||
| `GET` | `/channels` | Список каналов (пагинация) | `?username=...` |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"telegram_id": -1002345678901,
|
||
"title": "string",
|
||
"username": "string | null",
|
||
"description": "string | null",
|
||
"subscribers_count": 15000
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Purchase Plan (План закупа)
|
||
|
||
План закупа - список каналов для покупки рекламы в рамках проекта.
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{ws}/projects/{proj}/purchase-plan/channels` | Список каналов плана (пагинация) |
|
||
| `POST` | `/workspaces/{ws}/projects/{proj}/purchase-plan/channels` | Добавить канал в план |
|
||
| `PATCH` | `/workspaces/{ws}/projects/{proj}/purchase-plan/channels/{id}` | Обновить параметры канала |
|
||
| `DELETE` | `/workspaces/{ws}/projects/{proj}/purchase-plan/channels/{id}` | Удалить из плана |
|
||
|
||
**Body** `POST`:
|
||
```json
|
||
{
|
||
"channel_id": "uuid",
|
||
"planned_date": "2025-01-15T12:00:00Z", // optional
|
||
"price": 5000.50, // optional
|
||
"comment": "string" // optional
|
||
}
|
||
```
|
||
|
||
**Body** `PATCH`:
|
||
```json
|
||
{
|
||
"planned_date": "2025-01-15T12:00:00Z", // optional
|
||
"price": 5000.50, // optional
|
||
"comment": "string" // optional
|
||
}
|
||
```
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"status": "PLANNED | COMPLETED | CANCELLED",
|
||
"planned_date": "2025-01-15T12:00:00Z | null",
|
||
"price": 5000.50,
|
||
"comment": "string | null",
|
||
"channel": {
|
||
"id": "uuid",
|
||
"title": "string",
|
||
"username": "string | null",
|
||
"subscribers_count": 15000
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Creatives (Креативы)
|
||
|
||
Креативы = рекламные материалы (текст с пригласительной ссылкой).
|
||
|
||
| Метод | Эндпоинт | Описание | Фильтры |
|
||
|-------|----------|----------|---------|
|
||
| `GET` | `/workspaces/{ws}/creatives` | Список креативов (пагинация) | `?project_id=...&include_archived=false` |
|
||
| `GET` | `/workspaces/{ws}/creatives/{id}` | Один креатив | |
|
||
| `POST` | `/workspaces/{ws}/creatives?project_id=...` | Создать креатив | |
|
||
| `PATCH` | `/workspaces/{ws}/creatives/{id}` | Обновить креатив | |
|
||
| `DELETE` | `/workspaces/{ws}/creatives/{id}` | Удалить (архивировать) | |
|
||
|
||
**Body** `POST/PATCH`:
|
||
```json
|
||
{
|
||
"name": "string",
|
||
"text": "Текст с {invite_link} тегом"
|
||
}
|
||
```
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"name": "string",
|
||
"text": "string",
|
||
"status": "ACTIVE | ARCHIVED",
|
||
"placements_count": 5
|
||
}
|
||
```
|
||
|
||
**Важно**: `{invite_link}` в тексте заменяется на уникальную ссылку при создании размещения.
|
||
|
||
---
|
||
|
||
## Placements (Размещения)
|
||
|
||
Размещения = факт размещения креатива в канале.
|
||
|
||
| Метод | Эндпоинт | Описание | Фильтры |
|
||
|-------|----------|----------|---------|
|
||
| `GET` | `/workspaces/{ws}/placements` | Список размещений (пагинация) | `?project_id=...&placement_channel_id=...&creative_id=...&include_archived=false` |
|
||
| `GET` | `/workspaces/{ws}/placements/{id}` | Одно размещение | |
|
||
| `POST` | `/workspaces/{ws}/placements` | Создать размещение | |
|
||
| `PATCH` | `/workspaces/{ws}/placements/{id}` | Обновить размещение | |
|
||
| `DELETE` | `/workspaces/{ws}/placements/{id}` | Удалить (архивировать) | |
|
||
|
||
**Body** `POST`:
|
||
```json
|
||
{
|
||
"project_id": "uuid",
|
||
"placement_channel_id": "uuid",
|
||
"creative_id": "uuid",
|
||
"placement_date": "2025-01-15T12:00:00Z",
|
||
"cost": 5000.50, // optional
|
||
"comment": "string", // optional
|
||
"ad_post_url": "https://t.me/channel/123", // optional
|
||
"invite_link_type": "PUBLIC | APPROVAL"
|
||
}
|
||
```
|
||
|
||
**Body** `PATCH`:
|
||
```json
|
||
{
|
||
"placement_date": "2025-01-15T12:00:00Z", // optional
|
||
"cost": 5000.50, // optional
|
||
"comment": "string", // optional
|
||
"ad_post_url": "https://t.me/channel/123" // optional
|
||
}
|
||
```
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"placement_date": "2025-01-15T12:00:00Z",
|
||
"cost": 5000.50,
|
||
"comment": "string | null",
|
||
"ad_post_url": "string | null",
|
||
"invite_link_type": "PUBLIC | APPROVAL",
|
||
"invite_link": "https://t.me/+UniqueCode",
|
||
"status": "ACTIVE | ARCHIVED",
|
||
"subscriptions_count": 42,
|
||
"views_count": 1850,
|
||
"views_availability": "UNKNOWN | AVAILABLE | UNAVAILABLE | MANUAL",
|
||
"last_views_fetch_at": "2025-01-15T14:30:00Z",
|
||
"project": {
|
||
"id": "uuid",
|
||
"title": "string",
|
||
"username": "string | null"
|
||
},
|
||
"placement_channel": {
|
||
"id": "uuid",
|
||
"title": "string",
|
||
"username": "string | null",
|
||
"subscribers_count": 15000
|
||
},
|
||
"creative": {
|
||
"id": "uuid",
|
||
"name": "string"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Типы пригласительных ссылок**:
|
||
- `PUBLIC` - открытая ссылка
|
||
- `APPROVAL` - с одобрением через бота
|
||
|
||
**Статус просмотров**:
|
||
- `UNKNOWN` - еще не проверялось
|
||
- `AVAILABLE` - просмотры доступны (автообновление)
|
||
- `UNAVAILABLE` - нет доступа к просмотрам
|
||
- `MANUAL` - ручной ввод
|
||
|
||
---
|
||
|
||
## Views History (История просмотров)
|
||
|
||
| Метод | Эндпоинт | Описание | Фильтры |
|
||
|-------|----------|----------|---------|
|
||
| `GET` | `/workspaces/{ws}/placements/{id}/views/history` | История просмотров (пагинация) | `?from_date=...&to_date=...` |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"views_count": 1850,
|
||
"fetched_at": "2025-01-15T14:30:00Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Analytics (Аналитика)
|
||
|
||
Все эндпоинты поддерживают фильтр `?project_id=...` для конкретного проекта.
|
||
|
||
### Аналитика размещений
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{ws}/analytics/placements` | Аналитика по размещениям (пагинация) |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"placement_id": "uuid",
|
||
"placement_channel_title": "string",
|
||
"placement_channel_username": "string | null",
|
||
"creative_name": "string",
|
||
"placement_date": "2025-01-15T12:00:00Z",
|
||
"cost": 5000.50,
|
||
"subscriptions_count": 42,
|
||
"views_count": 1850,
|
||
"cps": 119.05, // cost per subscription
|
||
"cpm": 2702.70 // cost per mille views
|
||
}
|
||
```
|
||
|
||
### Аналитика креативов
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{ws}/analytics/creatives` | Аналитика по креативам (пагинация) |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"creative_id": "uuid",
|
||
"creative_name": "string",
|
||
"placements_count": 5,
|
||
"total_cost": 25000.00,
|
||
"total_subscriptions": 210,
|
||
"total_views": 9250,
|
||
"avg_cps": 119.05,
|
||
"avg_cpm": 2702.70
|
||
}
|
||
```
|
||
|
||
### Аналитика каналов
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| `GET` | `/workspaces/{ws}/analytics/channels` | Аналитика по каналам (пагинация) |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"channel_id": "uuid",
|
||
"channel_title": "string",
|
||
"channel_username": "string | null",
|
||
"placements_count": 3,
|
||
"total_cost": 15000.00,
|
||
"total_subscriptions": 126,
|
||
"total_views": 5550,
|
||
"avg_cps": 119.05,
|
||
"avg_cpm": 2702.70
|
||
}
|
||
```
|
||
|
||
### Аналитика расходов
|
||
|
||
| Метод | Эндпоинт | Описание | Фильтры |
|
||
|-------|----------|----------|---------|
|
||
| `GET` | `/workspaces/{ws}/analytics/spending` | Динамика расходов | `?date_from=...&date_to=...&grouping=DAY\|WEEK\|MONTH` |
|
||
|
||
**Response**:
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"date": "2025-01-15",
|
||
"cost": 5000.50,
|
||
"subscriptions": 42,
|
||
"views": 1850
|
||
}
|
||
],
|
||
"total_cost": 25000.00,
|
||
"total_subscriptions": 210,
|
||
"total_views": 9250
|
||
}
|
||
```
|
||
|
||
**Группировка** (`DateGrouping`): `DAY`, `WEEK`, `MONTH`
|
||
|
||
---
|
||
|
||
## Пагинация
|
||
|
||
Все эндпоинты с пагинацией возвращают:
|
||
|
||
```json
|
||
{
|
||
"items": [...],
|
||
"total": 42,
|
||
"page": 1,
|
||
"size": 50,
|
||
"pages": 1
|
||
}
|
||
```
|
||
|
||
**Query параметры**: `?page=1&size=50`
|
||
|
||
---
|
||
|
||
## Статусы и Enum
|
||
|
||
### WorkspaceUserStatus
|
||
- `ACTIVE` - активный участник
|
||
- `INVITED` - приглашён, но не принял
|
||
|
||
### WorkspaceInviteStatus
|
||
- `PENDING` - ожидает принятия
|
||
- `ACCEPTED` - принято
|
||
- `REJECTED` - отклонено
|
||
|
||
### ProjectStatus / ChannelStatus
|
||
- `ACTIVE` - активен
|
||
- `INACTIVE` - неактивен
|
||
- `ARCHIVED` - архивирован
|
||
|
||
### CreativeStatus / PlacementStatus
|
||
- `ACTIVE` - активен
|
||
- `ARCHIVED` - архивирован
|
||
|
||
### PurchasePlanStatus
|
||
- `PLANNED` - запланирован
|
||
- `COMPLETED` - завершён
|
||
- `CANCELLED` - отменён
|
||
|
||
### InviteLinkType
|
||
- `PUBLIC` - открытая ссылка
|
||
- `APPROVAL` - с одобрением бота
|
||
|
||
### PostViewsAvailability
|
||
- `UNKNOWN` - неизвестно (ещё не проверялось)
|
||
- `AVAILABLE` - доступны (автообновление)
|
||
- `UNAVAILABLE` - недоступны
|
||
- `MANUAL` - ручной ввод
|
||
|
||
---
|
||
|
||
## Коды ошибок HTTP
|
||
|
||
- `200` - OK
|
||
- `201` - Created
|
||
- `204` - No Content (успешное удаление)
|
||
- `400` - Bad Request (валидация)
|
||
- `401` - Unauthorized (нет токена)
|
||
- `403` - Forbidden (нет прав)
|
||
- `404` - Not Found
|
||
- `500` - Internal Server Error
|
||
|
||
**Формат ошибки**:
|
||
```json
|
||
{
|
||
"detail": "Error message"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Особенности
|
||
|
||
1. **Все UUID в формате**: `xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx`
|
||
2. **Даты в ISO 8601**: `2025-01-15T12:00:00Z` (UTC)
|
||
3. **Метрики автообновляются**: просмотры обновляются воркером каждые 60 секунд (если `views_availability = AVAILABLE`)
|
||
4. **Подписки отслеживаются**: через Telegram бота при присоединении по invite_link
|
||
5. **Права проверяются**: все эндпоинты под воркспейсами требуют соответствующих прав
|
||
6. **Soft delete**: удаление = архивирование (статус `ARCHIVED`)
|
||
|
||
---
|
||
|
||
## Telegram Bot Integration
|
||
|
||
- Создание проектов (добавление целевых каналов)
|
||
- Создание креативов через диалог
|
||
- Приглашения в воркспейсы
|
||
- Отслеживание подписок через invite links
|
||
- Начальная аутентификация (`/start` → временный токен)
|
||
|