Integration API v1
Base URL
https://api.retailsolution.ai
Локально: http://localhost:8009
Prefix
/integration/v1
Swagger
/docs → tag integration-v1
Правила авторизации
Два типа токена. Scope по умолчанию: analytics, catalog. visitors добавляется отдельно при создании ключа.
| Токен | Как получить | Где используется |
|---|---|---|
| Интеграционный JWT | POST /integration/v1/token | /me, каталог, аналитика, посетители/пользователи |
API-ключ cv_live_… / cv_test_… | POST /integration/v1/api-keys | Так же, как JWT (Bearer) |
Dashboard JWT (POST /token) | Логин сотрудника | Только CRUD API-ключей |
Staff JWT на аналитике/каталоге → 401 Staff tokens are not accepted on the integration API
Доступные API
| # | Method | Endpoint | Кто | Scope |
|---|---|---|---|---|
| 1 | POST | /integration/v1/token | публичный | — |
| 2 | GET | /integration/v1/me | партнёр | any |
| 3 | POST | /integration/v1/api-keys | админ компании | — |
| 4 | GET | /integration/v1/api-keys | админ компании | — |
| 5 | DELETE | /integration/v1/api-keys/{key_id} | админ компании | — |
| 6 | GET | /integration/v1/branches | партнёр | catalog |
| 7 | GET | /integration/v1/buildings | партнёр | catalog |
| 8 | GET | /integration/v1/cameras | партнёр | catalog |
| 9 | GET | /integration/v1/analytics/branch-summary | партнёр | analytics |
| 10 | GET | /integration/v1/analytics/branch-summary/export.xlsx | партнёр | analytics |
| 11 | GET | /integration/v1/analytics/hourly | партнёр | analytics |
| 12 | GET | /integration/v1/analytics/traffic | партнёр | analytics |
| 13 | GET | /integration/v1/visitors | партнёр | visitors |
| 14 | GET | /integration/v1/users | партнёр | visitors |
Нет: создание интеграционного пользователя / сброс пароля API.
POST/integration/v1/token
Пароль → JWT (1 час). Только пользователь is_integration_user=true. Не JSON body — form-urlencoded.
1POST /integration/v1/token2Content-Type: application/x-www-form-urlencoded34username=yuksalish_api&password=SECRET
Ответ 200
1{2"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",3"token_type": "bearer",4"expires_in": 36005}
| Status | Body |
|---|---|
| 401 | { "detail": "Invalid credentials" } |
| 403 | { "detail": "Not an integration account" } |
| 400 | Inactive user или нет компании |
| 429 | retry_after + заголовок Retry-After |
GET/integration/v1/me
Текущий principal и компания. JWT или API-ключ.
1GET /integration/v1/me2Authorization: Bearer eyJ...
Ответ 200 (JWT)
1{2"user_id": 88,3"username": "yuksalish_api",4"email": "api@yuksalish.uz",5"first_name": "API",6"last_name": "User",7"company": {8"id": 3,9"name": "Yuksalish",10"description": "Retail",11"type": null,12"phone": "+99890...",13"email": "info@yuksalish.uz",14"logo": "https://s3.example/presigned..."15}16}
Ответ 200 (API-ключ)
user_id равен 0, username равен api_key:{id}.
1{2"user_id": 0,3"username": "api_key:12",4"email": null,5"first_name": null,6"last_name": null,7"company": { "id": 3, "name": "Yuksalish" }8}
| Status | Причина |
|---|---|
| 401 | Нет токена / неверный / staff JWT |
| 404 | Компания не найдена |
POST/integration/v1/api-keys
Создание ключа. Dashboard JWT. is_admin или владелец компании (или superuser + company_id). api_key приходит только в этом ответе — показать один раз в UI и скопировать.
1POST /integration/v1/api-keys2Authorization: Bearer <dashboard_jwt>3Content-Type: application/json
1{2"name": "Yuksalish ERP",3"scopes": ["analytics", "catalog"],4"environment": "live"5}
| Поле | Обязательно | По умолчанию |
|---|---|---|
name | да | — |
scopes | нет | Разрешено: analytics, catalog, visitors |
environment | нет | live → cv_live_, test → cv_test_ |
1{2"id": 12,3"name": "Yuksalish ERP",4"key_prefix": "cv_live_a1b2",5"scopes": ["analytics", "catalog"],6"is_active": true,7"created_at": "2026-08-19T10:15:00",8"last_used_at": null,9"api_key": "cv_live_a1b2c3d4e5f6...."10}
GET/integration/v1/api-keys
Полный secret не возвращается. is_active: false = отозван.
1GET /integration/v1/api-keys2Authorization: Bearer <dashboard_jwt>
1[2{3"id": 12,4"name": "Yuksalish ERP",5"key_prefix": "cv_live_a1b2",6"scopes": ["analytics", "catalog"],7"is_active": true,8"created_at": "2026-08-19T10:15:00",9"last_used_at": "2026-08-19T12:00:00"10}11]
DELETE/integration/v1/api-keys/{key_id}
1DELETE /integration/v1/api-keys/122Authorization: Bearer <dashboard_jwt>
1{2"detail": "API key not found"3}
Ответ 204 — без body. 404: { "detail": "API key not found" }
1{2"detail": "API key not found"3}
GET/integration/v1/branches
Нет scope → 403 API key or token is missing scope: catalog.
1GET /integration/v1/branches2Authorization: Bearer <integration_jwt | cv_live_>
1[2{3"id": 4,4"branch_title": "Chilonzor",5"building_id": 10,6"timezone": "Asia/Tashkent",7"company_id": 38}9]
GET/integration/v1/buildings
1GET /integration/v1/buildings2Authorization: Bearer <...>
1[2{3"id": 10,4"name": "Chilonzor store",5"address": "Toshkent, Chilonzor",6"latitude": 41.28,7"longitude": 69.21,8"zip_code": "100115"9}10]
GET/integration/v1/cameras
Query building_id необязателен. Не возвращается: password, login, ddns_rtsp_url, embedding.
1GET /integration/v1/cameras2GET /integration/v1/cameras?building_id=10
1[2{3"id": 101,4"name": "Kirish",5"device_id": "cam-001",6"device_ip": "192.168.1.***",7"building_id": 10,8"room_id": 3,9"camera_type": "entry",10"is_traffic_camera": false,11"calibration_active": true12}13]
GET/integration/v1/analytics/branch-summary
Дневной отчёт филиала из Excel (JSON). conversion_rate = visit_count / traffic_count (4 знака). Трафик 0 → null.
| Query | Обязательно | Комментарий |
|---|---|---|
start_date | да | YYYY-MM-DD, inclusive |
end_date | да | inclusive, максимум 31 дней |
branch_id | нет | Один филиал |
include_empty_branches | нет | По умолчанию false |
1GET /integration/v1/analytics/branch-summary?start_date=2026-08-17&end_date=2026-08-172GET /integration/v1/analytics/branch-summary?start_date=2026-08-01&end_date=2026-08-17&branch_id=4&include_empty_branches=false
1{2"start_date": "2026-08-17",3"end_date": "2026-08-17",4"branches": [5{6"branch_id": 4,7"branch_name": "Chilonzor",8"traffic_count": 1200,9"conversion_rate": 0.35,10"visit_count": 420,11"visit_share_pct": 58.33,12"gender": { "male_count": 200, "male_pct": 47.62, "female_count": 220, "female_pct": 52.38 },13"age": { "under_18": 10, "age_18_30": 180, "age_31_40": 140, "age_41_54": 70, "age_55_plus": 20 },14"peak_hour": { "label": "20:00-21:00", "count": 80 },15"low_hour": { "label": "08:00-09:00", "count": 5 }16}17],18"totals": { "branch_name": "Total", "traffic_count": 1200, "visit_count": 420 }19}
| Status | detail |
|---|---|
| 400 | start_date должен быть <= end_date / диапазон не больше 31 дня |
| 404 | Филиал не найден |
| 403 | нет scope analytics |
| 429 | лимит 30/час |
GET/integration/v1/analytics/branch-summary/export.xlsx
Тот же summary, файл Excel. Лимит: 5/hour. Frontend: responseType: 'blob', затем скачивание. lang = uz | en | ru (по умолчанию uz).
1GET /integration/v1/analytics/branch-summary/export.xlsx?start_date=2026-08-17&end_date=2026-08-17&lang=uz2Authorization: Bearer <...>
Content-Type: spreadsheetml.sheet. Body: binary XLSX, не JSON.
GET/integration/v1/analytics/hourly
Один филиал, один день, 24 часа. Имя query — date. Оба обязательны. В hours всегда 24 элемента.
1GET /integration/v1/analytics/hourly?branch_id=4&date=2026-08-17
1{2"branch_id": 4,3"branch_name": "Chilonzor",4"date": "2026-08-17",5"timezone": "Asia/Tashkent",6"hours": [7{ "label": "00:00-01:00", "count": 0 },8{ "label": "20:00-21:00", "count": 80 },9{ "label": "23:00-00:00", "count": 4 }10]11}
GET/integration/v1/analytics/traffic
Только уличный поток (traffic_count). Даты обязательны, максимум 31 день, branch_id необязателен.
1GET /integration/v1/analytics/traffic?start_date=2026-08-17&end_date=2026-08-172GET /integration/v1/analytics/traffic?start_date=2026-08-17&end_date=2026-08-17&branch_id=4
1{2"start_date": "2026-08-17",3"end_date": "2026-08-17",4"branches": [5{ "branch_id": 4, "branch_name": "Chilonzor", "traffic_count": 1200 },6{ "branch_id": 5, "branch_name": "Yunusobod", "traffic_count": 800 }7],8"total_traffic_count": 20009}
GET/integration/v1/visitors
Сырые посетители. Scope visitors. Максимум 7 дней, максимум 500 строк. Вектора embedding нет.
| Query | Обязательно | Комментарий |
|---|---|---|
start_date / end_date | да | максимум 7 дней |
gender | нет | male | female |
camera_id / building_id | нет | |
limit | нет | 1–500, по умолчанию 100 |
1GET /integration/v1/visitors?start_date=2026-08-17&end_date=2026-08-17&limit=1002GET /integration/v1/visitors?start_date=2026-08-17&end_date=2026-08-17&gender=female&building_id=10&camera_id=101&limit=50
1[2{3"id": 45,4"face_id": 789,5"age": 22,6"gender": "female",7"building_id": 10,8"camera_id": 101,9"visit_status": "visitor",10"exit_time": null,11"face_embeddings": [12{13"id": 123,14"camera_id": 101,15"event_time": "2026-08-17T10:30:00",16"crop_url": "https://s3.../presigned",17"visit_status": "visitor"18}19]20}21]
GET/integration/v1/users
Сотрудники. Scope visitors. Нет пароля/hash и face-векторов.
1GET /integration/v1/users?limit=1002GET /integration/v1/users?building_id=10&department_id=2&branch_id=4&gender=male&search_str=ali&limit=50
1[2{3"id": 7,4"username": "ali.karimov",5"email": "ali@company.com",6"first_name": "Ali",7"last_name": "Karimov",8"gender": "male",9"phone": "+998901234567",10"position": "Manager",11"building_id": 10,12"department_id": 2,13"branch_id": 4,14"is_active": true,15"face_photos": [{ "id": 55, "image_url": "https://s3.../presigned" }]16}17]
Общие ошибки и лимиты
Ответ 401
1{2"detail": "Not authenticated"3}
Ответ 401
1{2"detail": "Could not validate credentials"3}
Ответ 401
1{2"detail": "Invalid API key"3}
Ответ 401
1{2"detail": "Staff tokens are not accepted on the integration API"3}
Ответ 403
1{2"detail": "API key or token is missing scope: analytics"3}
Ответ 429
1{2"detail": "Too many requests. Please try again later.",3"retry_after": 36004}
| Группа эндпоинтов | Лимит |
|---|---|
| POST token | 10 / минуту |
| /me, /branches, /buildings, /cameras | 60 / час |
| branch-summary JSON, hourly, traffic | 30 / час |
| export.xlsx | 5 / час |
| /visitors | 10 / минуту |
Сценарий клиента
1TOKEN=$(curl -s -X POST 'https://api.retailsolution.ai/integration/v1/token' \2-H 'Content-Type: application/x-www-form-urlencoded' \3-d 'username=yuksalish_api&password=SECRET' | jq -r .access_token)
1curl -s -H "Authorization: Bearer $TOKEN" \2'https://api.retailsolution.ai/integration/v1/me'
1curl -s -H "Authorization: Bearer $TOKEN" \2'https://api.retailsolution.ai/integration/v1/branches'
1curl -s -H "Authorization: Bearer $TOKEN" \2'https://api.retailsolution.ai/integration/v1/analytics/branch-summary?start_date=2026-08-17&end_date=2026-08-17'
1curl -s -H "Authorization: Bearer cv_live_xxxx" \2'https://api.retailsolution.ai/integration/v1/analytics/branch-summary?start_date=2026-08-17&end_date=2026-08-17'