# Спецификация API-слоя — Grace CRM Assistant

**Версия:** 1\.0 **Дата:** 4 мая 2026

**Статус:** Этап 0 -- согласование файл 03_api\_[spec.md](http://spec.md)

---

## 1\. Назначение

API-слой Grace CRM Assistant предоставляет интерфейс для:

-  получения данных из OpenSearch внешними системами (BI, ERP, 1С)

-  получения AI-сигналов и рекомендаций

-  управления агентами (запуск синхронизации, статус системы)

-  будущей интеграции с другими сервисами экосистемы I-TECH

Все эндпоинты доступны только на внутренней сети. Публичного доступа нет.

---

## 2\. Базовые параметры

| Параметр        | Значение                                                                     |
|-----------------|------------------------------------------------------------------------------|
| Base URL        | [`http://grace-ai.i-tech.local/api/v1`](http://grace-ai.i-tech.local/api/v1) |
| Протокол        | HTTP/HTTPS (TLS 1.3 внутри периметра)                                        |
| Формат          | JSON                                                                         |
| Аутентификация  | Bearer Token (JWT)                                                           |
| Версионирование | URI (`/v1/`, `/v2/`)                                                         |

### Заголовки запроса

```
Authorization: Bearer {token}Content-Type: application/jsonAccept: application/json
```

### Стандартный формат ответа

```
{  "success": true,  "data": { ... },  "meta": {    "total": 100,    "page": 1,    "per_page": 20  }}
```

### Стандартный формат ошибки

```
{  "success": false,  "error": {    "code": "UNAUTHORIZED",    "message": "Токен недействителен или истёк"  }}
```

---

## 3\. Аутентификация и авторизация

### 3\.1 Получение токена

```
POST /api/v1/auth/tokenContent-Type: application/json​{  "client_id": "erp-system",  "client_secret": "***"}
```

**Ответ:**

```
{  "access_token": "eyJ...",  "expires_in": 3600,  "token_type": "Bearer"}
```

### 3\.2 Матрица доступа по ролям

| Группа эндпоинтов           | manager | rop | deputy_gd | admin | system |
|-----------------------------|---------|-----|-----------|-------|--------|
| `/projects` -- свои проекты | ✅       | ✅   | ✅         | ✅     | ✅      |
| `/projects` -- все проекты  | ❌       | ✅   | ✅         | ✅     | ✅      |
| `/recommendations`          | ✅       | ✅   | ❌         | ✅     | ✅      |
| `/analytics/team`           | ❌       | ✅   | ✅         | ✅     | ✅      |
| `/analytics/aggregate`      | ❌       | ❌   | ✅         | ✅     | ✅      |
| `/sync/*`                   | ❌       | ❌   | ❌         | ✅     | ✅      |
| `/admin/*`                  | ❌       | ❌   | ❌         | ✅     | ❌      |

---

## 4\. Эндпоинты -- данные CRM

### 4\.1 Проекты / Сделки

```
GET /api/v1/projects
```

**Параметры фильтрации:**

| Параметр          | Тип     | Описание                       |
|-------------------|---------|--------------------------------|
| `manager_id`      | integer | Фильтр по менеджеру            |
| `status`          | string  | Статус проекта                 |
| `probability_min` | integer | Минимальная вероятность, %     |
| `updated_since`   | ISO8601 | Изменено после даты            |
| `page`            | integer | Страница (default: 1)          |
| `per_page`        | integer | Записей на странице (max: 100) |

**Ответ:**

```
{  "success": true,  "data": [    {      "id": 15714,      "name": "ЖК Дмитровское небо",      "account_name": "КР ЭНЕРГО ООО",      "manager_name": "Пересунько Павел",      "project_status": "Отправлено КП",      "probability_percent": 65,      "amount": 91769410,      "forecast_date": "2026-06-30"    }  ],  "meta": { "total": 247, "page": 1, "per_page": 20 }}
```

```
GET /api/v1/projects/{id}
```

Возвращает полную карточку проекта включая историю комментариев и список расчётов.

---

### 4\.2 Рекомендации

```
GET /api/v1/recommendations
```

**Параметры:**

| Параметр     | Тип     | Описание                            |
|--------------|---------|-------------------------------------|
| `manager_id` | integer | Рекомендации для менеджера          |
| `project_id` | integer | Рекомендации по проекту             |
| `status`     | string  | `pending` / `acknowledged` / `done` |

**Ответ:**

```
{  "success": true,  "data": [    {      "id": "rec_001",      "project_id": 15714,      "project_name": "ЖК Дмитровское небо",      "account_name": "КР ЭНЕРГО ООО",      "action": "Позвонить клиенту и уточнить статус решения по КП",      "deadline": "2026-05-07",      "rationale": "Последний контакт 18 дней назад, КП на согласовании",      "knowledge_ref": "kb://cases/energo-jk-pattern",      "created_at": "2026-05-04T09:00:00",      "status": "pending"    }  ]}
```

```
POST /api/v1/recommendations/{id}/acknowledge
```

Менеджер подтверждает получение рекомендации.

```
POST /api/v1/recommendations/{id}/done
```

Менеджер отмечает рекомендацию выполненной.

---

### 4\.3 Аналитика

```
GET /api/v1/analytics/funnel
```

Воронка продаж с разбивкой по статусам и менеджерам.

**Параметры:** `manager_id`, `date_from`, `date_to`, `group_by` (manager / status / month)

```
GET /api/v1/analytics/data-quality
```

Коэффициент качества данных (Кк) по менеджерам и отделу.

**Ответ:**

```
{  "success": true,  "data": {    "team_kk": 0.82,    "managers": [      {        "manager_id": 12,        "manager_name": "Стегнина Мария",        "kk": 0.91,        "projects_total": 249,        "projects_with_gaps": 22      }    ]  }}
```

```
GET /api/v1/analytics/backlog
```

Сводка по backlog-позициям (незавершённые отгрузки).

---

## 5\. Эндпоинты -- управление системой

### 5\.1 Синхронизация (только role: admin, system)

```
POST /api/v1/sync/runContent-Type: application/json​{  "mode": "incremental",  "entities": ["projects", "orders"]}
```

**Ответ:**

```
{  "success": true,  "data": {    "run_id": "sync_2026_05_04_090000",    "status": "started",    "estimated_duration_sec": 25  }}
```

```
GET /api/v1/sync/status/{run_id}
```

Статус текущего или последнего запуска синхронизации.

```
GET /api/v1/sync/history
```

История запусков синхронизации (последние 30).

---

### 5\.2 Состояние системы

```
GET /api/v1/health
```

Доступен без авторизации. Возвращает статус всех компонентов.

```
{  "status": "healthy",  "components": {    "opensearch": "healthy",    "ragflow": "healthy",    "grace_api": "healthy",    "sync_agent": "healthy",    "last_sync": "2026-05-04T08:45:00"  }}
```

```
GET /api/v1/metrics
```

Метрики системы для мониторинга (только admin).

---

## 6\. Эндпоинты -- база знаний

```
GET /api/v1/knowledge/search
```

Поиск по базе знаний (через RAGflow).

**Параметры:** `q` (текст запроса), `industry`, `deal_stage`, `product_type`

**Ответ:**

```
{  "success": true,  "data": {    "answer": "По данному типу клиента рекомендуется...",    "sources": [      {        "id": "kb_case_001",        "title": "Кейс: ЦОД-проект, застройщик",        "excerpt": "...",        "relevance": 0.92      }    ]  }}
```

```
POST /api/v1/knowledge/documents
```

Загрузка нового документа в базу знаний (только role: knowledge_manager, admin).

---

## 7\. Коды ошибок

| Код                   | HTTP | Описание                             |
|-----------------------|------|--------------------------------------|
| `UNAUTHORIZED`        | 401  | Токен отсутствует или недействителен |
| `FORBIDDEN`           | 403  | Недостаточно прав для операции       |
| `NOT_FOUND`           | 404  | Ресурс не найден                     |
| `VALIDATION_ERROR`    | 422  | Ошибка валидации параметров          |
| `RATE_LIMITED`        | 429  | Превышен лимит запросов              |
| `INTERNAL_ERROR`      | 500  | Внутренняя ошибка сервера            |
| `SERVICE_UNAVAILABLE` | 503  | Компонент системы недоступен         |

---

## 8\. Лимиты

| Параметр                         | Значение                       |
|----------------------------------|--------------------------------|
| Rate limit                       | 100 запросов / минуту на токен |
| Максимум записей в ответе        | 100 (`per_page`)               |
| Максимальный размер тела запроса | 10 МБ                          |
| Таймаут                          | 30 секунд                      |