Skip to content

API Guide - Sync Integration

Complete guide for integrating with Sync REST API for contact transfer to AI.

Table of Contents


Authentication

All API requests require authentication using Bearer token in the Authorization header.

Authorization: Bearer YOUR_API_KEY

Obtaining API Keys

Contact your administrator to obtain an API key. Keep your API key secure and never commit it to version control.


Headers

Required Headers

Header Description Example
Authorization Bearer token for authentication Bearer abc123...
Idempotency-Key Unique identifier for request deduplication UUID v4
Content-Type Must be application/json application/json

Optional Headers

Header Description Example
X-Correlation-ID Request tracing identifier (auto-generated if not provided) UUID v4
X-Debug Enable dry-run mode (no data sent to AI) true
X-Signature HMAC signature (if enabled) Hex string
X-Timestamp Request timestamp for HMAC (RFC3339) 2024-01-15T12:00:00Z

Idempotency

All POST /v1/contacts/import requests must include an Idempotency-Key header to prevent duplicate processing.

How It Works

  1. First Request: Server processes the request and stores the response associated with the idempotency key.
  2. Replay (Same Payload): Server returns the stored response without reprocessing.
  3. Conflict (Different Payload): Server returns 422 Unprocessable Entity error.

Best Practices

  • Use UUID v4 for idempotency keys
  • Store keys on your side to enable retries
  • Keys are valid for 72 hours (configurable)

Example

curl -X POST https://import-api.liddex.ru/v1/contacts/import \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '[{"phone": "+79001234567", "tags": ["test"], "additionalFields": {}}]'

HMAC Signature (Optional)

If HMAC validation is enabled for your API client, you must include signature headers.

Generating Signature

  1. Construct message: timestamp + "\n" + request_body
  2. Compute HMAC-SHA256: HMAC(secret, message)
  3. Encode as hex string

Python Example

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

# Usage
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 Example

const crypto = require('crypto');

function generateSignature(secret, timestamp, body) {
    const message = timestamp + '\n' + body;
    return crypto
        .createHmac('sha256', secret)
        .update(message)
        .digest('hex');
}

// Usage
const timestamp = new Date().toISOString();
const body = JSON.stringify([{phone: "+79001234567", tags: [], additionalFields: {}}]);
const signature = generateSignature('your-secret', timestamp, body);

Endpoints

POST /v1/contacts/import

Import contacts for transfer to AI.

Request Body: Single object or array of contact objects.

{
  "phone": "79001234567",
  "tags": ["лид", "сайт"],
  "additionalFields": {
    "source": "website",
    "campaign": "summer2024"
  }
}

Or array:

[
  {
    "phone": "+79001234567",
    "tags": ["лид"],
    "additionalFields": {"source": "website"}
  },
  {
    "phone": "89001234568",
    "tags": ["клиент"],
    "additionalFields": {}
  }
]

Response (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"
}

Debug Mode Response (200 OK when 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": "Dry-run mode: no data sent to AI"
}

GET /v1/contacts/import/{job_id}

Get status of an import job.

Response (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}

Retrieve stored result for an idempotency key.

Response (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

Basic health check (no dependencies).

Response (200 OK):

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

GET /readyz

Readiness check (includes database connectivity).

Response (200 OK):

{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2024-01-15T12:00:00Z",
  "checks": {
    "database": {
      "status": "healthy",
      "message": "Database connection OK"
    }
  }
}

Error Handling

HTTP Status Codes

Code Meaning Action
200 OK Success (debug mode, status queries)
202 Accepted Job accepted for processing
400 Bad Request Fix validation errors
401 Unauthorized Check API key
403 Forbidden Check permissions/IP allowlist
404 Not Found Check job ID or idempotency key
422 Unprocessable Entity Idempotency conflict - use different key
429 Too Many Requests Slow down, retry with backoff
500 Internal Server Error Contact support

Error Response Format

{
  "error": "error_code",
  "message": "Human-readable error description",
  "details": {...}
}

Common Errors

Missing Idempotency Key:

{
  "error": "validation_error",
  "message": "Idempotency-Key header is required"
}

Invalid Phone Number:

{
  "error": "validation_error",
  "message": "Request validation failed",
  "details": [...]
}

Idempotency Conflict:

{
  "error": "idempotency_conflict",
  "message": "Idempotency key reused with different payload",
  "existing_created_at": "2024-01-15T12:00:00Z"
}


Rate Limiting

  • Rate limits are configurable per API client
  • Default: 10 requests per second
  • Exceeded limits return 429 Too Many Requests
  • Implement exponential backoff for retries

Examples

cURL Example

#!/bin/bash

API_KEY="your-api-key-here"
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 Example

import httpx
import uuid

API_KEY = "your-api-key-here"
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()

# Usage
contacts = [
    {
        "phone": "+79001234567",
        "tags": ["лид"],
        "additionalFields": {"source": "api"}
    }
]

result = await import_contacts(contacts)
print(f"Job ID: {result['job_id']}")

JavaScript/Node.js Example

const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

const API_KEY = 'your-api-key-here';
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;
}

// Usage
const contacts = [
    {
        phone: '+79001234567',
        tags: ['лид'],
        additionalFields: {source: 'api'}
    }
];

importContacts(contacts)
    .then(result => console.log('Job ID:', result.job_id))
    .catch(error => console.error('Error:', error.message));

Support

For questions or issues: - Check logs using correlation_id for tracing - Review error messages and details - Contact your integration support team