Skip to content

Примеры использования

Практические примеры интеграции с API Sync.

Содержание


Примеры на Python

Базовый импорт

import httpx
import uuid
import asyncio

API_KEY = "ваш-api-ключ"
BASE_URL = "https://import-api.liddex.ru"

async def import_contacts(contacts: list[dict]) -> dict:
    """Импорт контактов в AI."""
    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,
            timeout=30.0,
        )
        response.raise_for_status()
        return response.json()

# Использование
contacts = [
    {
        "phone": "+79001234567",
        "tags": ["лид", "сайт"],
        "additionalFields": {
            "source": "landing",
            "campaign": "summer2024",
        }
    },
    {
        "phone": "89001234568",
        "tags": ["клиент"],
        "additionalFields": {
            "source": "referral",
        }
    }
]

result = asyncio.run(import_contacts(contacts))
print(f"ID задачи: {result['job_id']}")
print(f"Валидных контактов: {result['valid_contacts']}")

Опрос статуса задачи

async def wait_for_job_completion(
    job_id: str,
    max_wait: int = 300,
    poll_interval: int = 5
) -> dict:
    """Опрос статуса задачи до завершения или таймаута."""
    headers = {"Authorization": f"Bearer {API_KEY}"}
    elapsed = 0

    async with httpx.AsyncClient() as client:
        while elapsed < max_wait:
            response = await client.get(
                f"{BASE_URL}/v1/contacts/import/{job_id}",
                headers=headers,
            )
            response.raise_for_status()
            status = response.json()

            if status["status"] in ["completed", "failed"]:
                return status

            await asyncio.sleep(poll_interval)
            elapsed += poll_interval

    raise TimeoutError(f"Задача {job_id} не завершилась за {max_wait}с")

# Использование
result = asyncio.run(import_contacts(contacts))
final_status = asyncio.run(wait_for_job_completion(result["job_id"]))
print(f"Статус: {final_status['status']}")
print(f"Отправлено: {final_status['sent_contacts']}/{final_status['valid_contacts']}")

С HMAC подписью

import hmac
import hashlib
from datetime import datetime, timezone
import json

def generate_hmac_signature(secret: str, timestamp: str, body: bytes) -> str:
    """Генерация HMAC-SHA256 подписи."""
    message = f"{timestamp}\n".encode() + body
    return hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()

async def import_with_hmac(contacts: list[dict], hmac_secret: str) -> dict:
    """Импорт контактов с HMAC подписью."""
    timestamp = datetime.now(timezone.utc).isoformat()
    body = json.dumps(contacts).encode()
    signature = generate_hmac_signature(hmac_secret, timestamp, body)

    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Idempotency-Key": str(uuid.uuid4()),
        "X-Signature": signature,
        "X-Timestamp": timestamp,
        "Content-Type": "application/json",
    }

    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{BASE_URL}/v1/contacts/import",
            content=body,
            headers=headers,
        )
        response.raise_for_status()
        return response.json()

Режим отладки (Dry-Run)

async def validate_contacts(contacts: list[dict]) -> dict:
    """Валидация контактов без отправки в AI."""
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Idempotency-Key": str(uuid.uuid4()),
        "X-Debug": "true",
        "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()

# Использование
result = asyncio.run(validate_contacts([
    {"phone": "+79001234567", "tags": [], "additionalFields": {}},
    {"phone": "invalid", "tags": [], "additionalFields": {}},
]))

print(f"Валидных: {result['valid_contacts']}")
print(f"Невалидных: {result['invalid_contacts']}")

for contact in result['validation_results']:
    if not contact['is_valid']:
        print(f"Ошибка: {contact['original_phone']} - {contact['error']}")

Примеры на 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',
            },
            timeout: 30000,
        }
    );
    return response.data;
}

// Использование
const contacts = [
    {
        phone: '+79001234567',
        tags: ['лид', 'сайт'],
        additionalFields: {
            source: 'landing',
            campaign: 'summer2024',
        }
    }
];

importContacts(contacts)
    .then(result => {
        console.log('ID задачи:', result.job_id);
        console.log('Валидных контактов:', result.valid_contacts);
    })
    .catch(error => {
        console.error('Ошибка:', error.response?.data || error.message);
    });

С логикой повторов

const axios = require('axios');
const axiosRetry = require('axios-retry');

const client = axios.create({
    baseURL: BASE_URL,
    timeout: 30000,
});

// Настройка повторов
axiosRetry(client, {
    retries: 3,
    retryDelay: axiosRetry.exponentialDelay,
    retryCondition: (error) => {
        // Повтор при сетевых ошибках или 5xx
        return axiosRetry.isNetworkOrIdempotentRequestError(error) ||
               (error.response && error.response.status >= 500);
    },
});

async function importContactsWithRetry(contacts) {
    const response = await client.post('/v1/contacts/import', contacts, {
        headers: {
            'Authorization': `Bearer ${API_KEY}`,
            'Idempotency-Key': uuidv4(),
            'Content-Type': 'application/json',
        },
    });
    return response.data;
}

Пример на TypeScript

import axios, { AxiosInstance } from 'axios';
import { v4 as uuidv4 } from 'uuid';

interface Contact {
    phone: string;
    tags: string[];
    additionalFields: Record<string, any>;
}

interface ImportResponse {
    job_id: string;
    correlation_id: string;
    idempotency_status: string;
    status: string;
    total_contacts: number;
    valid_contacts: number;
    invalid_contacts: number;
    chunks_count: number;
    created_at: string;
}

class SyncClient {
    private client: AxiosInstance;

    constructor(apiKey: string, baseURL: string = 'https://import-api.liddex.ru') {
        this.client = axios.create({
            baseURL,
            headers: {
                'Authorization': `Bearer ${apiKey}`,
                'Content-Type': 'application/json',
            },
            timeout: 30000,
        });
    }

    async importContacts(contacts: Contact[]): Promise<ImportResponse> {
        const response = await this.client.post<ImportResponse>(
            '/v1/contacts/import',
            contacts,
            {
                headers: {
                    'Idempotency-Key': uuidv4(),
                },
            }
        );
        return response.data;
    }

    async getJobStatus(jobId: string): Promise<any> {
        const response = await this.client.get(`/v1/contacts/import/${jobId}`);
        return response.data;
    }
}

// Использование
const client = new SyncClient('ваш-api-ключ');

const contacts: Contact[] = [
    {
        phone: '+79001234567',
        tags: ['тест'],
        additionalFields: { source: 'typescript' },
    }
];

client.importContacts(contacts)
    .then(result => console.log('ID задачи:', result.job_id))
    .catch(error => console.error('Ошибка:', error.message));

Примеры с 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": "summer2024"
      }
    }
  ]'

Сохранение ID задачи и проверка статуса

#!/bin/bash

# Импорт и извлечение ID задачи
RESPONSE=$(curl -s -X POST "${BASE_URL}/v1/contacts/import" \
  -H "Authorization: Bearer ${API_KEY}" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '[{"phone": "+79001234567", "tags": [], "additionalFields": {}}]')

JOB_ID=$(echo $RESPONSE | jq -r '.job_id')
echo "ID задачи: $JOB_ID"

# Ожидание и проверка статуса
sleep 5

curl -s -X GET "${BASE_URL}/v1/contacts/import/${JOB_ID}" \
  -H "Authorization: Bearer ${API_KEY}" | jq

Обработка ошибок

Python

from httpx import HTTPStatusError

async def import_with_error_handling(contacts: list[dict]) -> dict:
    try:
        return await import_contacts(contacts)
    except HTTPStatusError as e:
        if e.response.status_code == 400:
            print("Ошибка валидации:", e.response.json())
        elif e.response.status_code == 401:
            print("Ошибка аутентификации - проверьте API ключ")
        elif e.response.status_code == 422:
            print("Конфликт идемпотентности - ключ использован с другими данными")
        elif e.response.status_code == 429:
            print("Превышен лимит запросов - повторите позже")
        else:
            print(f"Неожиданная ошибка: {e.response.status_code}")
        raise

JavaScript

async function importWithErrorHandling(contacts) {
    try {
        return await importContacts(contacts);
    } catch (error) {
        if (error.response) {
            const status = error.response.status;
            const data = error.response.data;

            switch (status) {
                case 400:
                    console.error('Ошибка валидации:', data);
                    break;
                case 401:
                    console.error('Ошибка аутентификации - проверьте API ключ');
                    break;
                case 422:
                    console.error('Конфликт идемпотентности:', data);
                    break;
                case 429:
                    console.error('Превышен лимит запросов - повторите позже');
                    break;
                default:
                    console.error(`Неожиданная ошибка: ${status}`, data);
            }
        } else {
            console.error('Сетевая ошибка:', error.message);
        }
        throw error;
    }
}

Расширенное использование

Пакетная обработка

async def import_in_batches(
    all_contacts: list[dict],
    batch_size: int = 1000,
) -> list[str]:
    """Импорт контактов пакетами."""
    job_ids = []

    for i in range(0, len(all_contacts), batch_size):
        batch = all_contacts[i:i + batch_size]
        result = await import_contacts(batch)
        job_ids.append(result['job_id'])

        print(f"Пакет {i // batch_size + 1}: Задача {result['job_id']}")

        # Ограничение частоты
        await asyncio.sleep(0.5)

    return job_ids

Мониторинг прогресса

async def monitor_jobs(job_ids: list[str]) -> dict:
    """Мониторинг нескольких задач."""
    results = {"completed": 0, "failed": 0, "pending": 0}

    async with httpx.AsyncClient() as client:
        for job_id in job_ids:
            response = await client.get(
                f"{BASE_URL}/v1/contacts/import/{job_id}",
                headers={"Authorization": f"Bearer {API_KEY}"},
            )
            status = response.json()

            if status["status"] == "completed":
                results["completed"] += 1
            elif status["status"] == "failed":
                results["failed"] += 1
            else:
                results["pending"] += 1

    return results

Смотрите также