Руководство по API - Интеграция Sync¶
Полное руководство по интеграции с REST API Sync для передачи контактов в AI.
Содержание¶
- Аутентификация
- Заголовки
- Идемпотентность
- HMAC подпись (опционально)
- Эндпоинты
- Обработка ошибок
- Ограничение частоты запросов
- Примеры
Аутентификация¶
Все запросы к API требуют аутентификации с использованием Bearer-токена в заголовке Authorization.
Получение API ключей¶
Свяжитесь с вашим администратором для получения API ключа. Храните API ключ в безопасности и никогда не коммитьте его в систему контроля версий.
Заголовки¶
Обязательные заголовки¶
| Заголовок | Описание | Пример |
|---|---|---|
Authorization |
Bearer токен для аутентификации | Bearer abc123... |
Idempotency-Key |
Уникальный идентификатор для дедупликации запросов | UUID v4 |
Content-Type |
Должен быть application/json |
application/json |
Опциональные заголовки¶
| Заголовок | Описание | Пример |
|---|---|---|
X-Correlation-ID |
Идентификатор для трассировки запроса (генерируется автоматически, если не указан) | UUID v4 |
X-Debug |
Включить режим тестирования (данные не отправляются в AI) | true |
X-Signature |
HMAC подпись (если включена) | Hex строка |
X-Timestamp |
Временная метка запроса для HMAC (RFC3339) | 2024-01-15T12:00:00Z |
Идемпотентность¶
Все запросы POST /v1/contacts/import должны включать заголовок Idempotency-Key для предотвращения дублирования обработки.
Как это работает¶
- Первый запрос: Сервер обрабатывает запрос и сохраняет ответ, связанный с ключом идемпотентности.
- Повтор (тот же payload): Сервер возвращает сохранённый ответ без повторной обработки.
- Конфликт (другой payload): Сервер возвращает ошибку
422 Unprocessable Entity.
Лучшие практики¶
- Используйте UUID v4 для ключей идемпотентности
- Храните ключи у себя для возможности повторных попыток
- Ключи действительны в течение 72 часов (настраивается)
Пример¶
curl -X POST https://import-api.liddex.ru/v1/contacts/import \
-H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '[{"phone": "+79001234567", "tags": ["тест"], "additionalFields": {}}]'
HMAC подпись (опционально)¶
Если HMAC валидация включена для вашего API клиента, вы должны включать заголовки подписи.
Генерация подписи¶
- Сформировать сообщение:
timestamp + "\n" + request_body - Вычислить HMAC-SHA256:
HMAC(secret, message) - Закодировать как hex строку
Пример на Python¶
import hmac
import hashlib
from datetime import datetime, timezone
def generate_signature(secret: str, timestamp: str, body: bytes) -> str:
message = f"{timestamp}\n".encode() + body
signature = hmac.new(
secret.encode(),
message,
hashlib.sha256
).hexdigest()
return signature
# Использование
timestamp = datetime.now(timezone.utc).isoformat()
body = b'[{"phone": "+79001234567", "tags": [], "additionalFields": {}}]'
signature = generate_signature("your-secret", timestamp, body)
headers = {
"X-Signature": signature,
"X-Timestamp": timestamp,
}
Пример на JavaScript¶
const crypto = require('crypto');
function generateSignature(secret, timestamp, body) {
const message = timestamp + '\n' + body;
return crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
}
// Использование
const timestamp = new Date().toISOString();
const body = JSON.stringify([{phone: "+79001234567", tags: [], additionalFields: {}}]);
const signature = generateSignature('your-secret', timestamp, body);
Эндпоинты¶
POST /v1/contacts/import¶
Импорт контактов для передачи в AI.
Тело запроса: Один объект или массив объектов контактов.
{
"phone": "79001234567",
"tags": ["лид", "сайт"],
"additionalFields": {
"source": "website",
"campaign": "summer2024"
}
}
Или массив:
[
{
"phone": "+79001234567",
"tags": ["лид"],
"additionalFields": {"source": "website"}
},
{
"phone": "89001234568",
"tags": ["клиент"],
"additionalFields": {}
}
]
Ответ (202 Accepted):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"correlation_id": "660e8400-e29b-41d4-a716-446655440001",
"idempotency_status": "created",
"status": "pending",
"total_contacts": 2,
"valid_contacts": 2,
"invalid_contacts": 0,
"chunks_count": 1,
"created_at": "2024-01-15T12:00:00Z"
}
Ответ в режиме отладки (200 OK когда X-Debug: true):
{
"correlation_id": "...",
"total_contacts": 2,
"valid_contacts": 1,
"invalid_contacts": 1,
"chunks_count": 1,
"validation_results": [
{
"original_phone": "+79001234567",
"normalized_phone": "+79001234567",
"is_valid": true,
"error": null,
"tags": ["лид"],
"additional_fields": {}
},
{
"original_phone": "invalid",
"normalized_phone": null,
"is_valid": false,
"error": "Invalid phone number",
"tags": [],
"additional_fields": {}
}
],
"chunks": [
{
"chunk_index": 0,
"size": 1,
"contacts": [...]
}
],
"message": "Режим тестирования: данные не отправлены в AI"
}
GET /v1/contacts/import/{job_id}¶
Получить статус задачи импорта.
Ответ (200 OK):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"correlation_id": "660e8400-e29b-41d4-a716-446655440001",
"status": "completed",
"total_contacts": 100,
"valid_contacts": 95,
"invalid_contacts": 5,
"sent_contacts": 95,
"failed_contacts": 0,
"chunks_total": 1,
"chunks_completed": 1,
"chunks_failed": 0,
"chunks_pending": 0,
"chunks": [
{
"chunk_index": 0,
"status": "sent",
"size": 95,
"attempt_count": 1,
"last_attempt_at": "2024-01-15T12:01:00Z",
"sent_at": "2024-01-15T12:01:00Z",
"response_status_code": 200,
"error_message": null
}
],
"created_at": "2024-01-15T12:00:00Z",
"updated_at": "2024-01-15T12:01:00Z",
"completed_at": "2024-01-15T12:01:00Z",
"error_message": null
}
GET /v1/idempotency/{key}¶
Получить сохранённый результат по ключу идемпотентности.
Ответ (200 OK):
{
"key": "550e8400-e29b-41d4-a716-446655440000",
"status": "active",
"payload_hash": "abc123...",
"job_id": "660e8400-e29b-41d4-a716-446655440001",
"created_at": "2024-01-15T12:00:00Z",
"accessed_at": "2024-01-15T12:05:00Z",
"access_count": 3,
"expires_at": "2024-01-18T12:00:00Z",
"original_status_code": 202,
"original_response": {...}
}
GET /healthz¶
Базовая проверка здоровья (без зависимостей).
Ответ (200 OK):
GET /readyz¶
Проверка готовности (включает подключение к базе данных).
Ответ (200 OK):
{
"status": "healthy",
"version": "1.0.0",
"timestamp": "2024-01-15T12:00:00Z",
"checks": {
"database": {
"status": "healthy",
"message": "Database connection OK"
}
}
}
Обработка ошибок¶
HTTP коды статуса¶
| Код | Значение | Действие |
|---|---|---|
| 200 | OK | Успех (режим отладки, запросы статуса) |
| 202 | Accepted | Задача принята к обработке |
| 400 | Bad Request | Исправьте ошибки валидации |
| 401 | Unauthorized | Проверьте API ключ |
| 403 | Forbidden | Проверьте права доступа/IP белый список |
| 404 | Not Found | Проверьте ID задачи или ключ идемпотентности |
| 422 | Unprocessable Entity | Конфликт идемпотентности - используйте другой ключ |
| 429 | Too Many Requests | Снизьте частоту, повторите с отсрочкой |
| 500 | Internal Server Error | Свяжитесь с поддержкой |
Формат ошибки¶
Частые ошибки¶
Отсутствует ключ идемпотентности:
Неверный номер телефона:
Конфликт идемпотентности:
{
"error": "idempotency_conflict",
"message": "Ключ идемпотентности повторно использован с другим payload",
"existing_created_at": "2024-01-15T12:00:00Z"
}
Ограничение частоты запросов¶
- Лимиты настраиваются для каждого API клиента
- По умолчанию: 10 запросов в секунду
- При превышении лимитов возвращается
429 Too Many Requests - Используйте экспоненциальную отсрочку для повторов
Примеры¶
Пример с cURL¶
#!/bin/bash
API_KEY="ваш-api-ключ"
BASE_URL="https://import-api.liddex.ru"
IDEMPOTENCY_KEY=$(uuidgen)
curl -X POST "${BASE_URL}/v1/contacts/import" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
-H "Content-Type: application/json" \
-d '[
{
"phone": "+79001234567",
"tags": ["лид", "сайт"],
"additionalFields": {
"source": "landing",
"campaign": "winter2024"
}
}
]'
Пример на Python¶
import httpx
import uuid
API_KEY = "ваш-api-ключ"
BASE_URL = "https://import-api.liddex.ru"
async def import_contacts(contacts: list):
headers = {
"Authorization": f"Bearer {API_KEY}",
"Idempotency-Key": str(uuid.uuid4()),
"Content-Type": "application/json",
}
async with httpx.AsyncClient() as client:
response = await client.post(
f"{BASE_URL}/v1/contacts/import",
json=contacts,
headers=headers,
)
response.raise_for_status()
return response.json()
# Использование
contacts = [
{
"phone": "+79001234567",
"tags": ["лид"],
"additionalFields": {"source": "api"}
}
]
result = await import_contacts(contacts)
print(f"ID задачи: {result['job_id']}")
Пример на JavaScript/Node.js¶
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const API_KEY = 'ваш-api-ключ';
const BASE_URL = 'https://import-api.liddex.ru';
async function importContacts(contacts) {
const response = await axios.post(
`${BASE_URL}/v1/contacts/import`,
contacts,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Idempotency-Key': uuidv4(),
'Content-Type': 'application/json',
}
}
);
return response.data;
}
// Использование
const contacts = [
{
phone: '+79001234567',
tags: ['лид'],
additionalFields: {source: 'api'}
}
];
importContacts(contacts)
.then(result => console.log('ID задачи:', result.job_id))
.catch(error => console.error('Ошибка:', error.message));
Поддержка¶
По вопросам или проблемам:
- Проверьте логи, используя correlation_id для трассировки
- Изучите сообщения об ошибках и детали
- Свяжитесь с вашей командой поддержки интеграции