---
title: REST API
description: Удобное для разработчиков руководство по TSync REST API — начните с аутентификации по токену, затем читайте и записывайте счета, клиентов, лиды, остатки, платежи и многое другое. Включает эндпоинты, форматы запросов/ответов и примеры кода.
product: TSync Intelligence 11.3.6
language: ru
canonical: https://docs.tsync.pro/ru/modules/tsync-rest-api/
source: https://docs.tsync.pro/llms.txt
---

# REST API

TSync REST API позволяет вашему собственному коду взаимодействовать с TSync. С его
помощью вы можете загружать данные в другую систему, создавать собственную панель
или мобильное приложение, синхронизировать две системы или подключать TSync к
инструментам автоматизации — всё это по обычному HTTPS с JSON.

Если вы уже работали с любым современным веб-API, это покажется знакомым: вы
получаете токен, отправляете его с каждым запросом и получаете чистый JSON в ответ.
Эта страница проведёт вас по основам, а затем перечислит каждый эндпоинт с примерами
запросов и ответов.

**Базовый URL:** `https://your-domain.com/api/v1`

Каждый путь ниже указан относительно этого базового. Так что `/invoices` на самом деле
означает `https://your-domain.com/api/v1/invoices`.

---

## Начало работы за три шага

1. **Создайте токен** в админ-панели (см. ниже) и скопируйте его в надёжное место.
2. **Отправьте его в запросе.** Самая быстрая проверка — список ваших счетов:

   ```bash
   curl -s -H "Authorization: Bearer YOUR_TOKEN" \
     "https://your-domain.com/api/v1/invoices?per_page=5"
   ```

3. **Прочитайте JSON.** Успешный ответ содержит `"status": true` и ваши данные в поле
   `data`. Вот и весь цикл — всё остальное это просто другие эндпоинты и параметры.

---

## Аутентификация

Каждый запрос должен нести Bearer-токен в заголовке `Authorization`.

### Генерация токена

1. Откройте **Setup → REST API tokens** (`/admin/tsync_api_tokens`). Можно также нажать
   **Ctrl/⌘+K** и найти «API».
2. Введите **метку** (например, «Mobile App», «Интеграция Zapier»), чтобы потом узнать его.
3. Выберите **сотрудника**, от имени которого действует токен. **Токен наследует права
   этого человека** — он может делать ровно то, что может он, не больше.
4. Нажмите **Создать токен**. Токен показывается **один раз** — скопируйте и сохраните его
   надёжно. Снова увидеть его не получится (хранится только хеш).

### Использование токена

Включайте его в каждый запрос:

```
Authorization: Bearer your-api-token-here
```

Поскольку токен наследует права своего сотрудника, доступ совпадает с админ-интерфейсом.
Если этот человек не может просматривать счета в TSync, то и токен не может обратиться к
`/invoices` — он получит `403`.

### Отзыв токена

Перейдите в **Setup → REST API tokens**, найдите токен в списке (метка, владелец, последнее
использование, срок, статус) и нажмите **Отозвать**. Он перестаёт работать немедленно.

---

## Ограничение частоты запросов (rate limiting)

- **100 запросов в минуту** на токен.
- Каждый ответ включает заголовки, чтобы вы могли регулировать темп:

| Заголовок | Значение |
|--------|---------|
| `X-RateLimit-Limit` | Максимум запросов за окно (например, 100) |
| `X-RateLimit-Remaining` | Сколько запросов осталось в текущем окне |
| `X-RateLimit-Reset` | Unix-метка времени сброса окна |

Превысите лимит — и получите `429 Too Many Requests`. Тело ответа сообщает, сколько
ждать:

```json
{
  "status": false,
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Retry after 23 seconds.",
  "retry_after": 23
}
```

Простой и вежливый клиент следит за `X-RateLimit-Remaining` и сбавляет темп либо
повторяет попытку через `retry_after` секунд, когда видит `429`.

---

## Формат ответа

Каждый ответ использует одну и ту же структуру JSON, так что обрабатывать их все можно
одинаково.

**Отдельный объект** возвращается в поле `data`:

```json
{
  "status": true,
  "data": {
    "id": 123,
    "number": "INV-000123",
    "total": 5000.00
  }
}
```

**Список** возвращается как массив в поле `data`, с блоком `meta` для постраничной
навигации:

```json
{
  "status": true,
  "data": [
    { "id": 1, "...": "..." },
    { "id": 2, "...": "..." }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 142,
    "total_pages": 6
  }
}
```

**Ошибка** всегда содержит `"status": false`, короткий код `error`, по которому можно
ветвить логику, и понятное человеку сообщение `message`:

```json
{
  "status": false,
  "error": "not_found",
  "message": "Invoice #999 not found."
}
```

Совет: сначала проверяйте `status`. Если он `true`, читайте `data`; если `false` —
читайте `error` и `message`.

---

## Постраничная навигация (pagination)

Каждый эндпоинт-список поддерживает постраничную навигацию:

| Параметр | По умолчанию | Значение |
|-----------|---------|---------|
| `page` | 1 | Номер страницы (с 1) |
| `per_page` | 25 | Элементов на странице (макс. 100) |

Используйте значение `total_pages` в `meta`, чтобы понять, когда остановиться.

Пример: `GET /api/v1/invoices?page=2&per_page=50`

---

## Фильтрация и сортировка

Эндпоинты-списки принимают параметры запроса для сужения и упорядочивания результатов:

| Параметр | Применяется к | Значение |
|-----------|-----------|---------|
| `search` | Ко всем | Текстовый поиск по ключевым полям |
| `status` | Счета, Лиды | Фильтр по статусу (например, `paid`, `unpaid`, `overdue`) |
| `date_from` | Ко всему с датами | Начальная дата (`YYYY-MM-DD`) |
| `date_to` | Ко всему с датами | Конечная дата (`YYYY-MM-DD`) |
| `client_id` | Счета, Платежи | Фильтр по клиенту |
| `assigned` | Лиды | Фильтр по ID назначенного сотрудника |
| `sort` | Ко всем | Поле для сортировки (например, `date`, `total`, `name`) |
| `order` | Ко всем | `asc` или `desc` |

Пример: `GET /api/v1/invoices?status=unpaid&date_from=2026-01-01&sort=total&order=desc`

---

## Эндпоинты

API охватывает ваши основные бизнес-записи. Ниже каждый из них с параметрами и
примером ответа.

### Счета (Invoices)

#### Список счетов

```
GET /api/v1/invoices
```

Параметры запроса: `status`, `client_id`, `date_from`, `date_to`, `search`,
`page`, `per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 123,
      "number": "INV-000123",
      "client_id": 45,
      "client_name": "Acme SRL",
      "date": "2026-05-01",
      "duedate": "2026-05-31",
      "subtotal": 4201.68,
      "total_tax": 798.32,
      "total": 5000.00,
      "currency": "RON",
      "status": "unpaid",
      "status_label": "Unpaid"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 142, "total_pages": 6 }
}
```

#### Получить один счёт

```
GET /api/v1/invoices/:id
```

Возвращает счёт целиком, включая строки и любые зафиксированные платежи.

```json
{
  "status": true,
  "data": {
    "id": 123,
    "number": "INV-000123",
    "client_id": 45,
    "client_name": "Acme SRL",
    "date": "2026-05-01",
    "duedate": "2026-05-31",
    "subtotal": 4201.68,
    "total_tax": 798.32,
    "total": 5000.00,
    "currency": "RON",
    "status": "unpaid",
    "items": [
      {
        "description": "Consulting services — May 2026",
        "qty": 40,
        "rate": 100.00,
        "tax_name": "TVA 19%",
        "tax_rate": 19.00,
        "amount": 4000.00
      }
    ],
    "payments": [],
    "created_at": "2026-05-01T10:30:00+03:00"
  }
}
```

---

### Клиенты (Clients)

#### Список клиентов

```
GET /api/v1/clients
```

Параметры запроса: `search`, `page`, `per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 45,
      "company": "Acme SRL",
      "vat": "RO12345678",
      "phonenumber": "+40721000000",
      "city": "Bucharest",
      "country": "Romania",
      "active": true,
      "total_invoiced": 125000.00,
      "health_score": 82
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 310, "total_pages": 13 }
}
```

#### Получить одного клиента

```
GET /api/v1/clients/:id
```

Возвращает полные данные клиента, включая контакты, заметки и сводную статистику.

---

### Лиды (Leads)

#### Список лидов

```
GET /api/v1/leads
```

Параметры запроса: `status`, `source`, `assigned`, `search`, `date_from`,
`date_to`, `page`, `per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 789,
      "name": "John Smith",
      "company": "Widget Corp",
      "email": "john@widgetcorp.com",
      "phonenumber": "+40722000000",
      "value": 15000.00,
      "status": "new",
      "source": "Website",
      "assigned": 3,
      "assigned_name": "Maria Popescu",
      "created_at": "2026-05-15T14:20:00+03:00"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 58, "total_pages": 3 }
}
```

#### Создать лид

```
POST /api/v1/leads
Content-Type: application/json
```

**Тело запроса:**

```json
{
  "name": "Jane Doe",
  "company": "NewCo SRL",
  "email": "jane@newco.ro",
  "phonenumber": "+40723000000",
  "value": 8000.00,
  "source": "API",
  "assigned": 3,
  "description": "Interested in warehouse module"
}
```

**Ответ (`201 Created`):**

```json
{
  "status": true,
  "data": {
    "id": 790,
    "name": "Jane Doe",
    "company": "NewCo SRL",
    "status": "new",
    "created_at": "2026-05-16T09:00:00+03:00"
  }
}
```

---

### Остатки (Stock)

#### Список товаров на складе

```
GET /api/v1/stock
```

Параметры запроса: `search`, `warehouse_id`, `below_reorder` (boolean), `page`,
`per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 201,
      "code": "MAT-001",
      "name": "Steel Rod 10mm",
      "warehouse_id": 1,
      "warehouse_name": "Main Warehouse",
      "qty_on_hand": 450,
      "qty_reserved": 30,
      "qty_available": 420,
      "reorder_level": 100,
      "unit": "kg",
      "avg_cost": 12.50,
      "total_value": 5625.00
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 1200, "total_pages": 48 }
}
```

#### Движения товаров

```
GET /api/v1/stock/movements
```

Параметры запроса: `item_id`, `warehouse_id`, `type` (in/out/transfer),
`date_from`, `date_to`, `page`, `per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 5001,
      "item_id": 201,
      "item_name": "Steel Rod 10mm",
      "type": "in",
      "qty": 100,
      "reference_type": "grn",
      "reference_id": 88,
      "warehouse_id": 1,
      "date": "2026-05-14T08:00:00+03:00",
      "staff_id": 2,
      "staff_name": "Ion Popescu"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 8400, "total_pages": 336 }
}
```

---

### Платежи (Payments)

#### Список платежей

```
GET /api/v1/payments
```

Параметры запроса: `client_id`, `invoice_id`, `date_from`, `date_to`, `page`,
`per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 567,
      "invoice_id": 123,
      "invoice_number": "INV-000123",
      "client_id": 45,
      "amount": 5000.00,
      "payment_mode": "Bank Transfer",
      "date": "2026-05-10",
      "note": "Wire transfer ref: TRF-2026-0510"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 890, "total_pages": 36 }
}
```

---

### Расходы (Expenses)

#### Список расходов

```
GET /api/v1/expenses
```

Параметры запроса: `category`, `date_from`, `date_to`, `billable` (boolean),
`search`, `page`, `per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 340,
      "category": "Office Supplies",
      "amount": 250.00,
      "currency": "RON",
      "tax": 47.50,
      "date": "2026-05-12",
      "client_id": null,
      "billable": false,
      "reference_no": "RCP-2026-0512",
      "note": "Printer cartridges"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 234, "total_pages": 10 }
}
```

---

### Сотрудники (Staff)

#### Список сотрудников

```
GET /api/v1/staff
```

Параметры запроса: `search`, `role`, `active` (boolean), `page`, `per_page`

**Ответ:**

```json
{
  "status": true,
  "data": [
    {
      "id": 3,
      "firstname": "Maria",
      "lastname": "Popescu",
      "email": "maria@company.ro",
      "role": "Senior Sales",
      "active": true,
      "last_login": "2026-05-16T08:45:00+03:00"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total": 18, "total_pages": 1 }
}
```

#### Собственный пользователь токена

```
GET /api/v1/staff/me
```

Удобно для проверки того, что токен работает, и для просмотра того, что ему разрешено.
Возвращает сотрудника, стоящего за токеном, его права и текущий статус ограничения
частоты запросов.

```json
{
  "status": true,
  "data": {
    "id": 3,
    "firstname": "Maria",
    "lastname": "Popescu",
    "email": "maria@company.ro",
    "role": "Senior Sales",
    "permissions": ["invoices.view", "clients.view", "leads.manage", "stock.view"],
    "token_description": "Mobile App",
    "rate_limit": 100,
    "rate_limit_remaining": 87
  }
}
```

---

### Наборы данных аналитики (Analytics datasets)

#### Список наборов данных

```
GET /api/v1/analytics/datasets
```

Перечисляет встроенные наборы данных аналитики, которые можно запустить. У каждого есть
стабильный строковый `id`, читаемый `label` и краткое `description`.

**Ответ:**

```json
{
  "status": true,
  "data": [
    { "id": "revenue_monthly",      "label": "Monthly Revenue",       "description": "Total invoiced revenue grouped by month." },
    { "id": "leads_by_status",      "label": "Leads by Status",       "description": "Count of leads grouped by their current status." },
    { "id": "expenses_by_category", "label": "Expenses by Category",  "description": "Total expenses grouped by category." },
    { "id": "invoices_aging",       "label": "Invoices Aging",        "description": "Unpaid invoices grouped by aging buckets." },
    { "id": "stock_valuation",      "label": "Stock Valuation",       "description": "Current stock quantities and values by warehouse." }
  ]
}
```

#### Run a dataset

```
GET /api/v1/analytics/run/:dataset_id
```

Запускает один из наборов данных выше (`:dataset_id` — это `id` из списка) и возвращает
вычисленные строки. Каждый набор проверяет соответствующее право (например,
`revenue_monthly` требует право на просмотр счетов).

```json
{
  "status": true,
  "data": {
    "dataset": "revenue_monthly",
    "results": [
      { "month": "2026-05", "revenue": 184200.00, "invoice_count": 37 }
    ],
    "generated_at": "2026-05-16 02:00:00"
  }
}
```

---

### More endpoints

Ещё несколько эндпоинтов на чтение дополняют API. Они подчиняются тем же правилам
envelope, токена и постраничной навигации, что и описанные выше.

| Эндпоинт | Метод | Возвращает |
|----------|-------|------------|
| `/api/v1/status` | `GET` | Состояние сервиса и личность, стоящая за токеном (также ответ для `GET /api/v1`). |
| `/api/v1/items` | `GET` | Позиции каталога (список). `search`, `page`, `per_page`. |
| `/api/v1/items/:id` | `GET` | Одна позиция каталога. |
| `/api/v1/treasury/places` | `GET` | Места хранения денег — только чтение, в пределах компаний токена. |
| `/api/v1/bank/accounts` | `GET` | Банковские счета — только чтение, в пределах компаний токена. |

Некоторые ресурсы также принимают запись, когда у сотрудника токена есть нужное право:
`POST /api/v1/invoices`, `POST /api/v1/clients`, а также `GET /api/v1/leads/:id` и
`PUT /api/v1/leads/:id` в дополнение к эндпоинтам лидов, показанным выше.

---

## Коды ошибок

Когда `status` равен `false`, HTTP-статус и код `error` сообщают, что пошло не так:

| HTTP-статус | Код ошибки | Значение |
|------------|-----------|---------|
| 400 | `bad_request` | Некорректный запрос или неверные параметры |
| 401 | `unauthorized` | Отсутствует или недействителен токен |
| 403 | `forbidden` | Токен действителен, но не имеет прав на этот ресурс |
| 404 | `not_found` | Ресурс не существует |
| 422 | `validation_error` | Входные данные не прошли валидацию (подробности в объекте `errors`) |
| 429 | `rate_limit_exceeded` | Слишком много запросов |
| 500 | `server_error` | На сервере произошла ошибка |

Ошибка валидации точно указывает, какие поля нужно исправить:

```json
{
  "status": false,
  "error": "validation_error",
  "message": "Validation failed.",
  "errors": {
    "email": ["The email field is required."],
    "name": ["The name must be at least 2 characters."]
  }
}
```

---

## Примеры кода

Одни и те же две задачи — получить список неоплаченных счетов и создать лид — на трёх
языках.

### cURL

```bash
# List unpaid invoices
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  "https://erp.example.com/api/v1/invoices?status=unpaid&per_page=10"

# Get a single client
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  "https://erp.example.com/api/v1/clients/45"

# Create a lead
curl -s -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","email":"jane@example.com","value":5000,"source":"API"}' \
  "https://erp.example.com/api/v1/leads"
```

### PHP

```php
<?php
$token = 'YOUR_TOKEN';
$base  = 'https://erp.example.com/api/v1';

// List invoices
$ch = curl_init("{$base}/invoices?status=unpaid&per_page=50");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ["Authorization: Bearer {$token}"],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

foreach ($response['data'] as $invoice) {
    echo "#{$invoice['number']} — {$invoice['total']} {$invoice['currency']}\n";
}

// Create a lead
$ch = curl_init("{$base}/leads");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer {$token}",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'name'    => 'Jane Doe',
        'email'   => 'jane@example.com',
        'value'   => 5000,
        'source'  => 'API',
    ]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

echo "Created lead #{$result['data']['id']}\n";
```

### JavaScript (fetch)

```javascript
const TOKEN = 'YOUR_TOKEN';
const BASE  = 'https://erp.example.com/api/v1';

// List invoices
const invoices = await fetch(`${BASE}/invoices?status=unpaid`, {
  headers: { 'Authorization': `Bearer ${TOKEN}` }
}).then(r => r.json());

console.log(`Found ${invoices.meta.total} unpaid invoices`);
invoices.data.forEach(inv => {
  console.log(`#${inv.number} — ${inv.total} ${inv.currency}`);
});

// Create a lead
const newLead = await fetch(`${BASE}/leads`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Jane Doe',
    email: 'jane@example.com',
    value: 5000,
    source: 'API'
  })
}).then(r => r.json());

console.log(`Created lead #${newLead.data.id}`);
```

---

## Webhooks (скоро)

Push-уведомления при изменении записей запланированы на будущий релиз. Пока же
опрашивайте API с фильтром `date_from`, чтобы подхватывать новые или обновлённые
записи с момента последней проверки.

---

## Права доступа (Permissions)

Доступ к API использует ту же систему прав, что и админ-интерфейс. Соответствующие
права:

| Право | Нужно, чтобы |
|-----------|-----------|
| Manage API tokens | Генерировать и отзывать токены |
| API access | Вообще пользоваться API (базовое право) |
| Per-record permissions | Достучаться до каждого эндпоинта (например, просмотр счетов, создание лидов) |

Поскольку токены наследуют роль своего сотрудника, проще всего управлять тем, что
может делать токен, назначив его сотруднику с ровно нужным набором прав.

---

## Смотрите также

- [Автоматизации](tsync-automations.md) — реагируйте на изменения данных без написания
  кода
- [Начало работы с TSync](../index.md) — обзор продукта

---

## Формат ответа (v5.9.17)

Каждый ответ использует единый формат:

**Успех (один объект):**
```json
{ "status": true, "data": { } }
```

**Успех (список):**
```json
{ "status": true, "data": [ ], "meta": { "page": 1, "per_page": 25, "total": 100, "total_pages": 4 } }
```

**Ошибка:**
```json
{ "status": false, "error": "not_found", "message": "…" }
```

(`ok` также присутствует рядом со `status` для старых клиентов.) Каждый ответ содержит заголовки лимита — `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` — и каждый вызов логируется (токен, сотрудник, компания, метод, endpoint, статус, длительность, IP).

### Эндпоинты

`GET /api/v1/status` · `GET /api/v1/staff/me` · `GET|POST /api/v1/clients` · `GET|POST /api/v1/leads`, `PUT /api/v1/leads/{id}` · `GET /api/v1/items` · `GET /api/v1/stock` · `GET /api/v1/treasury/places` (только чтение) · `GET /api/v1/bank/accounts` (только чтение) · плюс счета, платежи, расходы, аналитика.

Каждый токен принадлежит сотруднику и соблюдает его права; данные не возвращаются между компаниями, к которым у токена нет доступа.
