Skip to content

Руководство по API - Интеграция Sync

Полное руководство по интеграции с REST API Sync для передачи контактов в AI.

Содержание


Аутентификация

Все запросы к API требуют аутентификации с использованием Bearer-токена в заголовке Authorization.

Authorization: Bearer ВАШ_API_КЛЮЧ

Получение 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 для предотвращения дублирования обработки.

Как это работает

  1. Первый запрос: Сервер обрабатывает запрос и сохраняет ответ, связанный с ключом идемпотентности.
  2. Повтор (тот же payload): Сервер возвращает сохранённый ответ без повторной обработки.
  3. Конфликт (другой 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 клиента, вы должны включать заголовки подписи.

Генерация подписи

  1. Сформировать сообщение: timestamp + "\n" + request_body
  2. Вычислить HMAC-SHA256: HMAC(secret, message)
  3. Закодировать как 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):

{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2024-01-15T12:00:00Z",
  "checks": {}
}

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": "error_code",
  "message": "Человекочитаемое описание ошибки",
  "details": {...}
}

Частые ошибки

Отсутствует ключ идемпотентности:

{
  "error": "validation_error",
  "message": "Требуется заголовок Idempotency-Key"
}

Неверный номер телефона:

{
  "error": "validation_error",
  "message": "Ошибка валидации запроса",
  "details": [...]
}

Конфликт идемпотентности:

{
  "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 для трассировки - Изучите сообщения об ошибках и детали - Свяжитесь с вашей командой поддержки интеграции