API Guide - Sync Integration¶
Complete guide for integrating with Sync REST API for contact transfer to AI.
Table of Contents¶
- Authentication
- Headers
- Idempotency
- HMAC Signature (Optional)
- Endpoints
- Error Handling
- Rate Limiting
- Examples
Authentication¶
All API requests require authentication using Bearer token in the Authorization header.
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¶
- First Request: Server processes the request and stores the response associated with the idempotency key.
- Replay (Same Payload): Server returns the stored response without reprocessing.
- Conflict (Different Payload): Server returns
422 Unprocessable Entityerror.
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¶
- Construct message:
timestamp + "\n" + request_body - Compute HMAC-SHA256:
HMAC(secret, message) - 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):
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¶
Common Errors¶
Missing Idempotency Key:
Invalid Phone Number:
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