# API Документация ## Базовый URL ``` https://api.tgex.app/v1 ``` ## Аутентификация Все запросы (кроме `/auth/*`) требуют JWT токен в заголовке: ``` Authorization: Bearer ``` --- ## 1. Аутентификация ### POST `/auth/init` Инициализация процесса авторизации через Telegram **Response:** ```typescript { bot_username: string; start_param: string; // "login" } ``` ### POST `/auth/complete` Завершение авторизации с одноразовым токеном **Request:** ```typescript { token: string; // one-time token from bot } ``` **Response:** ```typescript { access_token: string; refresh_token: string; user: User; } ``` ### POST `/auth/refresh` Обновление access токена **Request:** ```typescript { refresh_token: string; } ``` **Response:** ```typescript { access_token: string; } ``` ### GET `/auth/me` Получение информации о текущем пользователе **Response:** ```typescript User; ``` --- ## 2. Типы данных ### User ```typescript { id: string; telegram_id: number; username: string | null; first_name: string; last_name: string | null; created_at: string; // ISO 8601 } ``` ### TargetChannel ```typescript { id: string; telegram_id: number; title: string; username: string | null; // @channel_name description: string | null; is_active: boolean; created_at: string; updated_at: string; // Stats total_purchases: number; total_subscriptions: number; avg_cpf: number | null; // Average Cost Per Follower } ``` ### ExternalChannel ```typescript { id: string; telegram_id: number | null; title: string; username: string | null; link: string; // t.me link or custom subscribers_count: number | null; description: string | null; created_at: string; updated_at: string; // Relations target_channels: string[]; // IDs of target channels // Stats total_purchases: number; avg_cpf: number | null; avg_cpm: number | null; } ``` ### Creative ```typescript { id: string; name: string; text: string; // Template with {link} placeholder target_channel_id: string; is_archived: boolean; created_at: string; updated_at: string; // Relations target_channel: TargetChannel; // Stats total_purchases: number; total_subscriptions: number; avg_cpf: number | null; } ``` ### Purchase ```typescript { id: string; target_channel_id: string; external_channel_id: string; creative_id: string; // Purchase details scheduled_date: string | null; // ISO 8601 actual_date: string | null; cost: number | null; // in rubles comment: string | null; post_link: string | null; // Link to ad post in external channel invite_link: string; // Generated invite link invite_link_type: "public" | "private"; // with bot approval or not is_archived: boolean; created_at: string; updated_at: string; // Relations target_channel: TargetChannel; external_channel: ExternalChannel; creative: Creative; // Stats subscriptions_count: number; views_count: number | null; cpf: number | null; // Cost Per Follower cpm: number | null; // Cost Per Mile (1000 views) conversion_rate: number | null; // subscriptions / views * 100 } ``` ### Subscription ```typescript { id: string; purchase_id: string; telegram_user_id: number; username: string | null; joined_at: string; // ISO 8601 event_type: "chat_member" | "join_request"; } ``` ### ViewsSnapshot ```typescript { id: string; purchase_id: string; views: number; fetched_at: string; // ISO 8601 } ``` --- ## 3. Целевые каналы ### GET `/target-channels` Получение списка целевых каналов **Query params:** - `is_active?: boolean` - фильтр по активности **Response:** ```typescript { data: TargetChannel[]; total: number; } ``` ### GET `/target-channels/:id` Получение детальной информации о канале **Response:** ```typescript TargetChannel & { recent_purchases: Purchase[]; // last 5 stats_by_period: { period: 'day' | 'week' | 'month'; subscriptions: number; cost: number; cpf: number; }[]; } ``` ### PATCH `/target-channels/:id` Обновление канала (только is_active) **Request:** ```typescript { is_active: boolean; } ``` **Response:** ```typescript TargetChannel; ``` ### DELETE `/target-channels/:id` Отключение канала (soft delete) **Response:** ```typescript { success: true; } ``` --- ## 4. Внешние каналы ### GET `/external-channels` Получение списка внешних каналов **Query params:** - `target_channel_id?: string` - фильтр по целевому каналу - `search?: string` - поиск по названию/username **Response:** ```typescript { data: ExternalChannel[]; total: number; } ``` ### GET `/external-channels/:id` Получение детальной информации **Response:** ```typescript ExternalChannel & { purchases: Purchase[]; } ``` ### POST `/external-channels` Создание нового внешнего канала **Request:** ```typescript { title: string; username?: string; link: string; subscribers_count?: number; description?: string; target_channel_ids: string[]; // Привязка к целевым каналам } ``` **Response:** ```typescript ExternalChannel; ``` ### PATCH `/external-channels/:id` Обновление внешнего канала **Request:** ```typescript Partial<{ title: string; username: string; link: string; subscribers_count: number; description: string; target_channel_ids: string[]; }>; ``` **Response:** ```typescript ExternalChannel; ``` ### DELETE `/external-channels/:id` Удаление внешнего канала **Response:** ```typescript { success: true; } ``` ### POST `/external-channels/import` Импорт из Excel (Tgstat) **Request:** ``` Content-Type: multipart/form-data file: File (Excel) target_channel_id: string ``` **Response:** ```typescript { imported: number; skipped: number; errors: string[]; channels: ExternalChannel[]; } ``` --- ## 5. Креативы ### GET `/creatives` Получение списка креативов **Query params:** - `target_channel_id?: string` - фильтр по целевому каналу - `is_archived?: boolean` - фильтр по архивности **Response:** ```typescript { data: Creative[]; total: number; } ``` ### GET `/creatives/:id` Получение детальной информации **Response:** ```typescript Creative & { purchases: Purchase[]; } ``` ### POST `/creatives` Создание нового креатива **Request:** ```typescript { name: string; text: string; // Must contain {link} placeholder target_channel_id: string; } ``` **Response:** ```typescript Creative; ``` ### PATCH `/creatives/:id` Обновление креатива **Request:** ```typescript Partial<{ name: string; text: string; is_archived: boolean; }>; ``` **Response:** ```typescript Creative; ``` ### DELETE `/creatives/:id` Удаление креатива (только если нет активных закупов) **Response:** ```typescript { success: true; } ``` --- ## 6. Закупы ### GET `/purchases` Получение списка закупов **Query params:** - `target_channel_id?: string` - `external_channel_id?: string` - `creative_id?: string` - `is_archived?: boolean` - `date_from?: string` (ISO 8601) - `date_to?: string` (ISO 8601) - `sort?: 'date' | 'cost' | 'cpf' | 'subscriptions'` - `order?: 'asc' | 'desc'` **Response:** ```typescript { data: Purchase[]; total: number; } ``` ### GET `/purchases/:id` Получение детальной информации о закупе **Response:** ```typescript Purchase & { subscriptions: Subscription[]; views_history: ViewsSnapshot[]; } ``` ### POST `/purchases` Создание нового закупа **Request:** ```typescript { target_channel_id: string; external_channel_id: string; creative_id: string; scheduled_date?: string; actual_date?: string; cost?: number; comment?: string; post_link?: string; invite_link_type: 'public' | 'private'; } ``` **Response:** ```typescript Purchase & { formatted_message: string; // Creative text with invite_link inserted } ``` ### PATCH `/purchases/:id` Обновление закупа **Request:** ```typescript Partial<{ scheduled_date: string; actual_date: string; cost: number; comment: string; post_link: string; is_archived: boolean; }>; ``` **Response:** ```typescript Purchase; ``` ### DELETE `/purchases/:id` Удаление закупа **Response:** ```typescript { success: true; } ``` ### POST `/purchases/:id/refresh-views` Принудительное обновление просмотров **Response:** ```typescript { views: number; fetched_at: string; } ``` --- ## 7. Аналитика ### GET `/analytics/overview` Общая статистика **Query params:** - `target_channel_id?: string` - `date_from?: string` - `date_to?: string` **Response:** ```typescript { total_purchases: number; total_cost: number; total_subscriptions: number; avg_cpf: number; avg_cpm: number; avg_conversion_rate: number; } ``` ### GET `/analytics/costs` Данные для графика затрат **Query params:** - `target_channel_id?: string` - `date_from: string` (required) - `date_to: string` (required) - `group_by: 'day' | 'week' | 'month' | 'quarter' | 'year'` **Response:** ```typescript { data: { period: string; // ISO 8601 date (start of period) cost: number; purchases_count: number; subscriptions: number; } []; } ``` ### GET `/analytics/channels` Аналитика по каналам **Query params:** - `target_channel_id?: string` - `date_from?: string` - `date_to?: string` - `type: 'external' | 'target'` **Response:** ```typescript { data: { channel_id: string; channel_name: string; purchases_count: number; total_cost: number; total_subscriptions: number; avg_cpf: number; avg_cpm: number; } []; } ``` ### GET `/analytics/creatives` Аналитика по креативам **Query params:** - `target_channel_id?: string` - `date_from?: string` - `date_to?: string` **Response:** ```typescript { data: { creative_id: string; creative_name: string; purchases_count: number; total_cost: number; total_subscriptions: number; avg_cpf: number; conversion_rate: number; } []; } ``` --- ## 8. Коды ошибок ```typescript { error: { code: string; message: string; details?: any; } } ``` Основные коды: - `UNAUTHORIZED` - 401 - `FORBIDDEN` - 403 - `NOT_FOUND` - 404 - `VALIDATION_ERROR` - 422 - `INTERNAL_ERROR` - 500 - `CHANNEL_NOT_FOUND` - 404 - `CHANNEL_ALREADY_EXISTS` - 409 - `INSUFFICIENT_PERMISSIONS` - 403 - `CREATIVE_IN_USE` - 409 - `INVALID_INVITE_LINK` - 422