PHP (Guzzle) PHP (cURL) cURL JavaScript Python JSON

Вступ

Це сторінка документації API KWIGA. За допомогою API ви можете виконувати дії від свого імені в ваших кабінетах KWIGA — кабінет це ваш робочий простір (акаунт / тенант), де живуть ваші курси, контакти та решта даних.

Для того щоб увімкнути API, в особистому кабінеті в розділі settings потрібно відмітити відповідний чекбокс. Після цього буде згенерований токен для API та отриманий хеш кабінету, які потрібні для подальших звернень до API.

При компрометації токена його можна перевипустити, що зробить минулий токен неактивним і згенерує новий.

API доступний тільки для тих користувачів, у кого в кабінеті включено API.

Обмеження частоти запитів

API має обмеження на кількість звернень 200 запитів на хвилину. Ліміт рахується окремо для кожного API-токена.

У кожній відповіді повертаються такі заголовки з інформацією про ліміт:

Заголовок Опис
X-RateLimit-Limit максимальна кількість запитів на хвилину.
X-RateLimit-Remaining скільки запитів ще доступно в поточній хвилині.
Retry-After через скільки секунд можна повторити запит. Повертається лише при перевищенні ліміту (HTTP 429).
X-RateLimit-Reset UNIX timestamp, коли скидається поточне вікно. Повертається лише при перевищенні ліміту (HTTP 429).

Ідемпотентність

Мутуючі ендпоінти публічного API (зараз POST /contacts/purchases та POST /contacts/:contact/rewards; інші будуть позначатися індивідуально) приймають опціональний header Idempotency-Key. Передайте унікальне значення один раз і використовуйте його на кожному ретраї тієї ж логічної операції — сервер «згорне» повтори в один сайд-ефект і на кожен ретрай поверне ту ж відповідь.

Ендпоінти, що підтримують ідемпотентність, позначені в цій документації плашкою: Ідемпотентний

Контракт:

Header Поведінка
Idempotency-Key (request) Унікальний рядок від клієнта (1–255 символів). Підійде UUIDv4 або стабільний бізнес-ключ типу reward-for-quiz-attempt-4821. Порожній/відсутній header вимикає ідемпотентність для цього виклику. Клієнти, які не можуть передавати кастомні headers (form-based інтеграції), можуть передати те саме значення як поле body/query idempotency_key — header має пріоритет, якщо передані обидва.
Idempotent-Replayed: true (response) Повертається на другому та подальших викликах з тим самим ключем. Якщо бачите цей header — сервер НЕ виконував операцію повторно, body містить той самий обʼєкт, що був створений при першому виклику (сутність перечитується та серіалізується заново, тому відображає свій поточний стан). Якщо цю сутність на цей момент було видалено остаточно, body — це мінімальне підтвердження з її типом та id.

Область ключа — (кабінет, ендпоінт, ключ): той же бізнес-ключ на іншому ендпоінті або в іншому кабінеті вважається окремою операцією. Рекомендуємо все одно UUIDv4 — колізії стають статистично неможливими.

Дві відповіді, специфічні для ідемпотентності. 409 — операція з цим ключем уже виконується іншим запитом: повторіть трохи згодом і отримаєте її результат; виконувати її вдруге ми не будемо. 400 — ключ довший за 255 символів: непридатний ключ ми не ігноруємо мовчки, інакше ви вважали б себе захищеними, доки кожен ретрай створює нове замовлення.

Базова робота з API

<?php
// Get contact by ID

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts/:contact', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get contact by ID

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/contacts/:contact' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get contact by ID
const url = 'https://api.kwiga.com/contacts/:contact';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get contact by ID
import requests

url = 'https://api.kwiga.com/contacts/:contact'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts/:contact",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Всі запити повинні надсилатися на домен:

https://api.kwiga.com

Для авторизації передаємо API-токен в одному з варіантів:

Для ідентифікації кабінету, в якому відбуваються дії, обов'язково передаємо хеш-кабінету в одному з варіантів:

PUT та DELETE запити можна надсилати POST-запитом із зазначенням додаткового параметра _method з потрібним методом (PUT або DELETE).

Встановити локалізацію довідників та повідомлень валідації можна заголовком:

X-Language: {locale}

Locale Description
en English (default)
cs Czech
de German
el Greek
es Spanish
fr Franch
hu Hungarian
it Italian
ka Georgian
lv Polish
pl Polish
pt Portuguese
ro Romanian
ru Russian
uk Ukrainian
zh Chinese (Simplified)

Помилки

Код помилки Значення
400 Bad Request - Ваш запит невірний.
401 Unauthorized - Ваш API-ключ невірний.
403 Forbidden - Запитаний ресурс захован та доступний лише адміністраторам.
404 Not Found - Не знайдено. Вказана сторінка не знайдена.
405 Method Not Allowed - Метод не дозволений. Серверу відомий метод запиту, але цільовий ресурс не підтримує цей метод.
422 Unprocessable entity - Сутність неможливо обробити.
429 Too Many Requests - Дуже багато запитів - Ви надсилаєте надто багато запитів, зупиніться!
500 Internal Server Error - Внутрішня помилка сервера. Проблема із нашим сервером. Повторіть спробу пізніше.
503 Service Unavailable - ми тимчасово відключені від мережі через технічне обслуговування. Повторіть спробу пізніше.

CRM. Контакти

Картки контактів

Список контактів

Приклад запиту:

<?php
// Get contacts list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts', $options);

$result = json_decode($response->getBody());
?>


# ---
# Paginated example
# ---

<?php
// Get contacts list with pagination

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts?page=1&per_page=15', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Get contacts list with filters

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_orders=1', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get contacts list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Paginated example
# ---

<?php
// Get contacts list with pagination

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts?page=1&per_page=15');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Get contacts list with filters

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_orders=1');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/contacts' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Paginated example
# ---

curl --location --request GET 'https://api.kwiga.com/contacts?page=1&per_page=15' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_orders=1' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get contacts list
const url = 'https://api.kwiga.com/contacts';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Paginated example
# ---

// Get contacts list with pagination
const url = 'https://api.kwiga.com/contacts?page=1&per_page=15';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Get contacts list with filters
const url = 'https://api.kwiga.com/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_orders=1';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get contacts list
import requests

url = 'https://api.kwiga.com/contacts'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Paginated example
# ---

# Get contacts list with pagination
import requests

url = 'https://api.kwiga.com/contacts?page=1&per_page=15'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Get contacts list with filters
import requests

url = 'https://api.kwiga.com/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_orders=1'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Paginated example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts?page=1&per_page=15",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_orders=1",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 1,
      "created_at": "2022-02-04T12:17:32.000000Z",
      "email": "test@example.com",
      "first_name": "James",
      "last_name": "Bond",
      "phone": "+380931234567",
      "tags": [
          {
              "id": 93,
              "name": "test-tag"
          }
      ],
      "utm": {
          "utm_source": [
              "test-source"
          ],
          "utm_campaign": [],
          "utm_medium": [],
          "utm_term": [
              "test-term"
          ],
          "utm_content": []
      },
      "offers": [
          {
              "id": 8,
              "unique_offer_code": "ptJmPPXVYs0t",
              "title": "Предложение #8",
              "limit_type": {
                  "id": 1,
                  "name": "Неограничено"
              },
              "limit_of_sales": null
          }
      ],
      "telegram_accounts": [
          {
              "telegram_id": 123456789,
              "username": "jamesbond"
          },
          {
              "telegram_id": 987654321,
              "username": null
          }
      ]
    },
    {
      "id": 2,
      "created_at": "2022-02-04T12:17:32.000000Z",
      "email": "test2@example.com",
      "first_name": "Petr",
      "last_name": "Ivanov",
      "phone": "+380983234512",
      "tags": [],
      "offers": [],
      "telegram_accounts": []
    }
  ],
  "links": {
        "first": "https://api.kwiga.com/contacts?page=1",
        "last": "https://api.kwiga.com/contacts?page=2",
        "prev": null,
        "next": "https://api.kwiga.com/contacts?page=2"
   },
   "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 2,
        "links": [
            {
                "url": null,
                "label": "&laquo; translation missing: ua.pagination_prev",
                "active": false
            },
            {
                "url": "https://api.kwiga.com/contacts?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": "https://api.kwiga.com/contacts?page=2",
                "label": "2",
                "active": false
            },
            {
                "url": "https://api.kwiga.com/contacts?page=2",
                "label": "translation missing: ua.pagination_next &raquo;",
                "active": false
            }
        ],
        "path": "https://api.kwiga.com/contacts",
        "per_page": 15,
        "to": 2,
        "total": 3
    }
}

GET https://api.kwiga.com/contacts

URL Parameters

filters object optional
Filter parameters
is_active integer optional
Фільтр активних контактів
date_from datetime optional
Фільтр за датою створення. Параметр 'від'
date_to datetime optional
Фільтр за датою створення. Параметр 'до'
last_activity_from datetime optional
Фільтр за датою останньої активності. Параметр 'від'
last_activity_to datetime optional
Фільтр за датою останньої активності. Параметр 'до'
search string optional
Фільтр по email, телефону, ПІБ
utm_source string optional
utm_source
utm_campaign string optional
utm_campaign
utm_medium string optional
utm_medium
utm_term string optional
utm_term
utm_content string optional
utm_content
offers integer[] optional
offers ids
products integer[] optional
products ids
emails string/string[] optional
Фільтр за точним email-адресою. Приймає рядок через кому або повторюваний масив; кожне значення має бути валідним email.
contact_ids integer/integer[] optional
Фільтр за id контактів. Приймає рядок через кому або повторюваний масив цілих чисел.
user_ids integer/integer[] optional
Фільтр за id юзерів (акаунтів учня). Приймає рядок через кому або повторюваний масив цілих чисел.

with_orders boolean optional
Додатково отримати інформацію по замовленням та пропозиціям контакта
with_certificates boolean optional
Додатково отримати інформацію по сертифікатам контакта
page integer optional
Номер сторінки
Default: 1
per_page integer optional
Кількість елементів вибірки
Default: 15
sort_by string optional
Possible values: asc, descDefault: desc

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

POST-аліас

POST https://api.kwiga.com/contacts/query

Функціональний аліас для GET-ендпоінта вище. Приймає ті самі параметри в тілі запиту замість query-string — стане в нагоді, коли список фільтрів завеликий для URL (або просто зручніше збирати JSON на клієнті). Body має пріоритет над query-string, форма відповіді ідентична.

Отримання контакту

Приклад запиту:

<?php
// Get contact by ID

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts/:contact', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get contact by ID

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/contacts/:contact' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get contact by ID
const url = 'https://api.kwiga.com/contacts/:contact';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get contact by ID
import requests

url = 'https://api.kwiga.com/contacts/:contact'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts/:contact",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": {
        "id": 132,
        "email": "bond@example.com",
        "first_name": "James",
        "last_name": "Bond",
        "phone_number": "931234567",
        "tags": [],
        "telegram_accounts": [
            {
                "telegram_id": 123456789,
                "username": "jamesbond"
            },
            {
                "telegram_id": 987654321,
                "username": null
            }
        ],
        "created_at": "2023-06-26T19:01:00.000000Z",
        "additional_fields": [
            {
                "id": 110,
                "field": {
                    "id": 17,
                    "title": "test global",
                    "is_local": false
                },
                "value": "this is test value"
            }
        ]
    }
}

GET https://api.kwiga.com/contacts/:contact

URL Parameters

with_certificates boolean optional
Додатково отримати інформацію по сертифікатам контакта

Структура відповіді

Створення контакту

Приклад запиту:

<?php
// Create new contact

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'first_name' => 'James',
        'last_name' => 'Bond',
        'email' => 'bond@example.com',
        'send_activation_email' => true,
        'phone' => '+380931234567',
        'manager_ids' => [264, 288],
        'additional_fields' => [{"field_id"=>17, "value"=>"this is test value"}],
    ],
];

$response = $client->request('POST', '/contacts', $options);

$result = json_decode($response->getBody());
?>
<?php
// Create new contact

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'first_name' => 'James',
    'last_name' => 'Bond',
    'email' => 'bond@example.com',
    'send_activation_email' => true,
    'phone' => '+380931234567',
    'manager_ids' => [264, 288],
    'additional_fields' => [{"field_id"=>17, "value"=>"this is test value"}],
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"first_name":"James","last_name":"Bond","email":"bond@example.com","send_activation_email":true,"phone":"+380931234567","manager_ids":[264,288],"additional_fields":[{"field_id":17,"value":"this is test value"}]}'
// Create new contact
const url = 'https://api.kwiga.com/contacts';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'first_name': 'James',
    'last_name': 'Bond',
    'email': 'bond@example.com',
    'send_activation_email': true,
    'phone': '+380931234567',
    'manager_ids': [264, 288],
    'additional_fields': [{"field_id"=>17, "value"=>"this is test value"}]
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Create new contact
import requests

url = 'https://api.kwiga.com/contacts'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'first_name': 'James',
    'last_name': 'Bond',
    'email': 'bond@example.com',
    'send_activation_email': true,
    'phone': '+380931234567',
    'manager_ids': [264, 288],
    'additional_fields': [{"field_id"=>17, "value"=>"this is test value"}],
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "first_name": "James",
    "last_name": "Bond",
    "email": "bond@example.com",
    "send_activation_email": true,
    "phone": "+380931234567",
    "manager_ids": [
      264,
      288
    ],
    "additional_fields": [
      {
        "field_id": 17,
        "value": "this is test value"
      }
    ]
  }
}

Приклад відповіді:

{
    "data": {
        "id": 132,
        "email": "bond@example.com",
        "first_name": "James",
        "last_name": "Bond",
        "phone_number": "931234567",
        "tags": [],
        "created_at": "2023-06-26T19:01:00.000000Z",
        "additional_fields": [
            {
                "id": 110,
                "field": {
                    "id": 17,
                    "title": "test global",
                    "is_local": false
                },
                "value": "this is test value"
            }
        ]
    }
}

POST https://api.kwiga.com/contacts

Request

email string required
Email - має бути унікальним у рамках кабінету
phone string optional
Номер телефону — формат: +{код країни}{телефон}
first_name string optional
Ім'я контакту
middle_name string optional
По-батькові контакту (або middle name)
last_name string optional
Прізвище контакту
name string optional
Повне ім'я одним рядком. Використовуйте, якщо у вас немає розбивки на ім'я/по-батькові/прізвище — сервер збереже значення як ім'я контакту.
tags string[] optional
Теги
send_activation_email boolean optional
Надіслати вітальний лист
Default: false
locale string optional
Мова контакту в форматі iso_2. Повний список значень див. у блоці локалей, які підтримуються.
Default: en
create_order boolean optional
Створити пусте замовлення
order_stage_id integer optional
Ідентифікатор статусу замовлення у воронці (можна отримати на сторінці CRM → Замовлення → Налаштування → Список статусів)
additional_fields object[] optional
Поля користувача (кастомні поля)
field_id integer optional
id кастомного поля (можна отримати в CRM → Контакти → Налаштування → Додавання полів користувача)
value string optional
Значення кастомного поля

Структура відповіді

Оновлення контакту

Приклад запиту:

<?php
// Update contact

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'first_name' => 'John',
        'last_name' => 'Black',
        'email' => 'bond123@example.com',
        'phone' => '+380931112233',
        'tags' => ["test-tag"],
    ],
];

$response = $client->request('PUT', '/contacts/:contact', $options);

$result = json_decode($response->getBody());
?>
<?php
// Update contact

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');

$data = [
    'first_name' => 'John',
    'last_name' => 'Black',
    'email' => 'bond123@example.com',
    'phone' => '+380931112233',
    'tags' => ["test-tag"],
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request PUT 'https://api.kwiga.com/contacts/:contact' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"first_name":"John","last_name":"Black","email":"bond123@example.com","phone":"+380931112233","tags":["test-tag"]}'
// Update contact
const url = 'https://api.kwiga.com/contacts/:contact';

const options = {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'first_name': 'John',
    'last_name': 'Black',
    'email': 'bond123@example.com',
    'phone': '+380931112233',
    'tags': ["test-tag"]
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Update contact
import requests

url = 'https://api.kwiga.com/contacts/:contact'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'first_name': 'John',
    'last_name': 'Black',
    'email': 'bond123@example.com',
    'phone': '+380931112233',
    'tags': ["test-tag"],
}

response = requests.put(url, headers=headers, json=data)

result = response.json()
{
  "method": "PUT",
  "url": "https://api.kwiga.com/contacts/:contact",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "first_name": "John",
    "last_name": "Black",
    "email": "bond123@example.com",
    "phone": "+380931112233",
    "tags": [
      "test-tag"
    ]
  }
}

Приклад відповіді:

{
    "data": {
        "id": 132,
        "email": "bond@example.com",
        "first_name": "James",
        "last_name": "Bond",
        "phone_number": "931234567",
        "tags": [],
        "created_at": "2023-06-26T19:01:00.000000Z",
        "additional_fields": [
            {
                "id": 110,
                "field": {
                    "id": 17,
                    "title": "test global",
                    "is_local": false
                },
                "value": "this is test value"
            }
        ]
    }
}

PUT https://api.kwiga.com/contacts/:contact

Request

email string optional
Email - має бути унікальним у рамках кабінету
phone string optional
Номер телефону — формат: +{код країни}{телефон}
first_name string optional
Ім'я контакту
last_name string optional
Прізвище контакту
tags string[] optional
Теги. Працюють в режимі sync. Тобто на контакті будуть тільки ті теги, які будуть передані
additional_fields object[] optional
Поля користувача (кастомні поля)
field_id integer optional
id кастомного поля (можна отримати в CRM → Контакти → Налаштування → Додавання полів користувача)
value string optional
Значення кастомного поля

Структура відповіді

Додати покупку Ідемпотентний

Цей метод створює або знаходить контакт и додає йому покупку пропозиції

Кожен виклик створює нове замовлення, навіть якщо контакт уже має цей продукт. Щоб ретрай (повторна відправка, подвійний клік менеджера, повторний вебхук) не породжував дублі замовлень, передайте Idempotency-Key: повтор із тим самим ключем не створить друге замовлення й поверне той самий контакт із header'ом Idempotent-Replayed: true.

Ключ ідемпотентності захищає від повтору ОДНОГО Й ТОГО САМОГО запиту. Якщо ж дублі надходять різними запитами (інтегратор надсилає покупку заново, ключа немає або він щоразу новий), передайте skip_same_subscription: true — тоді те, чим контакт уже володіє, повторно не видається і зайве замовлення не створюється. Механізми незалежні та сумісні: можна використовувати обидва одночасно.

Приклад запиту:

<?php
// Add purchase to contact

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'first_name' => 'James',
        'last_name' => 'Bond',
        'email' => 'bond@example.com',
        'send_activation_email' => true,
        'phone' => '+380931234567',
        'offer_id' => 18,
        'additional_fields' => [{"field_id"=>17, "value"=>"this is test value"}],
    ],
];

$response = $client->request('POST', '/contacts/purchases', $options);

$result = json_decode($response->getBody());
?>


# ---
# Idempotent example
# ---

<?php
// Safely retry the same purchase — passing Idempotency-Key prevents a duplicate order

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
        'Idempotency-Key' => 'purchase-crm-deal-4623778',
    ],
    'json' => [
        'email' => 'bond@example.com',
        'offer_id' => 18,
    ],
];

$response = $client->request('POST', '/contacts/purchases', $options);

$result = json_decode($response->getBody());
?>


# ---
# Skip_same_subscription example
# ---

<?php
// Skip the purchase when the contact already owns the offer

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'email' => 'bond@example.com',
        'offer_id' => 18,
        'skip_same_subscription' => true,
    ],
];

$response = $client->request('POST', '/contacts/purchases', $options);

$result = json_decode($response->getBody());
?>
<?php
// Add purchase to contact

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/purchases');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'first_name' => 'James',
    'last_name' => 'Bond',
    'email' => 'bond@example.com',
    'send_activation_email' => true,
    'phone' => '+380931234567',
    'offer_id' => 18,
    'additional_fields' => [{"field_id"=>17, "value"=>"this is test value"}],
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Idempotent example
# ---

<?php
// Safely retry the same purchase — passing Idempotency-Key prevents a duplicate order

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
    'Idempotency-Key: purchase-crm-deal-4623778',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/purchases');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'email' => 'bond@example.com',
    'offer_id' => 18,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Skip_same_subscription example
# ---

<?php
// Skip the purchase when the contact already owns the offer

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/purchases');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'email' => 'bond@example.com',
    'offer_id' => 18,
    'skip_same_subscription' => true,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts/purchases' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"first_name":"James","last_name":"Bond","email":"bond@example.com","send_activation_email":true,"phone":"+380931234567","offer_id":18,"additional_fields":[{"field_id":17,"value":"this is test value"}]}'


# ---
# Idempotent example
# ---

curl --location --request POST 'https://api.kwiga.com/contacts/purchases' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--header 'Idempotency-Key: purchase-crm-deal-4623778' \
--data-raw '{"email":"bond@example.com","offer_id":18}'


# ---
# Skip_same_subscription example
# ---

curl --location --request POST 'https://api.kwiga.com/contacts/purchases' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"email":"bond@example.com","offer_id":18,"skip_same_subscription":true}'
// Add purchase to contact
const url = 'https://api.kwiga.com/contacts/purchases';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'first_name': 'James',
    'last_name': 'Bond',
    'email': 'bond@example.com',
    'send_activation_email': true,
    'phone': '+380931234567',
    'offer_id': 18,
    'additional_fields': [{"field_id"=>17, "value"=>"this is test value"}]
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Idempotent example
# ---

// Safely retry the same purchase — passing Idempotency-Key prevents a duplicate order
const url = 'https://api.kwiga.com/contacts/purchases';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
    'Idempotency-Key': 'purchase-crm-deal-4623778',
  },
  body: JSON.stringify({
    'email': 'bond@example.com',
    'offer_id': 18
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Skip_same_subscription example
# ---

// Skip the purchase when the contact already owns the offer
const url = 'https://api.kwiga.com/contacts/purchases';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'email': 'bond@example.com',
    'offer_id': 18,
    'skip_same_subscription': true
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Add purchase to contact
import requests

url = 'https://api.kwiga.com/contacts/purchases'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'first_name': 'James',
    'last_name': 'Bond',
    'email': 'bond@example.com',
    'send_activation_email': true,
    'phone': '+380931234567',
    'offer_id': 18,
    'additional_fields': [{"field_id"=>17, "value"=>"this is test value"}],
}

response = requests.post(url, headers=headers, json=data)

result = response.json()


# ---
# Idempotent example
# ---

# Safely retry the same purchase — passing Idempotency-Key prevents a duplicate order
import requests

url = 'https://api.kwiga.com/contacts/purchases'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
    'Idempotency-Key': 'purchase-crm-deal-4623778',
}

data = {
    'email': 'bond@example.com',
    'offer_id': 18,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()


# ---
# Skip_same_subscription example
# ---

# Skip the purchase when the contact already owns the offer
import requests

url = 'https://api.kwiga.com/contacts/purchases'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'email': 'bond@example.com',
    'offer_id': 18,
    'skip_same_subscription': true,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/purchases",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "first_name": "James",
    "last_name": "Bond",
    "email": "bond@example.com",
    "send_activation_email": true,
    "phone": "+380931234567",
    "offer_id": 18,
    "additional_fields": [
      {
        "field_id": 17,
        "value": "this is test value"
      }
    ]
  }
}


# ---
# Idempotent example
# ---

{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/purchases",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Idempotency-Key": "purchase-crm-deal-4623778",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "email": "bond@example.com",
    "offer_id": 18
  }
}


# ---
# Skip_same_subscription example
# ---

{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/purchases",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "email": "bond@example.com",
    "offer_id": 18,
    "skip_same_subscription": true
  }
}

Приклад відповіді:

{
    "data": {
        "id": 132,
        "email": "bond@example.com",
        "first_name": "James",
        "last_name": "Bond",
        "phone_number": "931234567",
        "tags": [],
        "created_at": "2023-06-26T19:01:00.000000Z",
        "additional_fields": [
            {
                "id": 110,
                "field": {
                    "id": 17,
                    "title": "test global",
                    "is_local": false
                },
                "value": "this is test value"
            }
        ],
        "orders": [
            {
                "id": 1060,
                "type_id": 1,
                "first_paid_at": "2023-10-13T18:04:02.000000Z",
                "paid_at": "2023-10-13T18:04:02.000000Z",
                "created_at": "2023-10-13T18:04:02.000000Z",
                "updated_at": "2023-10-13T18:04:02.000000Z",
                "products": [
                    {
                        "id": 25,
                        "productable_id": 10,
                        "productable_type": "course",
                        "title": "test course",
                        "url": "https://sample-school.kwiga.com/courses/test-course"
                    }
                ],
                "payments": [
                    {
                        "id": 1231,
                        "status": 2,
                        "status_title": "Paid",
                        "payment_type": null,
                        "payment_type_title": null,
                        "payment_form": null,
                        "payment_form_title": null,
                        "price_info": {
                            "amount": 0,
                            "currency": {
                                "id": 147,
                                "code": "UAH",
                                "html_code": "₴",
                                "html_letter_code": "грн"
                            }
                        },
                        "paid_at": "2023-10-13T18:04:02.000000Z",
                        "created_at": "2023-10-13T18:04:02.000000Z",
                        "updated_at": "2023-10-13T18:04:02.000000Z",
                        "transactions": [
                            {
                                "id": 28,
                                "merchant_id": 2,
                                "payment_id": 1231,
                                "order_id": 1060,
                                "payment_system_status": "Declined",
                                "failure_reason": "Cardholder session expired",
                                "price": "147",
                                "currency_code": "UAH",
                                "payer_account": null,
                                "card_mask": null,
                                "rrn": null,
                                "fee": null,
                                "created_at": "2023-11-03T08:45:10.000000Z"
                            },
                            {
                                "id": 29,
                                "merchant_id": 2,
                                "payment_id": 1231,
                                "order_id": 1060,
                                "payment_system_status": "Approved",
                                "failure_reason": null,
                                "price": "147",
                                "currency_code": "UAH",
                                "payer_account": "JKNNASWTZ99SJ",
                                "card_mask": "53****2144",
                                "rrn": 330710335365,
                                "fee": null,
                                "created_at": "2023-11-03T08:48:10.000000Z"
                            }
                        ]
                    }
                ],
                "paid_status": "paid",
                "paid_status_title": "Сплачено",
                "order_stage": {
                    "id": 43,
                    "title": "Стейдж 1",
                    "order_group": {
                        "id": 22,
                        "slug": "voronka-1",
                        "title": null,
                        "order": 4,
                        "created_at": "2023-06-22T18:20:53.000000Z"
                    },
                    "order_funnel": {
                        "id": 1,
                        "title": "Воронка за замовчуванням",
                        "created_at": "2023-05-26T09:47:16.000000Z"
                    },
                    "created_at": "2023-06-22T18:21:10.000000Z"
                },
                "cost_info": {
                    "amount": 0,
                    "currency": {
                        "id": 147,
                        "code": "UAH",
                        "html_code": "₴",
                        "html_letter_code": "грн"
                    }
                },
                "managers": [
                    {
                        "id": 264,
                        "name": "test",
                        "email": "testfsdf@fdafasd.fsd"
                    },
                    {
                        "id": 288,
                        "name": "fdsf",
                        "email": "sdfs@fdf.dfss"
                    }
                ]
            }
        ]
    },
    "order": {
        "id": 1060,
        "type_id": 1,
        "first_paid_at": "2023-10-13T18:04:02.000000Z",
        "paid_at": "2023-10-13T18:04:02.000000Z",
        "created_at": "2023-10-13T18:04:02.000000Z",
        "updated_at": "2023-10-13T18:04:02.000000Z",
        "products": [
            {
                "id": 25,
                "productable_id": 10,
                "productable_type": "course",
                "title": "test course",
                "url": "https://sample-school.kwiga.com/courses/test-course"
            }
        ],
        "payments": [
            {
                "id": 1231,
                "status": 2,
                "status_title": "Paid",
                "payment_type": null,
                "payment_type_title": null,
                "payment_form": null,
                "payment_form_title": null,
                "price_info": {
                    "amount": 0,
                    "currency": {
                        "id": 147,
                        "code": "UAH",
                        "html_code": "₴",
                        "html_letter_code": "грн"
                    }
                },
                "paid_at": "2023-10-13T18:04:02.000000Z",
                "created_at": "2023-10-13T18:04:02.000000Z",
                "updated_at": "2023-10-13T18:04:02.000000Z",
                "transactions": [
                    {
                        "id": 28,
                        "merchant_id": 2,
                        "payment_id": 1231,
                        "order_id": 1060,
                        "payment_system_status": "Declined",
                        "failure_reason": "Cardholder session expired",
                        "price": "147",
                        "currency_code": "UAH",
                        "payer_account": null,
                        "card_mask": null,
                        "rrn": null,
                        "fee": null,
                        "created_at": "2023-11-03T08:45:10.000000Z"
                    },
                    {
                        "id": 29,
                        "merchant_id": 2,
                        "payment_id": 1231,
                        "order_id": 1060,
                        "payment_system_status": "Approved",
                        "failure_reason": null,
                        "price": "147",
                        "currency_code": "UAH",
                        "payer_account": "JKNNASWTZ99SJ",
                        "card_mask": "53****2144",
                        "rrn": 330710335365,
                        "fee": null,
                        "created_at": "2023-11-03T08:48:10.000000Z"
                    }
                ]
            }
        ],
        "paid_status": "paid",
        "paid_status_title": "Сплачено",
        "order_stage": {
            "id": 43,
            "title": "Стейдж 1",
            "order_group": {
                "id": 22,
                "slug": "voronka-1",
                "title": null,
                "order": 4,
                "created_at": "2023-06-22T18:20:53.000000Z"
            },
            "order_funnel": {
                "id": 1,
                "title": "Воронка за замовчуванням",
                "created_at": "2023-05-26T09:47:16.000000Z"
            },
            "created_at": "2023-06-22T18:21:10.000000Z"
        },
        "cost_info": {
            "amount": 0,
            "currency": {
                "id": 147,
                "code": "UAH",
                "html_code": "₴",
                "html_letter_code": "грн"
            }
        },
        "managers": [
            {
                "id": 264,
                "name": "test",
                "email": "testfsdf@fdafasd.fsd"
            },
            {
                "id": 288,
                "name": "fdsf",
                "email": "sdfs@fdf.dfss"
            }
        ],
        "crm_url": "https://sample-school.kwiga.com/expert/crm/orders/1060",
        "offers": [
            {
                "id": 18,
                "unique_offer_code": "full-course",
                "title": "Full course access",
                "price_info": {
                    "amount": 1200,
                    "currency": {
                        "id": 147,
                        "code": "UAH",
                        "html_code": "₴"
                    }
                }
            }
        ]
    }
}

POST https://api.kwiga.com/contacts/purchases

Request

email string required
Email - має бути унікальним у рамках кабінету
phone string optional
Номер телефону — формат: +{код країни}{телефон}
first_name string optional
Ім'я контакту
middle_name string optional
По-батькові контакту (або middle name)
last_name string optional
Прізвище контакту
name string optional
Повне ім'я одним рядком. Використовуйте, якщо у вас немає розбивки на ім'я/по-батькові/прізвище — сервер збереже значення як ім'я контакту.
tags string[] optional
Теги
send_activation_email boolean optional
Надіслати вітальний лист
Default: false
send_product_access_email boolean optional
Надіслати лист про доступ до продукту
Default: false
send_payment_success_email boolean optional
Надіслати лист про успішну оплату пропозиції
Default: false
locale string optional
Мова контакту в форматі iso_2. Повний список значень див. у блоці локалей, які підтримуються.
Default: en
offer_id integer optional
Ідентифікатор пропозиції — береться з кінця посилання сторінки редагування пропозиції (наприклад, https://sample-school.kwiga.com/expert/payments/offers/edit/3858). Якщо не переданий — запит звертається до поля product_ids.
product_ids integer[] optional
Масив id продуктів.
Якщо offer_id і product_ids відсутні, то буде створено пусте замовлення
order_stage_id integer optional
Ідентифікатор статусу замовлення у воронці (можна отримати на сторінці CRM → Замовлення → Налаштування → Список статусів)
is_paid boolean optional
Позначає створене замовлення як сплачене.
Default: true
skip_same_subscription boolean optional
Не видавати повторно те, чим контакт уже володіє. Володінням вважається сплачений доступ, термін якого ще не минув. За offer_id покупка пропускається повністю; з product_ids прибираються вже видані продукти, і якщо не лишилося жодного — замовлення не створюється. Коли замовлення не створено, поля order у відповіді немає — сам контакт повертається як завжди, з актуальним складом продуктів. За замовчуванням вимкнено, тому продовження й повторні покупки працюють як раніше.
Default: false
manager_ids integer[] optional
Менеджери замовлення (можна отримати на сторінці Налаштування → Доступи по управлінню)
comment string optional
Коментар до замовлення. Max: 5000
Max length: 5000 characters
additional_fields object[] optional
Поля користувача (кастомні поля)
field_id integer optional
id кастомного поля (можна отримати в CRM → Контакти → Налаштування → Додавання полів користувача)
value string optional
Значення кастомного поля

Структура відповіді

Вміст відповіді.
Замовлення, створене цим викликом — зокрема «порожнє» замовлення, якщо не передано ні offer_id, ні product_ids. Відсутнє, коли виклик замовлення не створив: за skip_same_subscription: true контакт уже володів тим, що просили видати; за product_ids разом із is_paid: false оформлюється передзапис без замовлення; у контакта немає прив'язаного користувача. У повторі за Idempotency-Key замовлення теж немає — операція не виконувалась, це видно за header'ом Idempotent-Replayed.

Теги

Додавання тегів контактам

Приклад запиту:

<?php
// Add tags to contacts

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'contacts' => [23698],
        'tags' => ["test-tag"],
    ],
];

$response = $client->request('POST', '/contacts/tags', $options);

$result = json_decode($response->getBody());
?>
<?php
// Add tags to contacts

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/tags');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'contacts' => [23698],
    'tags' => ["test-tag"],
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts/tags' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"contacts":[23698],"tags":["test-tag"]}'
// Add tags to contacts
const url = 'https://api.kwiga.com/contacts/tags';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'contacts': [23698],
    'tags': ["test-tag"]
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Add tags to contacts
import requests

url = 'https://api.kwiga.com/contacts/tags'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'contacts': [23698],
    'tags': ["test-tag"],
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/tags",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "contacts": [
      23698
    ],
    "tags": [
      "test-tag"
    ]
  }
}

Приклад відповіді:

{
    "success": true
}

POST https://api.kwiga.com/contacts/tags

Request

contact_ids integer/integer[] optional
Фільтр за id контактів. Приймає рядок через кому або повторюваний масив цілих чисел.
contacts integer[] optional
Legacy-аліас для contact_ids. Збережений заради зворотної сумісності; у нових інтеграціях використовуйте contact_ids.
tags string[] required
Теги

Структура відповіді

Тіло відповіді повертається без обгортки.

Видалення тегів контактів

Приклад запиту:

<?php
// Remove tags from contacts

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'contacts' => [23698],
        'tags' => ["test-tag"],
    ],
];

$response = $client->request('DELETE', '/contacts/tags', $options);

$result = json_decode($response->getBody());
?>
<?php
// Remove tags from contacts

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/tags');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');

$data = [
    'contacts' => [23698],
    'tags' => ["test-tag"],
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request DELETE 'https://api.kwiga.com/contacts/tags' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"contacts":[23698],"tags":["test-tag"]}'
// Remove tags from contacts
const url = 'https://api.kwiga.com/contacts/tags';

const options = {
  method: 'DELETE',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'contacts': [23698],
    'tags': ["test-tag"]
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Remove tags from contacts
import requests

url = 'https://api.kwiga.com/contacts/tags'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'contacts': [23698],
    'tags': ["test-tag"],
}

response = requests.delete(url, headers=headers, json=data)

result = response.json()
{
  "method": "DELETE",
  "url": "https://api.kwiga.com/contacts/tags",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "contacts": [
      23698
    ],
    "tags": [
      "test-tag"
    ]
  }
}

Приклад відповіді:

{
    "success": true
}

DELETE https://api.kwiga.com/contacts/tags

Request

contact_ids integer/integer[] optional
Фільтр за id контактів. Приймає рядок через кому або повторюваний масив цілих чисел.
contacts integer[] optional
Legacy-аліас для contact_ids. Збережений заради зворотної сумісності; у нових інтеграціях використовуйте contact_ids.
tags string[] required
Теги

Структура відповіді

Тіло відповіді повертається без обгортки.

Продукти та підписки

Список продуктів контакту

Приклад запиту:

<?php
// Get contact products

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts/:contact/products', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get contact products

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/products');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/contacts/:contact/products' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get contact products
const url = 'https://api.kwiga.com/contacts/:contact/products';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get contact products
import requests

url = 'https://api.kwiga.com/contacts/:contact/products'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts/:contact/products",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 299,
      "productable_id": 142,
      "productable_type": "course",
      "title": "Copy Test backup",
      "image_url": "",
      "is_published": false,
      "aggregated_subscription": {
        "is_active": true,
        "is_paid": true,
        "start_at": "2025-05-05T15:09:04.000000Z",
        "end_at": null,
        "offer_end_at": null,
        "order_end_at": null,
        "count_available_days": 39,
        "count_left_days": null,
        "state": {
          "id": 2,
          "name": "Open",
          "title": "Open"
        }
      },
      "subscriptions": [
        {
          "id": 4775,
          "creator_id": 24457,
          "user_id": 24457,
          "product_id": 299,
          "order_id": 2710,
          "offer_id": 757,
          "is_active": true,
          "start_at": "2025-05-05T15:09:04.000000Z",
          "order_end_at": null,
          "end_at": null,
          "paid_at": "2025-05-05T15:09:04.000000Z",
          "created_at": "2025-05-05T15:09:04.000000Z",
          "updated_at": "2025-05-05T15:09:04.000000Z"
        }
      ]
    }
  ]
}

GET https://api.kwiga.com/contacts/:contact/products

Повертає список продуктів контакту з інформацією про підписки та терміни доступу. Параметр aggregated_subscription містить зведену інформацію про терміни та активність доступу на основі всіх підписок користувача по продукту. Параметр subscriptions містить масив всіх підписок користувача по продукту.

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Видалити продукт у контакта

Приклад запиту:

<?php
// Delete contact product

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('DELETE', '/contacts/:contact/products/:product', $options);

$result = json_decode($response->getBody());
?>
<?php
// Delete contact product

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/products/:product');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request DELETE 'https://api.kwiga.com/contacts/:contact/products/:product' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Delete contact product
const url = 'https://api.kwiga.com/contacts/:contact/products/:product';

const options = {
  method: 'DELETE',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Delete contact product
import requests

url = 'https://api.kwiga.com/contacts/:contact/products/:product'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.delete(url, headers=headers)

result = response.json()
{
  "method": "DELETE",
  "url": "https://api.kwiga.com/contacts/:contact/products/:product",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "success": true
}

DELETE https://api.kwiga.com/contacts/:contact/products/:product

Видаляє підписки контакту по заданому продукту. Замовлення стають кастомними без доступу до видалених продуктів.

Структура відповіді

Тіло відповіді повертається без обгортки.

Видалити продукти у контакта (масово)

Приклад запиту:

<?php
// Delete contact products in bulk

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'email' => 'user@example.com',
        'product_id' => 299,
        'offers' => [421],
    ],
];

$response = $client->request('DELETE', '/contacts/products', $options);

$result = json_decode($response->getBody());
?>
<?php
// Delete contact products in bulk

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/products');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');

$data = [
    'email' => 'user@example.com',
    'product_id' => 299,
    'offers' => [421],
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request DELETE 'https://api.kwiga.com/contacts/products' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"email":"user@example.com","product_id":299,"offers":[421]}'
// Delete contact products in bulk
const url = 'https://api.kwiga.com/contacts/products';

const options = {
  method: 'DELETE',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'email': 'user@example.com',
    'product_id': 299,
    'offers': [421]
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Delete contact products in bulk
import requests

url = 'https://api.kwiga.com/contacts/products'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'email': 'user@example.com',
    'product_id': 299,
    'offers': [421],
}

response = requests.delete(url, headers=headers, json=data)

result = response.json()
{
  "method": "DELETE",
  "url": "https://api.kwiga.com/contacts/products",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "email": "user@example.com",
    "product_id": 299,
    "offers": [
      421
    ]
  }
}

Приклад відповіді:

{
  "data": {
    "affected_subscriptions": [
      2309,
      2661
    ],
    "affected_orders": [
      1578,
      1784
    ],
    "affected_by_offers": {
      "421": {
        "affected_subscriptions": [
          2309,
          2661
        ],
        "affected_orders": [
          1578,
          1784
        ]
      }
    },
    "affected_by_products": []
  },
  "contact_id": 125
}

DELETE https://api.kwiga.com/contacts/products

Видаляє підписки за заданими умовами, а замовлення стають кастомними без доступу до видалених продуктів. Можна комбінувати параметри product_id та offers для видалення частини продуктів по певному офферу.

Parameters

email string optional
Email контакту. Обов'язковий якщо contact_id не вказаний
contact_id integer optional
Id контакту. Обов'язковий якщо email не вказаний
product_id integer optional
Id продукту. Обов'язковий якщо offers не вказаний. Можна отримати наприклад в ендпоінті Список продуктів контакту або в Списку курсів (це буде поле product_id в курсі)
offers integer[] optional
Масив з id пропозицій по яких видаляємо підписки. Обов'язковий якщо product_id не вказаний. Можна отримати в адресному рядку редагування пропозиції або в апі в списку пропозицій контакту.

Структура відповіді

Вміст відповіді.
contact_id integer
ID of the contact whose subscriptions were affected

Заморозити підписку контакту

Приклад запиту:

<?php
// Freeze the subscription for 30 days

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'count_frozen_days' => 30,
    ],
];

$response = $client->request('POST', '/contacts/:contact/products/:product/freeze', $options);

$result = json_decode($response->getBody());
?>
<?php
// Freeze the subscription for 30 days

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/products/:product/freeze');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'count_frozen_days' => 30,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts/:contact/products/:product/freeze' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"count_frozen_days":30}'
// Freeze the subscription for 30 days
const url = 'https://api.kwiga.com/contacts/:contact/products/:product/freeze';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'count_frozen_days': 30
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Freeze the subscription for 30 days
import requests

url = 'https://api.kwiga.com/contacts/:contact/products/:product/freeze'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'count_frozen_days': 30,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/products/:product/freeze",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "count_frozen_days": 30
  }
}

Приклад відповіді:

{
  "data": {
    "id": 299,
    "productable_id": 142,
    "productable_type": "course",
    "title": "JS Foundations",
    "image_url": "https://cdn.kwiga.com/preview.jpg",
    "url": "https://kwiga.com/courses/js-foundations",
    "is_published": true,
    "aggregated_subscription": {
      "is_active": false,
      "is_paid": true,
      "start_at": "2026-06-05T00:00:00.000000Z",
      "end_at": "2026-08-04T00:00:00.000000Z",
      "offer_end_at": null,
      "order_end_at": "2026-08-04T00:00:00.000000Z",
      "frozen_at": "2026-05-20T10:00:00.000000Z",
      "extended_at": null,
      "count_available_days": 60,
      "count_left_days": 60,
      "state": {
        "id": 4,
        "name": "Frozen",
        "title": "Frozen"
      }
    },
    "subscriptions": [
      {
        "id": 4775,
        "creator_id": 24457,
        "user_id": 24457,
        "product_id": 299,
        "order_id": 2710,
        "offer_id": 757,
        "is_active": false,
        "start_at": "2026-06-05T00:00:00.000000Z",
        "order_end_at": "2026-08-04T00:00:00.000000Z",
        "end_at": "2026-08-04T00:00:00.000000Z",
        "frozen_at": "2026-05-20T10:00:00.000000Z",
        "extended_at": null,
        "paid_at": "2026-05-05T15:09:04.000000Z",
        "created_at": "2026-05-05T15:09:04.000000Z",
        "updated_at": "2026-05-06T12:42:11.000000Z"
      }
    ]
  }
}

POST https://api.kwiga.com/contacts/:contact/products/:product/freeze

Заморожує всі активні підписки контакту на цьому продукті на count_frozen_days календарних днів. Заморозка зсуває start_at / end_at / order_end_at уперед, робить підписку неактивною і повідомляє учня. При розморожуванні (ручному або автоматичному після закінчення терміну) start_at повертається назад на основі даних замовлення учня, а не просто відкочується на ту ж дельту.

URL Parameters

count_frozen_days integer required
На скільки днів заморозити підписку (1–400).
Range: 1 – 400

Структура відповіді

Вміст відповіді.

Розморозити підписку контакту

Приклад запиту:

<?php
// Unfreeze the subscription (no body)

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('POST', '/contacts/:contact/products/:product/unfreeze', $options);

$result = json_decode($response->getBody());
?>
<?php
// Unfreeze the subscription (no body)

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/products/:product/unfreeze');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts/:contact/products/:product/unfreeze' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Unfreeze the subscription (no body)
const url = 'https://api.kwiga.com/contacts/:contact/products/:product/unfreeze';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Unfreeze the subscription (no body)
import requests

url = 'https://api.kwiga.com/contacts/:contact/products/:product/unfreeze'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.post(url, headers=headers)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/products/:product/unfreeze",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": {
    "id": 299,
    "productable_id": 142,
    "productable_type": "course",
    "title": "JS Foundations",
    "image_url": "https://cdn.kwiga.com/preview.jpg",
    "url": "https://kwiga.com/courses/js-foundations",
    "is_published": true,
    "aggregated_subscription": {
      "is_active": false,
      "is_paid": true,
      "start_at": "2026-06-05T00:00:00.000000Z",
      "end_at": "2026-08-04T00:00:00.000000Z",
      "offer_end_at": null,
      "order_end_at": "2026-08-04T00:00:00.000000Z",
      "frozen_at": "2026-05-20T10:00:00.000000Z",
      "extended_at": null,
      "count_available_days": 60,
      "count_left_days": 60,
      "state": {
        "id": 4,
        "name": "Frozen",
        "title": "Frozen"
      }
    },
    "subscriptions": [
      {
        "id": 4775,
        "creator_id": 24457,
        "user_id": 24457,
        "product_id": 299,
        "order_id": 2710,
        "offer_id": 757,
        "is_active": false,
        "start_at": "2026-06-05T00:00:00.000000Z",
        "order_end_at": "2026-08-04T00:00:00.000000Z",
        "end_at": "2026-08-04T00:00:00.000000Z",
        "frozen_at": "2026-05-20T10:00:00.000000Z",
        "extended_at": null,
        "paid_at": "2026-05-05T15:09:04.000000Z",
        "created_at": "2026-05-05T15:09:04.000000Z",
        "updated_at": "2026-05-06T12:42:11.000000Z"
      }
    ]
  }
}

POST https://api.kwiga.com/contacts/:contact/products/:product/unfreeze

Розморожує всі заморожені підписки контакту на цьому продукті. Відновлює start_at, перераховує end_at / order_end_at (віднімає невикористаний залишок заморозки), активує доступ, якщо він не закінчився, і повідомляє учня. Тіло порожнє.

Структура відповіді

Вміст відповіді.

Продовжити підписку контакту

Приклад запиту:

<?php
// Extend access by 14 days

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'count_extend_days' => 14,
    ],
];

$response = $client->request('POST', '/contacts/:contact/products/:product/extend', $options);

$result = json_decode($response->getBody());
?>
<?php
// Extend access by 14 days

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/products/:product/extend');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'count_extend_days' => 14,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts/:contact/products/:product/extend' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"count_extend_days":14}'
// Extend access by 14 days
const url = 'https://api.kwiga.com/contacts/:contact/products/:product/extend';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'count_extend_days': 14
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Extend access by 14 days
import requests

url = 'https://api.kwiga.com/contacts/:contact/products/:product/extend'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'count_extend_days': 14,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/products/:product/extend",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "count_extend_days": 14
  }
}

Приклад відповіді:

{
  "data": {
    "id": 299,
    "productable_id": 142,
    "productable_type": "course",
    "title": "JS Foundations",
    "image_url": "https://cdn.kwiga.com/preview.jpg",
    "url": "https://kwiga.com/courses/js-foundations",
    "is_published": true,
    "aggregated_subscription": {
      "is_active": false,
      "is_paid": true,
      "start_at": "2026-06-05T00:00:00.000000Z",
      "end_at": "2026-08-04T00:00:00.000000Z",
      "offer_end_at": null,
      "order_end_at": "2026-08-04T00:00:00.000000Z",
      "frozen_at": "2026-05-20T10:00:00.000000Z",
      "extended_at": null,
      "count_available_days": 60,
      "count_left_days": 60,
      "state": {
        "id": 4,
        "name": "Frozen",
        "title": "Frozen"
      }
    },
    "subscriptions": [
      {
        "id": 4775,
        "creator_id": 24457,
        "user_id": 24457,
        "product_id": 299,
        "order_id": 2710,
        "offer_id": 757,
        "is_active": false,
        "start_at": "2026-06-05T00:00:00.000000Z",
        "order_end_at": "2026-08-04T00:00:00.000000Z",
        "end_at": "2026-08-04T00:00:00.000000Z",
        "frozen_at": "2026-05-20T10:00:00.000000Z",
        "extended_at": null,
        "paid_at": "2026-05-05T15:09:04.000000Z",
        "created_at": "2026-05-05T15:09:04.000000Z",
        "updated_at": "2026-05-06T12:42:11.000000Z"
      }
    ]
  }
}

POST https://api.kwiga.com/contacts/:contact/products/:product/extend

Додає count_extend_days календарних днів до end_at / order_end_at усіх підписок контакту на цьому продукті. Той же ендпоінт використовується CRM-автоматизаціями для реактивації прострочених підписок.

URL Parameters

count_extend_days integer required
На скільки днів продовжити підписку (1–400).
Range: 1 – 400

Структура відповіді

Вміст відповіді.

Змінити дату закінчення підписки

Приклад запиту:

<?php
// Set the subscription end to the given date in the chosen timezone

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'end_at' => '2027-01-01 23:59:59',
        'timezone_id' => 1,
    ],
];

$response = $client->request('PUT', '/contacts/:contact/products/:product/end-date', $options);

$result = json_decode($response->getBody());
?>
<?php
// Set the subscription end to the given date in the chosen timezone

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/products/:product/end-date');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');

$data = [
    'end_at' => '2027-01-01 23:59:59',
    'timezone_id' => 1,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request PUT 'https://api.kwiga.com/contacts/:contact/products/:product/end-date' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"end_at":"2027-01-01 23:59:59","timezone_id":1}'
// Set the subscription end to the given date in the chosen timezone
const url = 'https://api.kwiga.com/contacts/:contact/products/:product/end-date';

const options = {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'end_at': '2027-01-01 23:59:59',
    'timezone_id': 1
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Set the subscription end to the given date in the chosen timezone
import requests

url = 'https://api.kwiga.com/contacts/:contact/products/:product/end-date'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'end_at': '2027-01-01 23:59:59',
    'timezone_id': 1,
}

response = requests.put(url, headers=headers, json=data)

result = response.json()
{
  "method": "PUT",
  "url": "https://api.kwiga.com/contacts/:contact/products/:product/end-date",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "end_at": "2027-01-01 23:59:59",
    "timezone_id": 1
  }
}

Приклад відповіді:

{
  "data": {
    "id": 299,
    "productable_id": 142,
    "productable_type": "course",
    "title": "JS Foundations",
    "image_url": "https://cdn.kwiga.com/preview.jpg",
    "url": "https://kwiga.com/courses/js-foundations",
    "is_published": true,
    "aggregated_subscription": {
      "is_active": false,
      "is_paid": true,
      "start_at": "2026-06-05T00:00:00.000000Z",
      "end_at": "2026-08-04T00:00:00.000000Z",
      "offer_end_at": null,
      "order_end_at": "2026-08-04T00:00:00.000000Z",
      "frozen_at": "2026-05-20T10:00:00.000000Z",
      "extended_at": null,
      "count_available_days": 60,
      "count_left_days": 60,
      "state": {
        "id": 4,
        "name": "Frozen",
        "title": "Frozen"
      }
    },
    "subscriptions": [
      {
        "id": 4775,
        "creator_id": 24457,
        "user_id": 24457,
        "product_id": 299,
        "order_id": 2710,
        "offer_id": 757,
        "is_active": false,
        "start_at": "2026-06-05T00:00:00.000000Z",
        "order_end_at": "2026-08-04T00:00:00.000000Z",
        "end_at": "2026-08-04T00:00:00.000000Z",
        "frozen_at": "2026-05-20T10:00:00.000000Z",
        "extended_at": null,
        "paid_at": "2026-05-05T15:09:04.000000Z",
        "created_at": "2026-05-05T15:09:04.000000Z",
        "updated_at": "2026-05-06T12:42:11.000000Z"
      }
    ]
  }
}

PUT https://api.kwiga.com/contacts/:contact/products/:product/end-date

Перезаписує end_atorder_end_at, якщо вона є) явною датою. Дата інтерпретується у таймзоні timezone_id (див. ендпоінт списку таймзон); якщо timezone_id не вказано, end_at трактується як UTC.

URL Parameters

end_at datetime required
Нова дата закінчення підписки. Інтерпретується у таймзоні кабінету, зазначеній в timezone_id.
timezone_id integer optional
ID таймзони з ендпоінта списку таймзон. Необов'язковий — якщо не вказано, end_at трактується як UTC.

Структура відповіді

Вміст відповіді.

Бали (нагороди)

Журнал балів контакту

Приклад запиту:

<?php
// List rewards (default ordering)

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts/:contact/rewards', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Only accruals from quiz-passed and manual reasons, scoped to two products

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/contacts/:contact/rewards?filters[accrual_type]=accrued&filters[reason_types]=1,6&filters[product_ids]=10,11&per_page=50', $options);

$result = json_decode($response->getBody());
?>
<?php
// List rewards (default ordering)

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Only accruals from quiz-passed and manual reasons, scoped to two products

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards?filters[accrual_type]=accrued&filters[reason_types]=1,6&filters[product_ids]=10,11&per_page=50');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/contacts/:contact/rewards' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/contacts/:contact/rewards?filters[accrual_type]=accrued&filters[reason_types]=1,6&filters[product_ids]=10,11&per_page=50' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// List rewards (default ordering)
const url = 'https://api.kwiga.com/contacts/:contact/rewards';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Only accruals from quiz-passed and manual reasons, scoped to two products
const url = 'https://api.kwiga.com/contacts/:contact/rewards?filters[accrual_type]=accrued&filters[reason_types]=1,6&filters[product_ids]=10,11&per_page=50';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# List rewards (default ordering)
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Only accruals from quiz-passed and manual reasons, scoped to two products
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards?filters[accrual_type]=accrued&filters[reason_types]=1,6&filters[product_ids]=10,11&per_page=50'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts/:contact/rewards",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/contacts/:contact/rewards?filters[accrual_type]=accrued&filters[reason_types]=1,6&filters[product_ids]=10,11&per_page=50",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 9821,
      "points": 50,
      "accrual_type": {
        "id": "accrued",
        "slug": "Plus",
        "title": "Accrued"
      },
      "reason_type": {
        "id": 6,
        "slug": "Manual",
        "title": "Manual"
      },
      "event": null,
      "event_name": null,
      "eventable_type": null,
      "eventable_id": null,
      "event_comment": null,
      "manual_comment": "Bonus for active chat participation",
      "is_visible_to_student": true,
      "message": "Manual points accrual: Bonus for active chat participation",
      "product": null,
      "creator": {
        "id": 17,
        "name": "Alice Curator",
        "email": "alice@kwiga.com"
      },
      "created_at": "2026-05-24T11:14:51.000000Z"
    },
    {
      "id": 9820,
      "points": 25,
      "accrual_type": {
        "id": "accrued",
        "slug": "Plus",
        "title": "Accrued"
      },
      "reason_type": {
        "id": 1,
        "slug": "QuizPassed",
        "title": "Quiz passed"
      },
      "event": "quiz.passed",
      "event_name": "Quiz passed",
      "eventable_type": "quiz_attempt",
      "eventable_id": 4821,
      "event_comment": null,
      "manual_comment": null,
      "is_visible_to_student": null,
      "message": "Quiz Module 3 final test in lesson Closures & scope -> Attempt",
      "product": {
        "id": 431,
        "productable_type": "course",
        "productable_id": 226,
        "name": "JS Foundations",
        "url": "https://lm4.kwiga.com/courses/js-foundations"
      },
      "creator": null,
      "created_at": "2026-05-23T11:14:51.000000Z"
    },
    {
      "id": 9810,
      "points": -30,
      "accrual_type": {
        "id": "deducted",
        "slug": "Minus",
        "title": "Deducted"
      },
      "reason_type": {
        "id": 6,
        "slug": "Manual",
        "title": "Manual"
      },
      "event": null,
      "event_name": null,
      "eventable_type": null,
      "eventable_id": null,
      "event_comment": null,
      "manual_comment": null,
      "is_visible_to_student": null,
      "message": "Manual points accrual",
      "product": null,
      "creator": {
        "id": 17,
        "name": "Alice Curator",
        "email": "alice@kwiga.com"
      },
      "created_at": "2026-05-22T09:00:00.000000Z"
    }
  ],
  "links": {
    "first": "https://api.kwiga.com/contacts/142/rewards?page=1",
    "last": "https://api.kwiga.com/contacts/142/rewards?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://api.kwiga.com/contacts/142/rewards",
    "per_page": 15,
    "to": 3,
    "total": 3
  },
  "sum_points": 45,
  "sum_accrued_points": 75,
  "sum_deducted_points": -30
}

GET https://api.kwiga.com/contacts/:contact/rewards

Повертає журнал нарахувань і списань балів контакту — за квізами, замовленнями, подарунками, автоматизаціями і ручними діями кураторів. На верхньому рівні відповіді також повертаються суми sum_points, sum_accrued_points, sum_deducted_points за поточним фільтром.

URL Parameters

filters object optional
translation missing: ua.contacts.params.rewards_filters
product_ids integer/integer[] optional
Фільтр за id продуктів, до яких прив'язані бали.
accrual_type string optional
Фільтр за напрямком операції: accrued (додатні бали) або deducted (від'ємні).
Example: accruedPossible values: accrued, deducted
reason_types integer/integer[] optional

Фільтр за id причини нарахування. Кілька значень об'єднуються «або».

  • 1 — Практика пройдена
  • 2 — Скасування результатів практики
  • 3 — Скидання балів за спробу
  • 4 — Нові бали за спробу
  • 5 — Оплата балами
  • 6 — Вручну
  • 7 — Автоматизація
  • 8 — Змінено бали за спробу
  • 9 — Оплата балами за подарунок
Possible values: 1, 2, 3, 4, 5, 6, 7, 8, 9

per_page integer optional
Кількість елементів на сторінці.
Maximum: 500Default: 15
page integer optional
Номер сторінки.
Default: 1

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).
sum_points number
Чистий підсумок балів за поточним фільтром (нарахування мінус списання).
Сума додатних записів (тільки нарахування) за поточним фільтром.
Сума від'ємних записів (тільки списання) за поточним фільтром.

Ручне нарахування/списання балів контакту Ідемпотентний

Приклад запиту:

<?php
// Accrue 50 points for finishing a milestone, unbound from any product

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'points' => 50,
    ],
];

$response = $client->request('POST', '/contacts/:contact/rewards', $options);

$result = json_decode($response->getBody());
?>


# ---
# Accrue_with_product example
# ---

<?php
// Accrue 100 points scoped to product id 10

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'points' => 100,
        'product_id' => 10,
    ],
];

$response = $client->request('POST', '/contacts/:contact/rewards', $options);

$result = json_decode($response->getBody());
?>


# ---
# With_comment example
# ---

<?php
// Accrue 50 points with a curator comment that the student also sees in their journal

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'points' => 50,
        'comment' => 'Bonus for active chat participation',
        'is_visible_to_student' => true,
    ],
];

$response = $client->request('POST', '/contacts/:contact/rewards', $options);

$result = json_decode($response->getBody());
?>


# ---
# Idempotent_accrual example
# ---

<?php
// Safely retry the same accrual — passing Idempotency-Key collapses replays to a single FlowReward

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
        'Idempotency-Key' => 'reward-for-quiz-attempt-4821',
    ],
    'json' => [
        'points' => 25,
    ],
];

$response = $client->request('POST', '/contacts/:contact/rewards', $options);

$result = json_decode($response->getBody());
?>


# ---
# Deduct example
# ---

<?php
// Deduct 30 points

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'points' => -30,
    ],
];

$response = $client->request('POST', '/contacts/:contact/rewards', $options);

$result = json_decode($response->getBody());
?>
<?php
// Accrue 50 points for finishing a milestone, unbound from any product

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'points' => 50,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Accrue_with_product example
# ---

<?php
// Accrue 100 points scoped to product id 10

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'points' => 100,
    'product_id' => 10,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# With_comment example
# ---

<?php
// Accrue 50 points with a curator comment that the student also sees in their journal

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'points' => 50,
    'comment' => 'Bonus for active chat participation',
    'is_visible_to_student' => true,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Idempotent_accrual example
# ---

<?php
// Safely retry the same accrual — passing Idempotency-Key collapses replays to a single FlowReward

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
    'Idempotency-Key: reward-for-quiz-attempt-4821',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'points' => 25,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Deduct example
# ---

<?php
// Deduct 30 points

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/contacts/:contact/rewards');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'points' => -30,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/contacts/:contact/rewards' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"points":50}'


# ---
# Accrue_with_product example
# ---

curl --location --request POST 'https://api.kwiga.com/contacts/:contact/rewards' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"points":100,"product_id":10}'


# ---
# With_comment example
# ---

curl --location --request POST 'https://api.kwiga.com/contacts/:contact/rewards' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"points":50,"comment":"Bonus for active chat participation","is_visible_to_student":true}'


# ---
# Idempotent_accrual example
# ---

curl --location --request POST 'https://api.kwiga.com/contacts/:contact/rewards' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--header 'Idempotency-Key: reward-for-quiz-attempt-4821' \
--data-raw '{"points":25}'


# ---
# Deduct example
# ---

curl --location --request POST 'https://api.kwiga.com/contacts/:contact/rewards' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"points":-30}'
// Accrue 50 points for finishing a milestone, unbound from any product
const url = 'https://api.kwiga.com/contacts/:contact/rewards';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'points': 50
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Accrue_with_product example
# ---

// Accrue 100 points scoped to product id 10
const url = 'https://api.kwiga.com/contacts/:contact/rewards';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'points': 100,
    'product_id': 10
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# With_comment example
# ---

// Accrue 50 points with a curator comment that the student also sees in their journal
const url = 'https://api.kwiga.com/contacts/:contact/rewards';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'points': 50,
    'comment': 'Bonus for active chat participation',
    'is_visible_to_student': true
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Idempotent_accrual example
# ---

// Safely retry the same accrual — passing Idempotency-Key collapses replays to a single FlowReward
const url = 'https://api.kwiga.com/contacts/:contact/rewards';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
    'Idempotency-Key': 'reward-for-quiz-attempt-4821',
  },
  body: JSON.stringify({
    'points': 25
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Deduct example
# ---

// Deduct 30 points
const url = 'https://api.kwiga.com/contacts/:contact/rewards';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'points': -30
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Accrue 50 points for finishing a milestone, unbound from any product
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'points': 50,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()


# ---
# Accrue_with_product example
# ---

# Accrue 100 points scoped to product id 10
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'points': 100,
    'product_id': 10,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()


# ---
# With_comment example
# ---

# Accrue 50 points with a curator comment that the student also sees in their journal
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'points': 50,
    'comment': 'Bonus for active chat participation',
    'is_visible_to_student': true,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()


# ---
# Idempotent_accrual example
# ---

# Safely retry the same accrual — passing Idempotency-Key collapses replays to a single FlowReward
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
    'Idempotency-Key': 'reward-for-quiz-attempt-4821',
}

data = {
    'points': 25,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()


# ---
# Deduct example
# ---

# Deduct 30 points
import requests

url = 'https://api.kwiga.com/contacts/:contact/rewards'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'points': -30,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/rewards",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "points": 50
  }
}


# ---
# Accrue_with_product example
# ---

{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/rewards",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "points": 100,
    "product_id": 10
  }
}


# ---
# With_comment example
# ---

{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/rewards",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "points": 50,
    "comment": "Bonus for active chat participation",
    "is_visible_to_student": true
  }
}


# ---
# Idempotent_accrual example
# ---

{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/rewards",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Idempotency-Key": "reward-for-quiz-attempt-4821",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "points": 25
  }
}


# ---
# Deduct example
# ---

{
  "method": "POST",
  "url": "https://api.kwiga.com/contacts/:contact/rewards",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "points": -30
  }
}

Приклад відповіді:

{
  "data": {
    "id": 9822,
    "points": 50,
    "accrual_type": {
      "id": "accrued",
      "slug": "Plus",
      "title": "Accrued"
    },
    "reason_type": {
      "id": 6,
      "slug": "Manual",
      "title": "Manual"
    },
    "event": null,
    "event_name": null,
    "eventable_type": null,
    "eventable_id": null,
    "event_comment": null,
    "manual_comment": "Bonus for active chat participation",
    "is_visible_to_student": true,
    "message": "Manual points accrual: Bonus for active chat participation",
    "product": null,
    "creator": {
      "id": 17,
      "name": "Alice Curator",
      "email": "alice@kwiga.com"
    },
    "created_at": "2026-05-25T14:30:00.000000Z"
  }
}

POST https://api.kwiga.com/contacts/:contact/rewards

Додає ручний запис у журнал балів контакту. Додатнє points — нарахування, від'ємне — списання. Опціональний product_id прив'язує запис до конкретного продукту; інакше — «кабінет-рівневе». Reason type завжди Manual; куратором фіксується поточний API-користувач.

URL Parameters

points number required
Скільки балів нарахувати. Додатнє значення — нарахування, від'ємне — списання. Запис завжди фіксується з reason_type=Manual.
Range: -1000 – 1000
product_id integer optional
Опціональний id продукту, до якого прив'язати бали. Без нього — нарахування «кабінет-рівневе», без прив'язки до продукту.
comment string optional
Опціональний коментар куратора (до 255 символів). Зберігається разом із записом і дописується до людиночитаного message у відповіді. Учень побачить коментар у своєму журналі балів лише якщо is_visible_to_student=true.
Max length: 255 characters
is_visible_to_student boolean optional
Чи показувати коментар учневі в його власному журналі балів. Куратори (включно з цим публічним API) бачать коментар завжди; прапор керує тільки видимістю на стороні учня.
Default: false

Структура відповіді

Вміст відповіді.

CRM. Замовлення

Оновлення замовлення

Приклад запиту:

<?php
// Move the order to another funnel stage

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'order_stage_id' => 42,
    ],
];

$response = $client->request('PUT', '/orders/:order', $options);

$result = json_decode($response->getBody());
?>


# ---
# Contact example
# ---

<?php
// Reassign the order to another contact

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'contact_id' => 132,
    ],
];

$response = $client->request('PUT', '/orders/:order', $options);

$result = json_decode($response->getBody());
?>


# ---
# Both example
# ---

<?php
// Change stage and contact in one request

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'order_stage_id' => 42,
        'contact_id' => 132,
    ],
];

$response = $client->request('PUT', '/orders/:order', $options);

$result = json_decode($response->getBody());
?>
<?php
// Move the order to another funnel stage

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/orders/:order');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');

$data = [
    'order_stage_id' => 42,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Contact example
# ---

<?php
// Reassign the order to another contact

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/orders/:order');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');

$data = [
    'contact_id' => 132,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Both example
# ---

<?php
// Change stage and contact in one request

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/orders/:order');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');

$data = [
    'order_stage_id' => 42,
    'contact_id' => 132,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request PUT 'https://api.kwiga.com/orders/:order' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"order_stage_id":42}'


# ---
# Contact example
# ---

curl --location --request PUT 'https://api.kwiga.com/orders/:order' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"contact_id":132}'


# ---
# Both example
# ---

curl --location --request PUT 'https://api.kwiga.com/orders/:order' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"order_stage_id":42,"contact_id":132}'
// Move the order to another funnel stage
const url = 'https://api.kwiga.com/orders/:order';

const options = {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'order_stage_id': 42
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Contact example
# ---

// Reassign the order to another contact
const url = 'https://api.kwiga.com/orders/:order';

const options = {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'contact_id': 132
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Both example
# ---

// Change stage and contact in one request
const url = 'https://api.kwiga.com/orders/:order';

const options = {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'order_stage_id': 42,
    'contact_id': 132
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Move the order to another funnel stage
import requests

url = 'https://api.kwiga.com/orders/:order'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'order_stage_id': 42,
}

response = requests.put(url, headers=headers, json=data)

result = response.json()


# ---
# Contact example
# ---

# Reassign the order to another contact
import requests

url = 'https://api.kwiga.com/orders/:order'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'contact_id': 132,
}

response = requests.put(url, headers=headers, json=data)

result = response.json()


# ---
# Both example
# ---

# Change stage and contact in one request
import requests

url = 'https://api.kwiga.com/orders/:order'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'order_stage_id': 42,
    'contact_id': 132,
}

response = requests.put(url, headers=headers, json=data)

result = response.json()
{
  "method": "PUT",
  "url": "https://api.kwiga.com/orders/:order",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "order_stage_id": 42
  }
}


# ---
# Contact example
# ---

{
  "method": "PUT",
  "url": "https://api.kwiga.com/orders/:order",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "contact_id": 132
  }
}


# ---
# Both example
# ---

{
  "method": "PUT",
  "url": "https://api.kwiga.com/orders/:order",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "order_stage_id": 42,
    "contact_id": 132
  }
}

Приклад відповіді:

{
    "data": {
        "id": 5821,
        "type_id": 1,
        "crm_url": "https://sample-school.kwiga.com/expert/payments/orders/5821",
        "first_paid_at": "2026-05-12T10:03:00.000000Z",
        "paid_at": "2026-05-12T10:03:00.000000Z",
        "created_at": "2026-05-10T08:15:00.000000Z",
        "updated_at": "2026-07-17T11:40:00.000000Z",
        "paid_status": 2,
        "paid_status_title": "Paid",
        "order_stage": {
            "id": 42,
            "title": "In progress",
            "order_group": {
                "id": 3,
                "slug": "in-progress",
                "title": "In progress",
                "order": 2,
                "created_at": "2025-01-15T09:00:00.000000Z"
            },
            "order_funnel": {
                "id": 1,
                "title": "Default funnel",
                "created_at": "2025-01-15T09:00:00.000000Z"
            },
            "created_at": "2025-01-15T09:00:00.000000Z"
        },
        "cost_info": {
            "amount": 199.99,
            "amount_rounded": 200,
            "amount_formatted": "$199.99",
            "amount_formatted_code": "199.99 USD",
            "currency": {
                "id": 1,
                "code": "USD",
                "html_code": "$",
                "html_letter_code": "USD"
            }
        }
    }
}

PUT https://api.kwiga.com/orders/:order

Request

order_stage_id integer optional
Ідентифікатор цільової стадії замовлення у воронці (order_stages.id) — переносить замовлення на цю стадію. Обов'язковий, якщо не передано contact_id. Можна отримати на сторінці CRM → Замовлення → Налаштування → Список статусів.
contact_id integer optional
Ідентифікатор контакту (contacts.id), на який переоформлюється замовлення. Обов'язковий, якщо не передано order_stage_id.

Структура відповіді

Продукти

Список продуктів кабінету

Приклад запиту:

<?php
// Get products list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/products', $options);

$result = json_decode($response->getBody());
?>


# ---
# Paginated example
# ---

<?php
// Get products list with pagination and sorting

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/products?per_page=20&page=1&sort_by=asc', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get products list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/products');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Paginated example
# ---

<?php
// Get products list with pagination and sorting

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/products?per_page=20&page=1&sort_by=asc');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/products' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Paginated example
# ---

curl --location --request GET 'https://api.kwiga.com/products?per_page=20&page=1&sort_by=asc' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get products list
const url = 'https://api.kwiga.com/products';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Paginated example
# ---

// Get products list with pagination and sorting
const url = 'https://api.kwiga.com/products?per_page=20&page=1&sort_by=asc';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get products list
import requests

url = 'https://api.kwiga.com/products'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Paginated example
# ---

# Get products list with pagination and sorting
import requests

url = 'https://api.kwiga.com/products?per_page=20&page=1&sort_by=asc'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/products",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Paginated example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/products?per_page=20&page=1&sort_by=asc",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 299,
      "productable_id": 142,
      "productable_type": "course",
      "title": "Sample Course Title",
      "url": "http://test.local/courses/test-slug"
    }
  ],
  "links": {
    "first": "https://api.kwiga.com/products?page=1",
    "last": "https://api.kwiga.com/products?page=5",
    "prev": null,
    "next": "https://api.kwiga.com/products?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "path": "https://api.kwiga.com/products",
    "per_page": 15,
    "to": 15,
    "total": 75
  }
}

GET https://api.kwiga.com/products

Список продуктів кабінету. Є пагінація, за замовчуванням 15.

URL Parameters

sort_by string optional
Possible values: asc, descDefault: desc
per_page integer optional
Кількість елементів на сторінці
Default: 15
page integer optional
Номер сторінки
Default: 1

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Курси

Список курсів кабінету

Приклад запиту:

<?php
// Get courses list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/courses', $options);

$result = json_decode($response->getBody());
?>


# ---
# With_params example
# ---

<?php
// Get courses list with pagination and includes

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/courses?with[]=offers&with[]=description&with[]=program&per_page=15&page=1', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get courses list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/courses');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# With_params example
# ---

<?php
// Get courses list with pagination and includes

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/courses?with[]=offers&with[]=description&with[]=program&per_page=15&page=1');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/courses' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# With_params example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/courses?with[]=offers&with[]=description&with[]=program&per_page=15&page=1' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get courses list
const url = 'https://api.kwiga.com/courses';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# With_params example
# ---

// Get courses list with pagination and includes
const url = 'https://api.kwiga.com/courses?with[]=offers&with[]=description&with[]=program&per_page=15&page=1';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get courses list
import requests

url = 'https://api.kwiga.com/courses'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# With_params example
# ---

# Get courses list with pagination and includes
import requests

url = 'https://api.kwiga.com/courses?with[]=offers&with[]=description&with[]=program&per_page=15&page=1'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/courses",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# With_params example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/courses?with[]=offers&with[]=description&with[]=program&per_page=15&page=1",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 226,
      "product_id": 431,
      "type": {
        "id": 1,
        "name": "Course",
        "type": "course"
      },
      "title": "Test черновик квизов",
      "slug": "test-chernovik-kvizov",
      "preview": {
        "id": 3140,
        "uuid": "bdfe1347-574f-4829-a745-09ba2176cc2a",
        "name": "image_13.jpeg",
        "original_name": "image_13.jpeg",
        "url": "https://cdn57098163.ahacdn.me/local/cabinet-1/rcIJuNkWmpCQ/image_13.jpeg",
        "thumbnails": {
          "xs": "https://cdn57098163.ahacdn.me/local/cabinet-1/rcIJuNkWmpCQ/image_13_thumb_150.jpeg",
          "small": "https://cdn57098163.ahacdn.me/local/cabinet-1/rcIJuNkWmpCQ/image_13_thumb_500.jpeg",
          "medium": "https://cdn57098163.ahacdn.me/local/cabinet-1/rcIJuNkWmpCQ/image_13.jpeg",
          "large": "https://cdn57098163.ahacdn.me/local/cabinet-1/rcIJuNkWmpCQ/image_13.jpeg",
          "default": "https://cdn57098163.ahacdn.me/local/cabinet-1/rcIJuNkWmpCQ/image_13.jpeg"
        },
        "extension": "jpeg",
        "type_id": 2,
        "mime_type": "image/jpeg"
      },
      "url": "http://test.local/courses/test-chernovik-kvizov",
      "status": {
        "id": 3,
        "name": "Published"
      }
    }
  ],
  "links": {
    "first": "http://api.kwiga.local/courses?page=1",
    "last": "http://api.kwiga.local/courses?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "&laquo; Previous",
        "active": false
      },
      {
        "url": "http://api.kwiga.local/courses?page=1",
        "label": "1",
        "active": true
      },
      {
        "url": null,
        "label": "Next &raquo;",
        "active": false
      }
    ],
    "path": "http://api.kwiga.local/courses",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}

GET https://api.kwiga.com/courses

Список курсів кабінету. Є пагінація, за замовчуванням 15 (max: 15).

URL Parameters

with string[] optional
Масив з додатковими параметрами, які потрібно отримати разом з курсом. Можливі варіанти: offers (список всіх офферів з курсом), description (інфоблоки з описом курсу), program (дерево програми курсу)
Possible values: offers, description, program
sort_by string optional
Possible values: asc, descDefault: desc
per_page integer optional
Кількість елементів на сторінці
Range: 1 – 15Default: 15
page integer optional
Номер сторінки
Minimum: 1Default: 1
limit integer optional
Застарілий аліас для per_page. Залишений для зворотної сумісності з наявними інтеграціями; у новому коді використовуйте per_page. Якщо передано обидва параметри, перемагає per_page.
Range: 1 – 15

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Список учасників курсу

Приклад запиту:

<?php
// Get course users

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/courses/:course/users', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Get course users with filters

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/courses/:course/users?progress_general_from=50&progress_general_to=100&per_page=20', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get course users

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/courses/:course/users');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Get course users with filters

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/courses/:course/users?progress_general_from=50&progress_general_to=100&per_page=20');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/courses/:course/users' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --request GET 'https://api.kwiga.com/courses/:course/users?progress_general_from=50&progress_general_to=100&per_page=20' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get course users
const url = 'https://api.kwiga.com/courses/:course/users';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Get course users with filters
const url = 'https://api.kwiga.com/courses/:course/users?progress_general_from=50&progress_general_to=100&per_page=20';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get course users
import requests

url = 'https://api.kwiga.com/courses/:course/users'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Get course users with filters
import requests

url = 'https://api.kwiga.com/courses/:course/users?progress_general_from=50&progress_general_to=100&per_page=20'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/courses/:course/users",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/courses/:course/users?progress_general_from=50&progress_general_to=100&per_page=20",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": [
        {
            "user": {
                "id": 3,
                "name": "Admin Кабинет",
                "email": "test@test.com"
            },
            "contact": {
                "id": 1,
                "user_id": 3,
                "email": "test@test.com",
                "first_name": "test",
                "last_name": "admin cabinet",
                "phone": "979",
                "created_at": "2023-05-26T09:47:20.000000Z",
                "last_activity_at": null
            },
            "course_points": 0,
            "course_progress": {
                "course_id": 20,
                "course_url": "http://test.local/courses/Pm8CEofJ",
                "title": "Dripping",
                "lessons_count": 9,
                "lessons_count_viewed": 0,
                "lessons_viewed_percentage": 0,
                "lessons_count_completed": 0,
                "lessons_completion_percentage": 0,
                "quizzes_count": 0,
                "quizzes_count_completed": 0,
                "quizzes_completion_percentage": 0,
                "scores_max": 0,
                "quizzes_scores": 0,
                "product_scores": 0,
                "scores": 0,
                "is_completed": false,
                "completed_at": null,
                "current_lesson": {
                    "id": 48,
                    "course_id": 20,
                    "type_id": 1,
                    "status_id": 4,
                    "number": 6,
                    "title": "2",
                    "slug": "2",
                    "url": "http://test.local/courses/Pm8CEofJ/2",
                    "quizzes": [],
                    "module": {
                        "id": 7,
                        "course_id": 20,
                        "number": 1,
                        "title": "Module title"
                    }
                },
                "next_lesson": {
                    "id": 46,
                    "course_id": 20,
                    "type_id": 1,
                    "status_id": 4,
                    "number": 1,
                    "title": "3",
                    "slug": "3",
                    "url": "http://test.local/courses/Pm8CEofJ/3",
                    "module": {
                        "id": 7,
                        "course_id": 20,
                        "number": 1,
                        "title": "Module title"
                    }
                },
                "last_activity_at": null,
                "is_checkpoints_skipped": false,
                "checkpoints": [],
                "is_current_dripping_date_skipped": false,
                "is_next_dripping_date_skipped": false,
                "current_dripping_date": null,
                "next_dripping_date": null
            },
            "lessons_available_count": 9,
            "is_full_access": true,
            "subscription": {
                "is_active": true,
                "is_paid": true,
                "start_at": "2024-04-23T13:45:02.000000Z",
                "end_at": null,
                "offer_end_at": null,
                "order_end_at": null,
                "count_available_days": 661,
                "count_left_days": null,
                "state": {
                    "id": 2,
                    "name": "Open",
                    "title": "Open"
                }
            }
        }
    ],
    "links": {
        "first": "http://api.kwiga.local/courses/20/users?page=1",
        "last": "http://api.kwiga.local/courses/20/users?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "active": false
            },
            {
                "url": "http://api.kwiga.local/courses/20/users?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "active": false
            }
        ],
        "path": "http://api.kwiga.local/courses/20/users",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}

GET https://api.kwiga.com/courses/:course/users

Виводить список учасників курсу та їх прогрес. Є пагінація, за замовчуванням 15 (max: 250). Замініть :course на id курсу, який можна взяти в адресному рядку редагування/управління курсу або в ендпоінті по списку курсів.

URL Parameters

search string optional
Нестрогий пошук по імейлу або імені
contact_id integer optional
По id контакту. Об'єднується з emails / contact_ids / user_id / user_ids у єдиний фільтр аудиторії (union).
contact_ids integer/integer[] optional
Фільтр за id контактів. Приймає рядок через кому або повторюваний масив цілих чисел. Об'єднується з emails / contact_id / user_id / user_ids у єдиний фільтр аудиторії (union).
user_id integer optional
По id користувача. Об'єднується з emails / contact_id / contact_ids / user_ids у єдиний фільтр аудиторії (union).
user_ids integer/integer[] optional
Фільтр за id юзерів (акаунтів учня). Приймає рядок через кому або повторюваний масив цілих чисел. Об'єднується з emails / contact_id / contact_ids / user_id у єдиний фільтр аудиторії (union).
emails string/string[] optional
Фільтр за точним email-адресою. Приймає рядок через кому або повторюваний масив; кожне значення має бути валідним email. Об'єднується з contact_id / contact_ids / user_id / user_ids у єдиний фільтр аудиторії (union).
progress_general_from integer optional
Загальний прогрес від (від 0 до 99)
progress_general_to integer optional
Загальний прогрес до (від 0 до 100)
lessons_viewed_from integer optional
Відсоток перегляду уроку від (від 0 до 99)
lessons_viewed_to integer optional
Відсоток перегляду уроку до (від 0 до 100)
quizzes_progress_from integer optional
Відсоток пройдених практик від (від 0 до 99)
quizzes_progress_to integer optional
Відсоток пройдених практик до (від 0 до 100)
last_activity_from datetime optional
Остання активність на курсі від (дата UTC)
last_activity_to datetime optional
Остання активність на курсі до (дата UTC)
per_page integer optional
Кількість елементів на сторінці
Maximum: 250Default: 15
page integer optional
Номер сторінки
Default: 1
include string/string[] optional

Необов'язковий список розширень відповіді через кому. Кожен токен підключає одну секцію відповіді — клієнт платить лише за те, що попросив.

  • course_program — додає програму курсу на верхньому рівні відповіді у поле course_program (масив вузлів CourseProgram; у lesson-вузлів у children ідуть info-section вузли тієї ж форми з прив'язаними квізами у полі quizzes). Повертається один раз поряд з data/links/meta, бо програма однакова для всіх учнів сторінки.
  • lesson_progress — додає у кожен елемент data[] поле lesson_progress: масив об'єктів LessonProgress (по одному запису на урок, по якому в учня є прогрес).
  • module_progress — додає у кожен елемент data[] поле module_progress: масив об'єктів ModuleProgress.
  • quiz_progress — додає у кожен елемент data[] поле quiz_progress: масив об'єктів QuizProgress, по одному запису на пару (course_lesson_id, quiz_id) зі зведенням за останньою чинною спробою учня.

Увага: один і той самий квіз може бути прив'язаний до кількох уроків одного курсу, тому quiz_progress адресується парою урок/квіз, а не одним quiz_id. Клієнт зшиває per-user прогрес з деревом за course_nodeble_id lesson/module-вузлів і за парою (lesson.course_nodeble_id, quizzes[i].id) для section-вузлів.

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).
Conditional: Returned when include contains course_program.
Програма курсу, повертається на верхньому рівні коли include містить course_program. Дерево вузлів CourseProgram; у lesson-вузлів у children лежать info-section вузли тієї ж форми з прив'язаними квізами. Однакова для всіх учнів сторінки, тому API повертає її один раз замість повторення в кожному ряду.

POST-аліас

POST https://api.kwiga.com/courses/:course/users/query

Функціональний аліас для GET-ендпоінта вище. Приймає ті самі параметри в тілі запиту замість query-string — стане в нагоді, коли список фільтрів завеликий для URL (або просто зручніше збирати JSON на клієнті). Body має пріоритет над query-string, форма відповіді ідентична.

Отримання детальної аналітики по уроках

На додачу до зведення прогресу по курсу ендпоінт може повернути детальну розбивку прогресу по уроках. Розширення запитується query-параметром include (повний список токенів і тип даних, який кожен з них підключає, описаний у його описі вище, в URL Parameters). Програма курсу повертається один раз на верхньому рівні у полі course_program — вона однакова для всіх учнів сторінки, тому розмір відповіді залишається пропорційним добутку учнів на уроки/квізи, а не на розмір усього дерева.

Приклад відповіді з ?include=course_program,lesson_progress,module_progress,quiz_progress

{
  "data": [
    {
      "user": {
        "id": 3,
        "name": "Admin Кабинет",
        "email": "test@test.com"
      },
      "contact": {
        "id": 1,
        "user_id": 3,
        "email": "test@test.com",
        "first_name": "test",
        "last_name": "admin cabinet",
        "phone": "979",
        "created_at": "2023-05-26T09:47:20.000000Z",
        "last_activity_at": null
      },
      "course_points": 0,
      "course_progress": {
        "course_id": 20,
        "title": "Dripping",
        "lessons_count": 9,
        "lessons_count_viewed": 2,
        "lessons_viewed_percentage": 22,
        "lessons_count_completed": 1,
        "lessons_completion_percentage": 11,
        "quizzes_count": 2,
        "quizzes_count_completed": 1,
        "quizzes_completion_percentage": 50,
        "scores_max": 200,
        "quizzes_scores": 80,
        "is_completed": false,
        "completed_at": null
      },
      "lessons_available_count": 9,
      "is_full_access": true,
      "lesson_progress": [
        {
          "lesson_id": 48,
          "is_watched": true,
          "is_completed": true,
          "quizzing_status": {
            "id": 4,
            "slug": "Completed",
            "title": "Completed"
          },
          "watching_start_at": "2026-05-20T11:20:00.000000Z",
          "watching_end_at": "2026-05-20T11:30:00.000000Z",
          "quizzing_start_at": "2026-05-20T11:30:00.000000Z",
          "quizzing_end_at": "2026-05-20T11:34:00.000000Z",
          "completed_at": "2026-05-20T11:35:00.000000Z",
          "created_at": "2026-05-20T11:20:00.000000Z",
          "updated_at": "2026-05-20T11:35:00.000000Z"
        },
        {
          "lesson_id": 46,
          "is_watched": true,
          "is_completed": false,
          "quizzing_status": {
            "id": 3,
            "slug": "InProgress",
            "title": "In progress"
          },
          "watching_start_at": "2026-05-21T09:00:00.000000Z",
          "watching_end_at": "2026-05-21T09:10:00.000000Z",
          "quizzing_start_at": null,
          "quizzing_end_at": null,
          "completed_at": null,
          "created_at": "2026-05-21T09:00:00.000000Z",
          "updated_at": "2026-05-21T09:10:00.000000Z"
        }
      ],
      "module_progress": [
        {
          "module_id": 7,
          "node_id": 341,
          "parent_node_id": null,
          "title": "Mathematics",
          "lessons_count": 12,
          "lessons_count_viewed": 5,
          "lessons_count_completed": 4,
          "lessons_completed_display_percentage": 33,
          "quizzes_count": 9,
          "quizzes_count_completed": 3,
          "quizzes_completion_percentage": 34,
          "quizzes_scores": 36,
          "scores_max": 108,
          "is_watched": false,
          "is_completed": false,
          "quizzing_status": {
            "id": 3,
            "slug": "InProgress",
            "title": "In progress"
          },
          "watching_start_at": "2026-05-20T11:20:00.000000Z",
          "watching_end_at": null,
          "quizzing_start_at": "2026-05-20T12:05:00.000000Z",
          "quizzing_end_at": null,
          "completed_at": null,
          "created_at": "2026-05-20T11:20:00.000000Z",
          "updated_at": "2026-05-21T09:10:00.000000Z"
        },
        {
          "module_id": 8,
          "node_id": 352,
          "parent_node_id": 341,
          "title": "Mathematics — geometry",
          "lessons_count": 5,
          "lessons_count_viewed": 0,
          "lessons_count_completed": 0,
          "lessons_completed_display_percentage": 0,
          "quizzes_count": 4,
          "quizzes_count_completed": 0,
          "quizzes_completion_percentage": 0,
          "quizzes_scores": 0,
          "scores_max": 48,
          "is_watched": false,
          "is_completed": false,
          "quizzing_status": null,
          "watching_start_at": null,
          "watching_end_at": null,
          "quizzing_start_at": null,
          "quizzing_end_at": null,
          "completed_at": null,
          "created_at": null,
          "updated_at": null
        }
      ],
      "quiz_progress": [
        {
          "course_lesson_id": 48,
          "quiz_id": 101,
          "status": {
            "id": 1,
            "slug": "Passed",
            "title": "Passed"
          },
          "scores": 80,
          "scores_max": 100,
          "started_at": "2026-05-20T11:30:00.000000Z",
          "finished_at": "2026-05-20T11:34:00.000000Z",
          "checked_at": null,
          "last_activity_at": "2026-05-20T11:34:00.000000Z",
          "attempt_number": 2
        }
      ]
    }
  ],
  "links": {
    "first": "https://api.example.com/courses/20/users?page=1",
    "last": "https://api.example.com/courses/20/users?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://api.example.com/courses/20/users",
    "per_page": 15,
    "to": 1,
    "total": 1
  },
  "course_structure": [
    {
      "id": 12,
      "course_nodeble_type": "course_module",
      "course_nodeble_id": 7,
      "parent_id": null,
      "order": 1,
      "course_nodeble": {
        "id": 7,
        "title": "Module title"
      },
      "children": [
        {
          "id": 34,
          "course_nodeble_type": "course_lesson",
          "course_nodeble_id": 48,
          "parent_id": 12,
          "order": 1,
          "course_nodeble": {
            "id": 48,
            "title": "Lesson 1"
          },
          "children": [
            {
              "id": 201,
              "course_nodeble_type": "info_section",
              "course_nodeble_id": 201,
              "parent_id": 34,
              "order": 1,
              "course_nodeble": {
                "id": 201,
                "name": "List of practice",
                "slug": "list-of-practice"
              },
              "quizzes": [
                {
                  "id": 101,
                  "name": "Module wrap-up quiz",
                  "quiz_score": {
                    "common_scores_min": 0,
                    "common_scores_max": 100,
                    "quiz_scores": 0,
                    "questions_scores_max": 80,
                    "statements_scores_max": 0,
                    "answers_scores_min": 0,
                    "answers_scores_max": 20
                  }
                }
              ],
              "children": []
            },
            {
              "id": 202,
              "course_nodeble_type": "info_section",
              "course_nodeble_id": 202,
              "parent_id": 34,
              "order": 2,
              "course_nodeble": {
                "id": 202,
                "name": "Theory",
                "slug": "theory"
              },
              "quizzes": [],
              "children": []
            }
          ]
        },
        {
          "id": 35,
          "course_nodeble_type": "course_lesson",
          "course_nodeble_id": 46,
          "parent_id": 12,
          "order": 2,
          "course_nodeble": {
            "id": 46,
            "title": "Lesson 2"
          },
          "children": [
            {
              "id": 203,
              "course_nodeble_type": "info_section",
              "course_nodeble_id": 203,
              "parent_id": 35,
              "order": 1,
              "course_nodeble": {
                "id": 203,
                "name": "Intro",
                "slug": "intro"
              },
              "quizzes": [],
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

Зшивка прогресу з деревом

Ендпоінт повертає програму курсу один раз на верхньому рівні, а масиви прогресу користувача — всередині кожного рядка data[]. Щоб накласти прогрес конкретного учня на дерево, зробіть так.

1. Побудуйте словники з його масивів прогресу для швидкого пошуку.

Про імена: записи прогресу використовують lesson_id/module_id/course_lesson_id, а вузли програми — course_nodeble_id. Це одне й те саме значення, просто в різних частинах відповіді — див. крок 2.

2. Обійдіть course_program і для кожного вузла візьміть потрібний запис.

У кожного вузла є course_nodeble_type + course_nodeble_id. Використовуйте course_nodeble_id як ключ пошуку:

Якщо пошук нічого не повернув — в учня ще немає прогресу по цьому вузлу, відмалюйте порожній стан. Програма курсу однакова для всіх учнів сторінки, тому її безпечно побудувати один раз і перевикористати між рядками.

Закриті групи

Отримання списку закритих груп

Приклад запиту:

<?php
// Get closed groups list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/closed-groups', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Get closed groups list filtered by title

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/closed-groups?filters[search]=VIP', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get closed groups list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/closed-groups');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Get closed groups list filtered by title

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/closed-groups?filters[search]=VIP');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/closed-groups' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/closed-groups?filters[search]=VIP' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get closed groups list
const url = 'https://api.kwiga.com/closed-groups';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Get closed groups list filtered by title
const url = 'https://api.kwiga.com/closed-groups?filters[search]=VIP';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get closed groups list
import requests

url = 'https://api.kwiga.com/closed-groups'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Get closed groups list filtered by title
import requests

url = 'https://api.kwiga.com/closed-groups?filters[search]=VIP'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/closed-groups",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/closed-groups?filters[search]=VIP",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": [
        {
            "id": 1,
            "product_id": 128,
            "title": "VIP mastermind",
            "slug": "vip-mastermind",
            "type": {
                "id": 1,
                "slug": "Telegram",
                "title": "Telegram"
            },
            "status": {
                "id": 2,
                "slug": "Active",
                "title": "Active"
            },
            "chatbot": {
                "name": "@my_group_bot"
            },
            "channel": {
                "telegram_channel_id": "-1001234567890",
                "linked_chat_id": "-1009876543210",
                "url": "https://t.me/my_group"
            },
            "created_at": "2024-01-15T10:00:00.000000Z",
            "updated_at": "2024-02-20T08:30:00.000000Z"
        },
        {
            "id": 2,
            "product_id": 131,
            "title": "Community (Kwiga native)",
            "slug": "community-kwiga-native",
            "type": {
                "id": 2,
                "slug": "Kwiga",
                "title": "Kwiga"
            },
            "status": {
                "id": 5,
                "slug": "NotConnected",
                "title": "Not connected"
            },
            "chatbot": null,
            "channel": null,
            "created_at": "2024-03-01T09:00:00.000000Z",
            "updated_at": "2024-03-01T09:00:00.000000Z"
        }
    ]
}

GET https://api.kwiga.com/closed-groups

Повертає закриті групи кабінету (Telegram або нативні Kwiga-спільноти, прив'язані до продукту). Потребує право closed_group_read.

URL Parameters

filters object optional
Параметри фільтрації
search string optional
Пошук за назвою закритої групи
Max length: 255 characters

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Отримання учасників закритої групи

Приклад запиту:

<?php
// Get closed group participants

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/closed-groups/:closedGroup/participants', $options);

$result = json_decode($response->getBody());
?>


# ---
# Paginated example
# ---

<?php
// Get closed group participants with pagination

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/closed-groups/:closedGroup/participants?page=1&per_page=50', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Get closed group participants filtered by status and search

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/closed-groups/:closedGroup/participants?filters[search]=Alice&filters[status_id]=1', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get closed group participants

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/closed-groups/:closedGroup/participants');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Paginated example
# ---

<?php
// Get closed group participants with pagination

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/closed-groups/:closedGroup/participants?page=1&per_page=50');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Get closed group participants filtered by status and search

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/closed-groups/:closedGroup/participants?filters[search]=Alice&filters[status_id]=1');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/closed-groups/:closedGroup/participants' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Paginated example
# ---

curl --location --request GET 'https://api.kwiga.com/closed-groups/:closedGroup/participants?page=1&per_page=50' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/closed-groups/:closedGroup/participants?filters[search]=Alice&filters[status_id]=1' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get closed group participants
const url = 'https://api.kwiga.com/closed-groups/:closedGroup/participants';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Paginated example
# ---

// Get closed group participants with pagination
const url = 'https://api.kwiga.com/closed-groups/:closedGroup/participants?page=1&per_page=50';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Get closed group participants filtered by status and search
const url = 'https://api.kwiga.com/closed-groups/:closedGroup/participants?filters[search]=Alice&filters[status_id]=1';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get closed group participants
import requests

url = 'https://api.kwiga.com/closed-groups/:closedGroup/participants'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Paginated example
# ---

# Get closed group participants with pagination
import requests

url = 'https://api.kwiga.com/closed-groups/:closedGroup/participants?page=1&per_page=50'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Get closed group participants filtered by status and search
import requests

url = 'https://api.kwiga.com/closed-groups/:closedGroup/participants?filters[search]=Alice&filters[status_id]=1'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/closed-groups/:closedGroup/participants",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Paginated example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/closed-groups/:closedGroup/participants?page=1&per_page=50",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/closed-groups/:closedGroup/participants?filters[search]=Alice&filters[status_id]=1",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": [
        {
            "user_id": 1024,
            "contact": {
                "id": 42,
                "user_id": 1024,
                "first_name": "John",
                "last_name": "Doe",
                "name": "John Doe",
                "email": "user@example.com",
                "phone": "+380931234567"
            },
            "status": {
                "id": 1,
                "slug": "ActiveMember",
                "title": "Active member"
            },
            "is_joined": true,
            "is_banned": false,
            "telegram_accounts": [
                {
                    "telegram_id": 123456789,
                    "username": "johndoe"
                },
                {
                    "telegram_id": 987654321,
                    "username": null
                }
            ],
            "subscription": {
                "is_active": true,
                "is_paid": true,
                "start_at": "2024-04-23T13:45:02.000000Z",
                "end_at": null,
                "offer_end_at": null,
                "order_end_at": null,
                "frozen_at": null,
                "extended_at": null,
                "count_available_days": 661,
                "count_left_days": null,
                "state": {
                    "id": 2,
                    "slug": "Open",
                    "title": "Open"
                }
            }
        },
        {
            "user_id": 1031,
            "contact": null,
            "status": {
                "id": 3,
                "slug": "NotJoined",
                "title": "Not joined"
            },
            "is_joined": false,
            "is_banned": false,
            "telegram_accounts": [],
            "subscription": {
                "is_active": false,
                "is_paid": true,
                "start_at": "2024-04-23T13:45:02.000000Z",
                "end_at": "2024-05-23T13:45:02.000000Z",
                "offer_end_at": "2024-05-23T13:45:02.000000Z",
                "order_end_at": null,
                "frozen_at": null,
                "extended_at": null,
                "count_available_days": 30,
                "count_left_days": 0,
                "state": {
                    "id": 3,
                    "slug": "Closed",
                    "title": "Closed"
                }
            }
        }
    ],
    "links": {
        "first": "https://api.kwiga.com/closed-groups/1/participants?page=1",
        "last": "https://api.kwiga.com/closed-groups/1/participants?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "https://api.kwiga.com/closed-groups/1/participants",
        "per_page": 15,
        "to": 2,
        "total": 2
    }
}

GET https://api.kwiga.com/closed-groups/:closedGroup/participants

Повертає посторінковий список учасників однієї закритої групи, по одному рядку на користувача. Потребує право closed_group_participants або product_participants. Замініть :closedGroup на ідентифікатор закритої групи.

URL Parameters

filters object optional
Параметри фільтрації
search string optional
Пошук за ім'ям або email учасника
Max length: 255 characters
status_id integer optional

Фільтр за id статусу участі в групі.

  • 1 — активний учасник
  • 2 — забанений
  • 3 — не приєднувався
  • 4 — вийшов з групи
  • 5 — доступ закінчився
Possible values: 1, 2, 3, 4, 5
emails string/string[] optional
Фільтр за точною email-адресою. Приймає рядок зі значеннями через кому або повторюваний масив; кожне значення має бути валідним email. Комбінується з contact_ids / user_ids в єдиний фільтр за аудиторією (об'єднання) — розв'язується в контакти кабінету та їхніх користувачів.
contact_ids integer/integer[] optional
Фільтр за ідентифікаторами контактів. Приймає рядок зі значеннями через кому або повторюваний масив цілих чисел; кожен id має належати поточному кабінету. Комбінується з emails / user_ids в єдиний фільтр за аудиторією (об'єднання) — розв'язується в користувачів контактів.
user_ids integer/integer[] optional
Фільтр за ідентифікаторами користувачів (акаунтів студентів). Приймає рядок зі значеннями через кому або повторюваний масив цілих чисел. Комбінується з emails / contact_ids в єдиний фільтр за аудиторією (об'єднання).

per_page integer optional
Кількість елементів на сторінці
Range: 1 – 250Default: 15
page integer optional
Номер сторінки
Minimum: 1Default: 1

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Квізи

Список розміщень квізів кабінету

Приклад запиту:

<?php
// Get all quiz placements of the cabinet

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/quiz-placements', $options);

$result = json_decode($response->getBody());
?>


# ---
# By_course example
# ---

<?php
// Get quiz placements of one course

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/quiz-placements?filters[course_ids]=35412&per_page=50', $options);

$result = json_decode($response->getBody());
?>


# ---
# Counted_in_progress example
# ---

<?php
// Get only placements counted in progress, with module chain

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/quiz-placements?filters[course_ids]=35412&filters[is_used_in_progress]=1&include=module_ids', $options);

$result = json_decode($response->getBody());
?>


# ---
# Shown_in_program example
# ---

<?php
// Get only placements shown in the course program

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/quiz-placements?filters[course_ids]=35412&filters[can_display_in_program]=1', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get all quiz placements of the cabinet

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/quiz-placements');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# By_course example
# ---

<?php
// Get quiz placements of one course

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&per_page=50');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Counted_in_progress example
# ---

<?php
// Get only placements counted in progress, with module chain

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[is_used_in_progress]=1&include=module_ids');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Shown_in_program example
# ---

<?php
// Get only placements shown in the course program

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[can_display_in_program]=1');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/quiz-placements' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# By_course example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&per_page=50' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Counted_in_progress example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[is_used_in_progress]=1&include=module_ids' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Shown_in_program example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[can_display_in_program]=1' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get all quiz placements of the cabinet
const url = 'https://api.kwiga.com/quiz-placements';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# By_course example
# ---

// Get quiz placements of one course
const url = 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&per_page=50';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Counted_in_progress example
# ---

// Get only placements counted in progress, with module chain
const url = 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[is_used_in_progress]=1&include=module_ids';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Shown_in_program example
# ---

// Get only placements shown in the course program
const url = 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[can_display_in_program]=1';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get all quiz placements of the cabinet
import requests

url = 'https://api.kwiga.com/quiz-placements'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# By_course example
# ---

# Get quiz placements of one course
import requests

url = 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&per_page=50'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Counted_in_progress example
# ---

# Get only placements counted in progress, with module chain
import requests

url = 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[is_used_in_progress]=1&include=module_ids'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Shown_in_program example
# ---

# Get only placements shown in the course program
import requests

url = 'https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[can_display_in_program]=1'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/quiz-placements",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# By_course example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&per_page=50",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Counted_in_progress example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[is_used_in_progress]=1&include=module_ids",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Shown_in_program example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/quiz-placements?filters[course_ids]=35412&filters[can_display_in_program]=1",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 9142,
      "quiz": {
        "id": 4211,
        "name": "Ukrainian language — unit 1 test",
        "type": {
          "id": 1,
          "slug": "Normal",
          "title": "Normal"
        },
        "questions_type": {
          "id": 2,
          "slug": "Single",
          "title": "Single choice"
        },
        "questions_count": 12,
        "quiz_score": {
          "common_scores_min": 0,
          "common_scores_max": 12,
          "quiz_scores": 0,
          "questions_scores_max": 12,
          "statements_scores_max": 0,
          "answers_scores_min": 0,
          "answers_scores_max": 12
        },
        "is_estimate_on": true,
        "is_auto_approve": true,
        "can_retry": true,
        "can_retry_only_failure": false,
        "count_tries": 3
      },
      "product_id": 431,
      "product": {
        "id": 431,
        "productable_type": "course",
        "productable_id": 226,
        "name": "PreStart 2026. PRO5.0",
        "url": "https://lm4.kwiga.com/courses/prestart-2026"
      },
      "course_lesson_id": 504,
      "lesson": {
        "id": 504,
        "title": "Unit 1 — vocabulary"
      },
      "info_section": {
        "id": 4410,
        "name": "Practice",
        "order": 3,
        "url": "https://lm4.kwiga.com/courses/prestart-2026/3/lessons/504",
        "crm_url": "https://lm4.kwiga.com/expert/course/226/lesson/504/constructor?section=2"
      },
      "quizable_type": "info_units",
      "quizable_id": 8801,
      "order": 1,
      "is_used_in_progress": true,
      "can_display_in_program": true,
      "is_checkpoint": true,
      "dripping": {
        "accessible_type": {
          "id": 1,
          "slug": "Immediately",
          "title": "Immediately from the moment of opening access to the course"
        },
        "accessible_after_days": null,
        "accessible_after_hours": null,
        "accessible_after_minutes": null,
        "accessible_start_at": null,
        "closed_type": {
          "id": 2,
          "slug": "AfterDays",
          "title": "N days after access is granted"
        },
        "closed_after_days": 14,
        "closed_after_hours": null,
        "closed_after_minutes": null,
        "closed_start_at": null
      },
      "module_ids": [
        122189
      ]
    },
    {
      "id": 9143,
      "quiz": {
        "id": 4211,
        "name": "Ukrainian language — unit 1 test",
        "type": {
          "id": 1,
          "slug": "Normal",
          "title": "Normal"
        },
        "questions_type": {
          "id": 2,
          "slug": "Single",
          "title": "Single choice"
        },
        "questions_count": 8,
        "quiz_score": {
          "common_scores_min": 0,
          "common_scores_max": 12,
          "quiz_scores": 0,
          "questions_scores_max": 12,
          "statements_scores_max": 0,
          "answers_scores_min": 0,
          "answers_scores_max": 12
        },
        "is_estimate_on": true,
        "is_auto_approve": true,
        "can_retry": true,
        "can_retry_only_failure": false,
        "count_tries": 3
      },
      "product_id": 431,
      "product": {
        "id": 431,
        "productable_type": "course",
        "productable_id": 226,
        "name": "PreStart 2026. PRO5.0",
        "url": "https://lm4.kwiga.com/courses/prestart-2026"
      },
      "course_lesson_id": 519,
      "lesson": {
        "id": 519,
        "title": "Unit 1 — vocabulary"
      },
      "info_section": null,
      "quizable_type": "info_units",
      "quizable_id": 8940,
      "order": 2,
      "is_used_in_progress": false,
      "can_display_in_program": true,
      "is_checkpoint": false,
      "dripping": null,
      "module_ids": [
        122190,
        122188
      ]
    }
  ],
  "links": {
    "first": "https://api.kwiga.com/quiz-placements?page=1",
    "last": "https://api.kwiga.com/quiz-placements?page=4",
    "prev": null,
    "next": "https://api.kwiga.com/quiz-placements?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 4,
    "path": "https://api.kwiga.com/quiz-placements",
    "per_page": 50,
    "to": 50,
    "total": 187
  }
}

GET https://api.kwiga.com/quiz-placements

Повертає, де саме кожен квіз розміщений у курсах кабінету. Рядок — це розміщення, а не квіз: один і той самий квіз може бути прив'язаний до кількох уроків і секцій, і в прогресі кожне розміщення рахується окремо. Тому кілька рядків з однаковим quiz.id — це норма.

Ендпоінт зв'язує структуру курсу та спроби — зокрема, звідси беруться значення для filters[quiz_ids]. Щоб дізнатися, скільки балів можна набрати за весь курс, візьміть розміщення з is_used_in_progress = true і просумуйте їхні quiz.quiz_score.common_scores_max. Так рахується склад курсу, однаковий для всіх. У конкретного учня частина уроків може бути ще не відкрита за розписанням або не входити в його тариф, тому його особисті цифри беруться з course_progress та module_progress.

URL Parameters

filters object optional
Фільтри, що звужують вибірку розміщень. Ідентифікатори перевіряються на належність поточному кабінету — чужий id відкидається, а не ігнорується тихо.
product_ids integer/integer[] optional
Фільтр за ідентифікаторами продуктів. Приймає рядок через кому або повторюваний масив; кожен id має належати поточному кабінету.
course_ids integer/integer[] optional
Фільтр за ідентифікаторами курсів. Приймає рядок через кому або повторюваний масив; кожен id має належати поточному кабінету.
lesson_ids integer/integer[] optional
Фільтр за ідентифікаторами уроків. Приймає рядок через кому або повторюваний масив.
quiz_ids integer/integer[] optional
Фільтр за ідентифікаторами квізів. Приймає рядок через кому або повторюваний масив; кожен id має належати поточному кабінету.
is_used_in_progress boolean optional
Залишити лише розміщення, які враховуються (1) або не враховуються (0) у прогресі. Прапорець задається на кожному розміщенні в CRM, тому один квіз може рахуватися в одному уроці й не рахуватися в іншому.
can_display_in_program boolean optional
Залишити лише розміщення, які показуються (1) або не показуються (0) у програмі курсу для учнів. Не пов'язано з is_used_in_progress: квіз може враховуватися в прогресі, не будучи видимим у програмі, і навпаки.

include string/string[] optional
Додаткові секції відповіді. module_ids додає ланцюжок модулів-предків уроку (найближчий модуль першим).
Possible values: module_ids
per_page integer optional
Кількість елементів на сторінці (максимум 250, за замовчуванням 50)
page integer optional
Номер сторінки

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

POST-аліас

POST https://api.kwiga.com/quiz-placements/query

Функціональний аліас для GET-ендпоінта вище. Приймає ті самі параметри в тілі запиту замість query-string — стане в нагоді, коли список фільтрів завеликий для URL (або просто зручніше збирати JSON на клієнті). Body має пріоритет над query-string, форма відповіді ідентична.

Список спроб у кабінеті

Приклад запиту:

<?php
// List quiz attempts (defaults — newest first by status_updated_at)

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/quiz-attempts', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Filtered list — pending check & need rework, scoped to two products

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/quiz-attempts?filters[practice_statuses]=3,6&filters[product_ids]=10,11&sort_by=last_activity_at&sort_dir=desc&per_page=50', $options);

$result = json_decode($response->getBody());
?>
<?php
// List quiz attempts (defaults — newest first by status_updated_at)

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/quiz-attempts');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Filtered list — pending check & need rework, scoped to two products

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/quiz-attempts?filters[practice_statuses]=3,6&filters[product_ids]=10,11&sort_by=last_activity_at&sort_dir=desc&per_page=50');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/quiz-attempts' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/quiz-attempts?filters[practice_statuses]=3,6&filters[product_ids]=10,11&sort_by=last_activity_at&sort_dir=desc&per_page=50' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// List quiz attempts (defaults — newest first by status_updated_at)
const url = 'https://api.kwiga.com/quiz-attempts';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Filtered list — pending check & need rework, scoped to two products
const url = 'https://api.kwiga.com/quiz-attempts?filters[practice_statuses]=3,6&filters[product_ids]=10,11&sort_by=last_activity_at&sort_dir=desc&per_page=50';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# List quiz attempts (defaults — newest first by status_updated_at)
import requests

url = 'https://api.kwiga.com/quiz-attempts'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Filtered list — pending check & need rework, scoped to two products
import requests

url = 'https://api.kwiga.com/quiz-attempts?filters[practice_statuses]=3,6&filters[product_ids]=10,11&sort_by=last_activity_at&sort_dir=desc&per_page=50'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/quiz-attempts",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/quiz-attempts?filters[practice_statuses]=3,6&filters[product_ids]=10,11&sort_by=last_activity_at&sort_dir=desc&per_page=50",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 4821,
      "root_id": 4810,
      "previous_id": 4815,
      "number_version": 3,
      "user_id": 712,
      "product_id": 431,
      "quiz_id": 88,
      "course_id": 226,
      "course_lesson_id": 504,
      "lesson_section": {
        "id": 88,
        "name": "Knowledge check",
        "order": 3,
        "url": "https://lm4.kwiga.com/courses/js-foundations/3/lessons/504",
        "crm_url": "https://lm4.kwiga.com/expert/courses/js-foundations/3/lessons/504/sections/3"
      },
      "status": {
        "id": 6,
        "slug": "PendingCheck",
        "title": "Pending review"
      },
      "scores": 12.5,
      "scores_max": 15,
      "count_questions": 10,
      "count_questions_correct": 8,
      "count_questions_incorrect": 2,
      "is_force_approved": false,
      "is_read": false,
      "started_at": "2026-05-23T11:02:14.000000Z",
      "last_activity_at": "2026-05-23T11:14:51.000000Z",
      "finished_at": "2026-05-23T11:14:51.000000Z",
      "deadline_at": null,
      "status_updated_at": "2026-05-23T11:14:51.000000Z",
      "checked_at": null,
      "commented_at": null,
      "canceled_at": null,
      "passed_time_in_seconds": 757,
      "crm_url": "https://lm4.kwiga.com/expert/practices?attempt_id=4821",
      "user": {
        "id": 712,
        "name": "Alice Student",
        "email": "alice@example.com"
      },
      "quiz": {
        "id": 88,
        "name": "Module 3 final test"
      },
      "course": {
        "id": 226,
        "product_id": 431,
        "type": {
          "id": 1,
          "name": "Course",
          "type": "course"
        },
        "title": "JS Foundations",
        "slug": "js-foundations",
        "url": "http://test.local/courses/js-foundations",
        "status": {
          "id": 3,
          "name": "Published"
        }
      },
      "lesson": {
        "id": 504,
        "title": "Closures & scope",
        "course": {
          "id": 226,
          "product_id": 431,
          "type_id": 1,
          "title": "JS Foundations",
          "url": "http://test.local/courses/js-foundations"
        },
        "module": {
          "id": 30,
          "course_id": 226,
          "number": 3,
          "title": "Functions in depth"
        }
      },
      "product": {
        "id": 431,
        "productable_type": "course",
        "productable_id": 226,
        "name": "JS Foundations",
        "url": "http://test.local/courses/js-foundations"
      }
    }
  ],
  "links": {
    "first": "https://api.kwiga.com/quiz-attempts?page=1",
    "last": "https://api.kwiga.com/quiz-attempts?page=4",
    "prev": null,
    "next": "https://api.kwiga.com/quiz-attempts?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 4,
    "path": "https://api.kwiga.com/quiz-attempts",
    "per_page": 15,
    "to": 15,
    "total": 58,
    "links": [
      { "url": null, "label": "&laquo; Previous", "active": false },
      { "url": "https://api.kwiga.com/quiz-attempts?page=1", "label": "1", "active": true },
      { "url": "https://api.kwiga.com/quiz-attempts?page=2", "label": "2", "active": false },
      { "url": "https://api.kwiga.com/quiz-attempts?page=2", "label": "Next &raquo;", "active": false }
    ]
  },
  "attempts_unread_count": 12
}

GET https://api.kwiga.com/quiz-attempts

Повертає посторінковий список спроб учнів за квізами, доступних авторизованому куратору в усьому кабінеті (без прив'язки до продукту в URL — передайте параметр products, щоб звузити вибірку). За замовчуванням 15 елементів на сторінку, максимум 250. Поле attempts_unread_count на верхньому рівні показує кількість непрочитаних спроб з тим самим набором фільтрів — зручно для «бейджа» нових спроб.

URL Parameters

filters object optional
Контейнер для всіх фільтрів даних. Сортування, пагінація та прапори додаткових секцій відповіді лежать у корені запиту, а не всередині filters.
search string optional
Нечіткий пошук за email/іменем учня, назвою квіза або уроку.
practice_statuses integer/integer[] optional

Фільтр за ідентифікаторами статусів спроб. Кілька значень об'єднуються «або».

  • 1 — Пройдено
  • 2 — Не пройдено
  • 3 — Потрібне доопрацювання
  • 4 — Остання версія змінила статус
  • 5 — У процесі
  • 6 — Очікує перевірки
  • 7 — Не розпочав
  • -1 — Неперевірені відповіді за завданнями LMS (службове значення лише для фільтра; у відповіді не зустрічається)
Possible values: -1, 1, 2, 3, 4, 5, 6, 7
product_ids integer/integer[] optional
Фільтр за ідентифікаторами продуктів (продуктів-курсів).
quiz_ids integer/integer[] optional
Фільтр за ідентифікаторами квізів.
lesson_ids integer/integer[] optional
Фільтр за ідентифікаторами уроків.
module_ids integer/integer[] optional
Фільтр за ідентифікаторами модулів.
offer_ids integer/integer[] optional
Фільтр за ідентифікаторами оферів, через які учень отримав доступ.
curator_ids integer/integer[] optional
Фільтр за ідентифікаторами відповідальних кураторів.
group_ids integer/integer[] optional
Фільтр за ідентифікаторами груп доступу до продукту.
emails string/string[] optional
Фільтр за email контактів-учасників спроб. Об'єднується з contact_ids / user_ids у єдиний фільтр аудиторії (union).
contact_ids integer/integer[] optional
Фільтр за id контактів кабінету (учасників спроб). Об'єднується з emails / user_ids (union).
user_ids integer/integer[] optional
Фільтр за id користувачів (акаунтів) — учасників спроб. Об'єднується з emails / contact_ids (union).
tag_ids integer/integer[] optional
Фільтр за ідентифікаторами тегів контактів учнів. Взаємно виключний з excluded_tag_ids — якщо передано обидва, перемагає excluded_tag_ids.
excluded_tag_ids integer/integer[] optional
Виключити спроби учнів, у яких виставлено будь-який із зазначених тегів контакту. Взаємно виключний з tag_ids.
last_activity_from datetime optional
Нижня межа (включно) дати останньої активності за спробою (UTC).
last_activity_to datetime optional
Верхня межа (включно) дати останньої активності за спробою (UTC).
status_updated_from datetime optional
Нижня межа (включно) дати останньої зміни статусу спроби (UTC).
status_updated_to datetime optional
Верхня межа (включно) дати останньої зміни статусу спроби (UTC).
finished_from datetime optional
Нижня межа (включно) дати завершення спроби (UTC).
finished_to datetime optional
Верхня межа (включно) дати завершення спроби (UTC).
curator_last_activity_from datetime optional
Нижня межа (включно) дати останньої активності куратора за спробою (UTC).
curator_last_activity_to datetime optional
Верхня межа (включно) дати останньої активності куратора за спробою (UTC).
only_last_attempt boolean optional
Якщо true — повертає лише останню невідмінену спробу кожного учня замість усієї історії.
curator_conditions string/string[] optional

Додаткові умови за вибраними curator_ids (без них ігноруються). Кілька значень обʼєднуються за curator_logic.

  • attached_to_quiz — куратор привʼязаний до продукту спроби
  • changed_status — куратор змінював статус спроби
  • commented_on_status — куратор залишав коментар при зміні статусу
  • commented_on_assignments — куратор коментував завдання спроби
Possible values: attached_to_quiz, changed_status, commented_on_status, commented_on_assignments
curator_logic string optional
Як обʼєднувати curator_conditionsany (АБО, за замовчуванням) або all (І). Працює лише разом із curator_ids та curator_conditions.
Example: anyPossible values: any, allDefault: any

per_page integer optional
Кількість елементів на сторінці.
Maximum: 250Default: 15
page integer optional
Номер сторінки.
Default: 1
sort_by string optional
Поле сортування результату.
Example: last_activity_atPossible values: last_activity_at, status_updated_at, finished_at, statusDefault: last_activity_at
sort_dir string optional
Напрямок сортування.
Example: descPossible values: asc, descDefault: desc

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).
Кількість непрочитаних кураторами спроб у кабінеті під поточний набір фільтрів. Підходить для глобального «бейджа» нових спроб.

POST-аліас

POST https://api.kwiga.com/quiz-attempts/query

Функціональний аліас для GET-ендпоінта вище. Приймає ті самі параметри в тілі запиту замість query-string — стане в нагоді, коли список фільтрів завеликий для URL (або просто зручніше збирати JSON на клієнті). Body має пріоритет над query-string, форма відповіді ідентична.

Пропозиції

Отримання списку пропозицій

Приклад запиту:

<?php
// Get offers list filtered by product

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/offers?product_id=130', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get offers list filtered by product

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/offers?product_id=130');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/offers?product_id=130' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get offers list filtered by product
const url = 'https://api.kwiga.com/offers?product_id=130';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get offers list filtered by product
import requests

url = 'https://api.kwiga.com/offers?product_id=130'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/offers?product_id=130",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
      "id": 273,
      "unique_offer_code": "nELQzbLRPmzo",
      "url": "https://kwiga.com/o/nELQzbLRPmzo",
      "title": "This is paid offer for my course",
      "description": "<p style=\"text-align: left\">Description with html.</p>",
      "short_description": "<p style=\"text-align: left\">Shot description with html.</p>",
      "price_type": {
        "id": 2,
        "name": "Платно"
      },
      "price": {
        "amount": "10.00",
        "amount_rounded": "10.00",
        "amount_formatted": "10.00 usd",
        "amount_formatted_code": "$10.00",
        "amount_formatted_code_short": "$10.00",
        "currency": {
          "id": 5,
          "code": "USD",
          "html_code": "$",
          "html_letter_code": "usd",
          "is_volatile": false
        }
      },
      "price_discounted": {
        "amount": 5,
        "amount_rounded": "5.00",
        "amount_formatted": "5.00 usd",
        "amount_formatted_code": "$5.00",
        "amount_formatted_code_short": "$5.00",
        "currency": {
          "id": 5,
          "code": "USD",
          "html_code": "$",
          "html_letter_code": "usd",
          "is_volatile": false
        }
      },
      "discount": {
        "id": 3,
        "price_discounted": 5,
        "limit_type": {
          "id": 1,
          "name": "Безстроково"
        },
        "start_at": "2023-12-28T14:41:00.000000Z",
        "start_at_utc": "2023-12-28T12:41:00.000000Z",
        "start_timezone_id": 371,
        "end_at": "2024-01-03T14:41:00.000000Z",
        "end_type": 3,
        "end_at_utc": "2024-01-03T12:41:00.000000Z",
        "end_timezone_id": 371,
        "sales_limit": null,
        "sales": null,
        "is_active": true,
        "is_available": true,
        "created_at": "2023-12-28T12:41:58.000000Z",
        "updated_at": "2023-12-28T12:41:58.000000Z",
        "start_timezone": {
          "id": 371,
          "name": "Europe/Kyiv",
          "name_full": "Europe/Kyiv (UTC+03:00)",
          "value": "UTC+03:00"
        },
        "end_timezone": {
          "id": 371,
          "name": "Europe/Kyiv",
          "name_full": "Europe/Kyiv (UTC+03:00)",
          "value": "UTC+03:00"
        }
      },
      "has_subscription": false,
      "limit_type": {
        "id": 1,
        "name": "Необмежено"
      },
      "limit_of_sales": null,
      "is_active": true,
      "is_draft": false,
      "validity_start": {
        "type": "validity_start",
        "duration_type_id": 1,
        "date_at": "2023-12-28T14:40:00.000000Z",
        "date_at_utc": "2023-12-28T12:40:00.000000Z",
        "timezone_id": 371,
        "timezone": {
          "id": 371,
          "name": "Europe/Kyiv",
          "name_full": "Europe/Kyiv (UTC+03:00)",
          "value": "UTC+03:00"
        },
        "after_months": 0,
        "after_days": 0,
        "specific_time": null
      },
      "validity_end": {
        "type": "validity_end",
        "duration_type_id": 3,
        "date_at": "2023-12-28T14:40:00.000000Z",
        "date_at_utc": "2023-12-28T12:40:00.000000Z",
        "timezone_id": 371,
        "timezone": {
          "id": 371,
          "name": "Europe/Kyiv",
          "name_full": "Europe/Kyiv (UTC+03:00)",
          "value": "UTC+03:00"
        },
        "after_months": 0,
        "after_days": 0,
        "specific_time": null
      },
      "products": [
        {
          "id": 130,
          "productable_id": 38,
          "productable_type": "course",
          "title": "This is my course",
          "url": "https://kwiga.com/courses/test-fail"
        }
      ]
    },
    {
      "id": 272,
      "unique_offer_code": "2obtMsUEujcy",
      "url": "https://kwiga.com/o/2obtMsUEujcy",
      "title": "This is free offer for my course",
      "description": null,
      "short_description": null,
      "price_type": {
        "id": 1,
        "name": "Безкоштовно"
      },
      "price": {
        "amount": "0.00",
        "amount_rounded": "0.00",
        "amount_formatted": "0.00 usd",
        "amount_formatted_code": "$0.00",
        "amount_formatted_code_short": "$0.00",
        "currency": {
          "id": 5,
          "code": "USD",
          "html_code": "$",
          "html_letter_code": "usd",
          "is_volatile": false
        }
      },
      "price_discounted": {
        "amount": 0,
        "amount_rounded": "0.00",
        "amount_formatted": "0.00 usd",
        "amount_formatted_code": "$0.00",
        "amount_formatted_code_short": "$0.00",
        "currency": {
          "id": 5,
          "code": "USD",
          "html_code": "$",
          "html_letter_code": "usd",
          "is_volatile": false
        }
      },
      "discount": null,
      "has_subscription": false,
      "limit_type": {
        "id": 1,
        "name": "Необмежено"
      },
      "limit_of_sales": null,
      "is_active": true,
      "is_draft": false,
      "validity_start": {
        "type": "validity_start",
        "duration_type_id": 1,
        "date_at": "2023-12-27T12:45:00.000000Z",
        "date_at_utc": "2023-12-27T10:45:00.000000Z",
        "timezone_id": 371,
        "timezone": {
          "id": 371,
          "name": "Europe/Kyiv",
          "name_full": "Europe/Kyiv (UTC+03:00)",
          "value": "UTC+03:00"
        },
        "after_months": 0,
        "after_days": 0,
        "specific_time": null
      },
      "validity_end": {
        "type": "validity_end",
        "duration_type_id": 3,
        "date_at": "2023-12-27T12:45:00.000000Z",
        "date_at_utc": "2023-12-27T10:45:00.000000Z",
        "timezone_id": 371,
        "timezone": {
          "id": 371,
          "name": "Europe/Kyiv",
          "name_full": "Europe/Kyiv (UTC+03:00)",
          "value": "UTC+03:00"
        },
        "after_months": 0,
        "after_days": 0,
        "specific_time": null
      },
      "products": [
        {
          "id": 130,
          "productable_id": 38,
          "productable_type": "course",
          "title": "This is my course",
          "url": "https://kwiga.com/courses/test-fail"
        }
      ]
    }
  ],
  "links": {
    "first": "http://api.kwiga.local/offers?page=1",
    "last": "http://api.kwiga.local/offers?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "&laquo; Previous",
        "active": false
      },
      {
        "url": "http://api.kwiga.local/offers?page=1",
        "label": "1",
        "active": true
      },
      {
        "url": null,
        "label": "Next &raquo;",
        "active": false
      }
    ],
    "path": "http://api.kwiga.local/offers",
    "per_page": 15,
    "to": 1,
    "total": 2
  }
}

GET https://api.kwiga.com/offers

URL Parameters

page integer optional
Номер сторінки
per_page integer optional
Кількість елементів вибірки
product_id integer optional
Фільтр по продукту
filters object optional
Filter parameters
search string optional
Пошуковий запит

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Отримання пропозиції

Приклад запиту:

<?php
// Get offer by ID

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/offers/:offer', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get offer by ID

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/offers/:offer');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/offers/:offer' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get offer by ID
const url = 'https://api.kwiga.com/offers/:offer';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get offer by ID
import requests

url = 'https://api.kwiga.com/offers/:offer'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/offers/:offer",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": {
    "id": 1,
    "unique_offer_code": "6rT1wj99lbZV",
    "title": "Offer #1",
    "limit_type": {
      "id": 2,
      "name": "Certain amount"
    },
    "limit_of_sales": 20,
    "purchases_count": 1,
    "sales_left": 19
  }
}

GET https://api.kwiga.com/offers/:offer

Структура відповіді

Маркетинг. Розсилка

Список списків контактів

Приклад запиту:

<?php
// Get contact lists

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/mailing/contact-lists', $options);

$result = json_decode($response->getBody());
?>


# ---
# Paginated example
# ---

<?php
// Get contact lists with pagination

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/mailing/contact-lists?page=1&per_page=15', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get contact lists

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/mailing/contact-lists');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Paginated example
# ---

<?php
// Get contact lists with pagination

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/mailing/contact-lists?page=1&per_page=15');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/mailing/contact-lists' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Paginated example
# ---

curl --location --request GET 'https://api.kwiga.com/mailing/contact-lists?page=1&per_page=15' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get contact lists
const url = 'https://api.kwiga.com/mailing/contact-lists';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Paginated example
# ---

// Get contact lists with pagination
const url = 'https://api.kwiga.com/mailing/contact-lists?page=1&per_page=15';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get contact lists
import requests

url = 'https://api.kwiga.com/mailing/contact-lists'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Paginated example
# ---

# Get contact lists with pagination
import requests

url = 'https://api.kwiga.com/mailing/contact-lists?page=1&per_page=15'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/mailing/contact-lists",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Paginated example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/mailing/contact-lists?page=1&per_page=15",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": [
    {
        "id": 2,
        "title": "Пример 2",
        "description": "Описание",
        "is_default": false,
        "created_at": "2022-04-28T13:22:44.000000Z",
        "updated_at": "2022-04-28T13:22:44.000000Z"
    },
    {
        "id": 1,
        "title": "Мой первый список",
        "description": "Данный список создаётся автоматически с вашим контактом внутри. Вы можете его отредактировать под свои потребности.",
        "is_default": true,
        "created_at": "2022-04-28T12:47:08.000000Z",
        "updated_at": "2022-04-28T12:47:08.000000Z"
    }
  ],
  "links": {
        "first": "https://api.kwiga.com/mailing/contact-lists?page=1",
        "last": "https://api.kwiga.com/mailing/contact-lists?page=2",
        "prev": null,
        "next": "https://api.kwiga.com/mailing/contact-lists?page=2"
   },
   "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 2,
        "links": [
            {
                "url": null,
                "label": "&laquo; translation missing: ua.pagination_prev",
                "active": false
            },
            {
                "url": "https://api.kwiga.com/mailing/contact-lists?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": "https://api.kwiga.com/mailing/contact-lists?page=2",
                "label": "2",
                "active": false
            },
            {
                "url": "https://api.kwiga.com/mailing/contact-lists?page=2",
                "label": "translation missing: ua.pagination_next &raquo;",
                "active": false
            }
        ],
        "path": "https://api.kwiga.com/mailing/contact-lists",
        "per_page": 2,
        "to": 2,
        "total": 3
    }
}

GET https://api.kwiga.com/mailing/contact-lists

URL Parameters

page integer optional
Номер сторінки
limit integer optional
Кількість елементів вибірки

Структура відповіді

Елементи поточної сторінки.

Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Отримання списку контактів

Приклад запиту:

<?php
// Get contact list by ID

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/mailing/contact-lists/:list', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get contact list by ID

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/mailing/contact-lists/:list');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/mailing/contact-lists/:list' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get contact list by ID
const url = 'https://api.kwiga.com/mailing/contact-lists/:list';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get contact list by ID
import requests

url = 'https://api.kwiga.com/mailing/contact-lists/:list'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/mailing/contact-lists/:list",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
  "data": {
    "id": 1,
    "title": "Мой первый список",
    "description": "Данный список создаётся автоматически с вашим контактом внутри. Вы можете его отредактировать под свои потребности.",
    "is_default": true,
    "created_at": "2022-04-28T12:47:08.000000Z",
    "updated_at": "2022-04-28T12:47:08.000000Z",
    "contacts": [
        {
            "id": 1,
            "first_name": "Alex",
            "middle_name": null,
            "last_name": "Иванович Burt",
            "name": "Иванович Burt Alex",
            "sex": null,
            "age": null,
            "email": "admin@grandstep.com.ua",
            "phone_country": "UA",
            "phone_number": "983721222",
            "city": null,
            "phone": "+380983721222",
            "created_at": "2022-04-28T12:47:07.000000Z",
            "updated_at": "2022-04-28T12:47:08.000000Z"
        }
    ],
    "statistic": {
        "contact_list_id": 1,
        "count_contacts": 1
    }
}
}

GET https://api.kwiga.com/mailing/contact-lists/:list

Структура відповіді

Створення списку контактів

Приклад запиту:

<?php
// Create contact list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'title' => 'Название группы',
        'description' => 'описание',
    ],
];

$response = $client->request('POST', '/mailing/contact-lists/', $options);

$result = json_decode($response->getBody());
?>
<?php
// Create contact list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/mailing/contact-lists/');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'title' => 'Название группы',
    'description' => 'описание',
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/mailing/contact-lists/' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"title":"Название группы","description":"описание"}'
// Create contact list
const url = 'https://api.kwiga.com/mailing/contact-lists/';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'title': 'Название группы',
    'description': 'описание'
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Create contact list
import requests

url = 'https://api.kwiga.com/mailing/contact-lists/'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'title': 'Название группы',
    'description': 'описание',
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/mailing/contact-lists/",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "title": "Название группы",
    "description": "описание"
  }
}

Приклад відповіді:

{
    "data": {
        "id": 3,
        "title": "fsdaf asfsf dsafsda fsda",
        "description": "1dsad sadsa ds dsa",
        "is_default": false,
        "created_at": "2022-05-04T09:57:37.000000Z",
        "updated_at": "2022-05-04T09:57:37.000000Z"
    }
}

POST https://api.kwiga.com/mailing/contact-lists

Request

title string required
Назва
description string optional
Опис

Структура відповіді

Додавання контактів до списку

Приклад запиту:

<?php
// Bulk add contacts to lists

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'contacts[]' => 1,
        'contacts[]' => 2,
        'contact_lists[]' => 1,
    ],
];

$response = $client->request('POST', '/mailing/contact-lists/bulk-contacts', $options);

$result = json_decode($response->getBody());
?>
<?php
// Bulk add contacts to lists

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/mailing/contact-lists/bulk-contacts');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'contacts[]' => 1,
    'contacts[]' => 2,
    'contact_lists[]' => 1,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/mailing/contact-lists/bulk-contacts' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"contacts[]":2,"contact_lists[]":1}'
// Bulk add contacts to lists
const url = 'https://api.kwiga.com/mailing/contact-lists/bulk-contacts';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'contacts[]': 1,
    'contacts[]': 2,
    'contact_lists[]': 1
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Bulk add contacts to lists
import requests

url = 'https://api.kwiga.com/mailing/contact-lists/bulk-contacts'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'contacts[]': 1,
    'contacts[]': 2,
    'contact_lists[]': 1,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/mailing/contact-lists/bulk-contacts",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "contacts[]": 2,
    "contact_lists[]": 1
  }
}

Приклад відповіді:

{
    "success": true
}

POST https://api.kwiga.com/mailing/contact-lists/bulk-contacts

URL Parameters

contacts integer[] required
Контакти (ID)
contact_lists integer[] required
Списки контактів (ID)

Структура відповіді

Тіло відповіді повертається без обгортки.

Продажі. Купони

Список купонів

Приклад запиту:

<?php
// Get coupons list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/coupons', $options);

$result = json_decode($response->getBody());
?>


# ---
# Filtered example
# ---

<?php
// Get coupons list with date filters

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/coupons?filters[date_from]=2022-04-15&filters[date_to]=2022-04-27', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get coupons list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/coupons');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>


# ---
# Filtered example
# ---

<?php
// Get coupons list with date filters

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/coupons?filters[date_from]=2022-04-15&filters[date_to]=2022-04-27');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/coupons' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'


# ---
# Filtered example
# ---

curl --location --globoff --request GET 'https://api.kwiga.com/coupons?filters[date_from]=2022-04-15&filters[date_to]=2022-04-27' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get coupons list
const url = 'https://api.kwiga.com/coupons';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });


# ---
# Filtered example
# ---

// Get coupons list with date filters
const url = 'https://api.kwiga.com/coupons?filters[date_from]=2022-04-15&filters[date_to]=2022-04-27';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get coupons list
import requests

url = 'https://api.kwiga.com/coupons'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()


# ---
# Filtered example
# ---

# Get coupons list with date filters
import requests

url = 'https://api.kwiga.com/coupons?filters[date_from]=2022-04-15&filters[date_to]=2022-04-27'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/coupons",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}


# ---
# Filtered example
# ---

{
  "method": "GET",
  "url": "https://api.kwiga.com/coupons?filters[date_from]=2022-04-15&filters[date_to]=2022-04-27",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": [
        {
            "id": 3,
            "code": "KwigaSMVT-MD",
            "reward": 30,
            "is_fixed": true,
            "is_disposable": false,
            "used_amount": 0,
            "total_uses": 10,
            "total_uses_per_user": 1,
            "user": {
                "id": 3,
                "avatar_url": "https://someurl.com",
                "hash": "EL9p2ycy4Ypga715",
                "name": "Test User",
                "email": "test@example.com",
                "tag_name": "TestUser"
            },
            "created_at": "2022-04-28T09:46:11.000000Z",
            "updated_at": "2022-04-28T09:46:11.000000Z",
            "deleted_at": null
        }
    ]
}

GET https://api.kwiga.com/coupons

URL Parameters

filters object optional
Filter parameters
date_from datetime optional
Фільтр за датою створення. Параметр 'від'
date_to datetime optional
Фільтр за датою створення. Параметр 'до'

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Отримання купона

Приклад запиту:

<?php
// Get coupon by ID

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/coupons/:coupon', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get coupon by ID

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/coupons/:coupon');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/coupons/:coupon' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get coupon by ID
const url = 'https://api.kwiga.com/coupons/:coupon';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get coupon by ID
import requests

url = 'https://api.kwiga.com/coupons/:coupon'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/coupons/:coupon",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": {
        "id": 1,
        "code": "KwigaSMVT-MD",
        "reward": 30,
        "is_fixed": true,
        "is_disposable": false,
        "used_amount": 0,
        "total_uses": 10,
        "total_uses_per_user": 1,
        "created_at": "2022-04-28T09:46:11.000000Z",
        "updated_at": "2022-04-28T09:46:11.000000Z",
        "deleted_at": null
    }
}

GET https://api.kwiga.com/coupons/:coupon

Структура відповіді

Створити купон

Приклад запиту:

<?php
// Create coupon

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'code' => 'MY-COUPON',
        'reward' => 0,
        'type_discount[id]' => 'discount_with_percent',
        'expires_at' => ,
        'timezone[id]' => 375,
        'total_uses' => 0,
        'total_uses_per_user' => 0,
    ],
];

$response = $client->request('POST', '/coupons', $options);

$result = json_decode($response->getBody());
?>
<?php
// Create coupon

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/coupons');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'code' => 'MY-COUPON',
    'reward' => 0,
    'type_discount[id]' => 'discount_with_percent',
    'expires_at' => ,
    'timezone[id]' => 375,
    'total_uses' => 0,
    'total_uses_per_user' => 0,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/coupons' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"code":"MY-COUPON","reward":0,"type_discount[id]":"discount_with_percent","expires_at":null,"timezone[id]":375,"total_uses":0,"total_uses_per_user":0}'
// Create coupon
const url = 'https://api.kwiga.com/coupons';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'code': 'MY-COUPON',
    'reward': 0,
    'type_discount[id]': 'discount_with_percent',
    'expires_at': ,
    'timezone[id]': 375,
    'total_uses': 0,
    'total_uses_per_user': 0
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Create coupon
import requests

url = 'https://api.kwiga.com/coupons'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'code': 'MY-COUPON',
    'reward': 0,
    'type_discount[id]': 'discount_with_percent',
    'expires_at': ,
    'timezone[id]': 375,
    'total_uses': 0,
    'total_uses_per_user': 0,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/coupons",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "code": "MY-COUPON",
    "reward": 0,
    "type_discount[id]": "discount_with_percent",
    "expires_at": null,
    "timezone[id]": 375,
    "total_uses": 0,
    "total_uses_per_user": 0
  }
}

Приклад відповіді:

{
    "data": {
        "id": 8,
        "code": "MY-COUPON",
        "reward": 100,
        "type_discount": {
            "id": "discount_with_percent",
            "name": "In % of order/offer value"
        },
        "type_date_expired": {
            "id": "indefinite_action",
            "name": "Never expires"
        },
        "type_total_count_used": {
            "id": "certain_amount",
            "name": "A certain amount"
        },
        "type_used_contact_lists": {
            "id": "all_users",
            "name": "All users"
        },
        "is_fixed": false,
        "is_active": true,
        "used_amount": 0,
        "has_infinite_used": true,
        "has_access_contact_lists": false,
        "total_uses": 0,
        "total_uses_per_user": 1,
        "contact_lists": [],
        "type": {
            "id": 4,
            "title": "Offers"
        },
        "expires_at": null,
        "expires_at_utc": null,
        "timezone_id": null,
        "currency_id": null,
        "currency": null,
        "created_at": "2024-01-09T16:17:55.000000Z",
        "updated_at": "2024-01-09T16:17:55.000000Z",
        "deleted_at": null
    }
}

POST https://api.kwiga.com/coupons

URL Parameters

code string optional
Код купону. Якщо не вказати, код згенерується автоматично
type_discount.id string required
Тип знижки: відсоток від суми - discount_with_percent або фіксована знижка - discount_with_currency
Example: discount_with_percent or discount_with_currency
expires_at datetime optional
Термін дії купона. Якщо null чи не вказувати, то безстроково
Example: 2024-12-31 or 2024-12-31 23:59:59
timezone.id integer optional
Таймзона дати терміну дії купона
total_uses integer optional
Ліміт на кількість використань купона. 0 - без обмежень
total_uses_per_user integer optional
Ліміт на кількість використань купона одним користувачем

Структура відповіді

Перевірка купона

Приклад запиту:

<?php
// Check coupon validity

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
    'json' => [
        'code' => 'VAJ6-EA',
        'price' => 65,
    ],
];

$response = $client->request('POST', '/coupons/check', $options);

$result = json_decode($response->getBody());
?>
<?php
// Check coupon validity

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/coupons/check');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

$data = [
    'code' => 'VAJ6-EA',
    'price' => 65,
];

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request POST 'https://api.kwiga.com/coupons/check' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>' \
--data-raw '{"code":"VAJ6-EA","price":65}'
// Check coupon validity
const url = 'https://api.kwiga.com/coupons/check';

const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
  body: JSON.stringify({
    'code': 'VAJ6-EA',
    'price': 65
  })
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Check coupon validity
import requests

url = 'https://api.kwiga.com/coupons/check'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

data = {
    'code': 'VAJ6-EA',
    'price': 65,
}

response = requests.post(url, headers=headers, json=data)

result = response.json()
{
  "method": "POST",
  "url": "https://api.kwiga.com/coupons/check",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "body": {
    "code": "VAJ6-EA",
    "price": 65
  }
}

Приклад відповіді:

{
  "data": {
    "can_use": true,
    "discount": 6.5
  }
}

POST https://api.kwiga.com/coupons/check

Перевіряє купон на існування і можливість використання (не вичерпано ліміт / не закінчився термін дії).
Зверніть увагу: при використанні на checkout сторінці купон може не застосовуватися при наступних умовах: якщо на пропозиції вимкнено використання купонів/даного купона; на купоні є обмеження списками контактів; користувач вже використовував купон і на купоні є обмеження по кількості використань.

Parameters

code string required
Код купона для перевірки
price number optional
Ціна для розрахунку знижки. Якщо вказати, то у відповіді буде повернуто значення знижки у полі discount

Структура відповіді

Сертифікати

Отримання сертифікат за номером

Приклад запиту:

<?php
// Get certificate by number

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/certificates/by-number/:number', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get certificate by number

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/certificates/by-number/:number');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/certificates/by-number/:number' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get certificate by number
const url = 'https://api.kwiga.com/certificates/by-number/:number';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get certificate by number
import requests

url = 'https://api.kwiga.com/certificates/by-number/:number'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/certificates/by-number/:number",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": {
        "id": 55,
        "uuid": "262e7042-1933-42a9-ba01-e96d7d5110dc",
        "cabinet_id": 1,
        "user_id": 273,
        "issued_at": "2023-07-25T15:36:44.000000Z",
        "number": "1986-0001",
        "url": "http://cabinet-1.kwiga.local/certificates/262e7042-1933-42a9-ba01-e96d7d5110dc",
        "created_at": "2023-07-25T15:36:44.000000Z",
        "updated_at": "2023-07-25T15:36:44.000000Z",
        "finished_at": "2023-07-25T15:36:44.000000Z",
        "points": 120.5,
        "user": {
            "id": 273,
            "name": "test name",
            "email": "test-student@kwiga.com"
        },
        "certificateble_title": "Test course"
    }
}

GET https://api.kwiga.com/certificates/by-number/:number

Структура відповіді

Таймзони

Отримання списку таймзон

Приклад запиту:

<?php
// Get timezones list

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.kwiga.com']);

$options = [
    'headers' => [
        'Accept' => 'application/json',
        'Token' => '<Token>',
        'Cabinet-Hash' => '<Cabinet-Hash>',
    ],
];

$response = $client->request('GET', '/timezones', $options);

$result = json_decode($response->getBody());
?>
<?php
// Get timezones list

$headers = [
    'Accept: application/json',
    'Token: <Token>',
    'Cabinet-Hash: <Cabinet-Hash>',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.kwiga.com/timezones');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response);
?>
curl --location --request GET 'https://api.kwiga.com/timezones' \
--header 'Content-Type: application/json' \
--header 'Token: <Token>' \
--header 'Cabinet-Hash: <Cabinet-Hash>'
// Get timezones list
const url = 'https://api.kwiga.com/timezones';

const options = {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
  },
};

fetch(url, options)
  .then(response => response.json())
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Error:', error);
  });
# Get timezones list
import requests

url = 'https://api.kwiga.com/timezones'

headers = {
    'Accept': 'application/json',
    'Token': '<Token>',
    'Cabinet-Hash': '<Cabinet-Hash>',
}

response = requests.get(url, headers=headers)

result = response.json()
{
  "method": "GET",
  "url": "https://api.kwiga.com/timezones",
  "headers": {
    "Token": "<Token>",
    "Cabinet-Hash": "<Cabinet-Hash>",
    "Accept": "application/json"
  }
}

Приклад відповіді:

{
    "data": [
        {
            "id": 1,
            "name": "Asia/Kabul",
            "name_full": "Asia/Kabul (UTC+04:30)",
            "value": "UTC+04:30"
        },
        {
            "id": 2,
            "name": "Europe/Tirane",
            "name_full": "Europe/Tirane (UTC+02:00)",
            "value": "UTC+02:00"
        },
        ...
    ]
}

GET https://api.kwiga.com/timezones

Структура відповіді

Елементи поточної сторінки.
Посилання на інші сторінки того самого списку.
Стан пагінації для поточного запиту (номер сторінки, загальна кількість тощо).

Схеми даних

Перевикористовувані об'єкти відповідей API. Поля з позначкою conditional з'являються у відповіді лише за виконання документованої умови (прапор запиту, опціональний include чи певний ендпоінт).

Контакти

Contact

id integer
Унікальний ідентифікатор контакту
user_id integer nullable
Ідентифікатор пов'язаного користувача (null, якщо у контакту ще немає акаунту користувача)
email string
Email контакту
first_name string nullable
Ім’я. Пусто, якщо не заповнено.
middle_name string nullable
По батькові. Пусто, якщо не заповнено.
last_name string nullable
Прізвище. Пусто, якщо не заповнено.
name string nullable
Відображуване ім'я, зібране з імені/по-батькові/прізвища у порядку, прийнятому в кабінеті.
phone string nullable
Телефон у тому вигляді, в якому він збережений. Пусто, якщо не вказано.
crm_url string nullable
Пряме посилання на сторінку контакту у CRM вашого кабінету.
created_at datetime
Момент створення контакту
tags FlowTag[] conditional
Conditional: Included when tags are loaded for the contact.
Теги, прикріплені до контакту
last_activity_at datetime conditional
Conditional: Included when the contact has activity history in the cabinet.
Момент останньої активності контакту в кабінеті
first_visit Visit conditional
Conditional: Included when the contact has at least one recorded visit.
Перший зафіксований візит контакту
utm UtmList conditional
Conditional: Included when UTM data has been collected for the contact.
Агрегована UTM-атрибуція за всіма візитами контакту
utm_visits Visit[] conditional
Conditional: Returned only on `GET /contacts/{id}` — not present on the contacts list.
Усі UTM-відстежені візити контакту
offers Offer[] conditional
Conditional: Included when `with_orders=true` is passed.
Пропозиції, куплені контактом
orders Order[] conditional
Conditional: Included when `with_orders=true` is passed.
Замовлення, розміщені контактом
Conditional: Included when the contact has custom field values configured.
Кастомні поля кабінету, заповнені для цього контакту
Conditional: Included when `with_certificates=true` is passed.
Інформація про випущені сертифікати за продуктами контакту
Telegram-акаунти, прив'язані до користувача контакту. Заповнюється, лише якщо користувач бере участь у продукті із закритою групою цього кабінету; інакше — порожній масив.

ContactIdentifier

id integer
Ідентифікатор контакту.
user_id integer nullable
Ідентифікатор користувача, пов’язаного з контактом. Пусто, якщо контакт не привʼязаний до користувача.
first_name string nullable
Ім’я. Пусто, якщо не заповнено.
last_name string nullable
Прізвище. Пусто, якщо не заповнено.
name string nullable
Повне ім'я контакту (ім'я + прізвище), якщо вказано
email string nullable
Email контакту (може бути прихований для не-власників)
phone string nullable
Телефон контакту (може бути прихований для не-власників)

ContactAdditionalField

field ContactField conditional
Conditional: Included when the field definition is part of the response.
Визначення кастомного поля кабінету
value string nullable
Сире значення, збережене для цього поля на контакті
display_value string nullable
Читаюче відображення значення (наприклад, локалізований enum-label)

ContactField

id integer
Ідентифікатор поля.
title string
Локалізована назва поля
is_local boolean
Чи є поле локальним для кабінету (не частиною глобальної схеми)
input_title string conditional
Conditional: Returned only for viewers with extended access to the field.
Підпис поля, який бачить той, хто заповнює.
format string conditional
Conditional: Returned only for viewers with extended access to the field.
Токен формату вводу для поля
validation string conditional
Conditional: Returned only for viewers with extended access to the field.
Вираз правила валідації
default_value string conditional
Conditional: Returned only for viewers with extended access to the field.
Значення, що підставляється за замовчуванням. Пусто, якщо значення за замовчуванням немає.
placeholder string conditional
Conditional: Returned only for viewers with extended access to the field.
Підказка всередині порожнього поля. Пусто, якщо підказку не задано.
type string conditional
Conditional: Returned only for viewers with extended access to the field.
Ідентифікатор типу поля (text, number, date, enum тощо)
is_default boolean conditional
Conditional: Returned only for viewers with extended access to the field.
Поле створене платформою і є в усіх кабінетах — на відміну від полів, доданих експертом.
is_required boolean conditional
Conditional: Returned only for viewers with extended access to the field.
Поле обовʼязкове до заповнення.
is_at_import boolean conditional
Conditional: Returned only for viewers with extended access to the field.
Поле доступне для заповнення при імпорті контактів.
visible_type Enum conditional
Conditional: Returned only for viewers with extended access to the field.
Кому поле видно.
order integer conditional
Conditional: Returned only for viewers with extended access to the field.
Порядок відображення всередині кабінету
values object[] conditional
Conditional: Returned for viewers with extended access when value options are configured.
Допустимі значення для enum-полів
name_order_type object conditional
Conditional: Returned for viewers with extended access when the field is the default full-name field.
Порядок відображення імені/прізвища, заданий у кабінеті

ContactList

id integer
Ідентифікатор списку.
cabinet_id integer conditional
Conditional: Included when the cabinet relation is part of the response.
Ідентифікатор кабінету-власника
user_id integer conditional
Conditional: Included when the owning user is part of the response.
Ідентифікатор користувача-власника
title string
Назва списку.
description string nullable
Опис списку. Пусто, якщо не заповнено.
is_default boolean
True для вбудованого списку за замовчуванням у кабінеті
is_active boolean
Список увімкнений і використовується в розсилках.
is_segment boolean
True, якщо список — динамічний сегмент, керований фільтрами
is_new boolean
Маркер для списків, створених після 2023-08-15 (використовується в UI)
paused_at datetime nullable
Коли список поставили на паузу. Пусто, якщо він не на паузі.
created_at datetime
Коли список було створено.
updated_at datetime
Коли список змінювали останній раз.
contacts_count integer conditional
Conditional: Included when contact counts are available for the list.
Скільки контактів у списку.
Conditional: Returned on the single-list endpoint; not present on the list response.
Контакти списку у скороченому вигляді.
Conditional: Included when list statistics are part of the response.
Зведена статистика за списком.
Conditional: Included when the list's email campaigns are part of the response.
Email-розсилки, пов'язані зі списком
segment_settings object conditional
Conditional: Present for dynamic segments; empty object for regular lists.
Правила фільтрації, що визначають членство в сегменті

ContactListStatistic

contact_list_id integer
Ідентифікатор списку контактів, до якого належить статистика.
count_contacts integer
Кількість контактів, які зараз у списку

FlowTag

id integer
Ідентифікатор тегу.
name string
Назва тегу.
contacts_count integer conditional
Conditional: Included when contact counts are available for the tag.
Скільки контактів позначено цим тегом

Курси

Course

id integer
Ідентифікатор курсу.
product_id integer
Ідентифікатор пов'язаного продукту (для пошуку через /contacts/{contact}/products)
type string
Ідентифікатор типу курсу (course / marathon / тощо)
title string
Назва курсу (фолбек на `Course
slug string nullable
URL-сумісний slug
preview FileSimple conditional
Conditional: Included when the course has a preview image.
Обкладинка курсу. Пусто, якщо обкладинку не задано.
url string
Публічне посилання на лендінг курсу
status string
Ідентифікатор статусу публікації курсу (draft, published тощо)
offers Offer[] conditional
Conditional: Included when the course offers are part of the response.
Тарифи, за якими продається курс.
info_units InfoUnit[] conditional
Conditional: Included when course info units are part of the response.
Інформаційні блоки курсу — з них зібрано вміст його уроків.
Conditional: Included when the course program is part of the response (rendered as a tree).
Програма курсу — дерево модулів і уроків.

CourseIdentify

id integer
Ідентифікатор курсу.
product_id integer
Ідентифікатор пов'язаного продукту
type_id integer
Ідентифікатор типу курсу (course / marathon / тощо)
title string
Назва курсу.
Conditional: Included when the course's lesson identity payloads are loaded.
Короткі ідентифікаційні записи уроків курсу
url string
Публічне посилання на лендінг курсу
preview_url string conditional
Conditional: Included when the course preview is loaded.
URL прев'ю-картинки курсу (або плейсхолдер за замовчуванням, якщо зображення не задано)
Conditional: Included when offer identity payloads tied to the course are loaded.
Короткі ідентифікаційні записи пропозицій, пов'язаних з курсом

CourseLessonIdentify

id integer
Ідентифікатор уроку.
title string
Назва уроку.
course CourseIdentify conditional
Conditional: Included when the parent course identity is part of the response.
Курс, якому належить урок.
Conditional: Included when the lesson's module branch is part of the response. Null for top-level lessons.
Модуль, у якому лежить урок. Пусто в уроків, що лежать у курсі самі по собі, поза модулями.

CourseLessonSimple

id integer
Ідентифікатор уроку.
course_id integer
Ідентифікатор курсу, якому належить урок.
type_id integer
Ідентифікатор типу уроку
status_id integer
Ідентифікатор статусу публікації уроку
number string nullable
Видимий користувачу номер уроку (токен порядку глави)
title string
Назва уроку.
slug string nullable
Частина адреси уроку — з неї складається посилання, за яким урок відкриває учень.
url string
Публічне посилання на урок
Conditional: Included when lesson quizzes are part of the response.
Квізи, прикріплені до уроку
course CourseIdentify conditional
Conditional: Included when the parent course summary is part of the response.
Ідентифікаційні дані батьківського курсу
Зведення батьківського модуля; null для уроків верхнього рівня

CourseModuleSimple

id integer
Ідентифікатор модуля.
course_id integer
Ідентифікатор курсу, якому належить модуль.
number string nullable
Видимий користувачу номер модуля / токен порядку
title string
Назва модуля.

CourseProgram

id integer
Ідентифікатор вузла дерева (course_node.id). Нюанс для info-section вузлів: поки секції не є повноцінними course_node, тут приходить info_sections.id; після апдейту рушія курсів сюди прийде справжній course_node.id — контракт при цьому не зміниться.
order integer
Позиція серед сусідніх вузлів
Поліморфний тип нижчележачої сутності — course_module, course_lesson, у секційних вузлів завжди info_section. Значення стабільні між версіями, можна матчити напряму.
Ідентификатор самої сутності вузла — уроку або модуля, залежно від того, що стоїть у course_nodeble_type. Не те саме, що id: той адресує місце в дереві, а ця сутність може стояти в дереві не один раз.
course_id integer
Ідентифікатор курсу, якому належить вузол.
title string
Назва нижчележачої сутності
preview FileSimple conditional
Conditional: Included when the node has a preview image attached (module/lesson nodes only).
Обкладинка вузла. Пусто, якщо обкладинку не задано.
Вкладені дочірні вузли тієї ж форми (порожній масив у листків): у module-вузлів — уроки; в ендпоінті списку учасників курсу (include=course_program) у lesson-вузлів — info-section вузли, в яких повертаються прив'язані квізи. У самих info-section вузлів поле зарезервоване під апдейт рушія курсів і поки завжди порожнє.
root_id integer nullable
Ідентифікатор кореня дерева (у info-section вузлів — null)
parent_id integer nullable
Ідентифікатор батьківського вузла; у info-section вузлів — course_node id батьківського course_lesson-вузла.
is_public boolean nullable
Вузол відкритий усім, у тому числі без купівлі курсу. Пусто, якщо до цього типу вузла ознака не застосовується.
url string nullable
Публічне посилання на вузол (якщо застосовно)
Conditional: Present only on info-section nodes (course users listing endpoint, `include=course_program`).
Лише у info-section вузлів. Квізи, прикріплені до секції, у вигляді масиву об'єктів QuizIdentifier. Об'єднує квізи з практик-info-units секції та lesson-level квізи з блоку "Список практик", що відображаються у секції, де цей блок розміщений. Пара (parent_lesson.course_nodeble_id, quizzes[i].id) збігається з quiz_progress[].course_lesson_id + quiz_id.

CourseProgress

course_id integer
Ідентифікатор курсу.
course_url string
Публічне посилання на лендінг курсу
title string
Назва курсу
lessons_count integer
Загальна кількість активних уроків у курсі
Кількість уроків, відкритих користувачем хоча б раз
Відсоток переглянутих користувачем уроків (0 – 100)
Кількість пройдених користувачем уроків
Відсоток пройдених користувачем уроків (0 – 100)
Відсоток проходження для відображення (обрізаний/округлений для UI)
quizzes_count integer
Загальна кількість квізів у курсі
Кількість пройдених користувачем квізів
Відсоток пройдених користувачем квізів (0 – 100)
scores_max number
Максимально можливий бал користувача за курс
Сумарний бал користувача за всіма квізами
Сумарний бал користувача на рівні продукту (поза квізами)
scores number
Сумарний бал користувача за всім курсом
is_completed boolean
True, якщо користувач повністю пройшов курс
completed_at datetime nullable
Момент повного завершення курсу; null поки курс у процесі
Урок, на якому користувач зараз знаходиться (null, якщо немає)
Наступний доступний користувачу урок (null у кінці курсу)
last_activity_at datetime nullable
Момент останньої активності користувача в курсі
True, якщо користувач пропустив блокувальні чекпоінти
Чекпоінт-квізи, до яких дійшов користувач
True, якщо користувач обійшов поточний drip-content бар'єр
True, якщо користувач обійшов наступний drip-content бар'єр
current_dripping_date datetime nullable
Коли розблоковується поточний drip-content бар'єр
next_dripping_date datetime nullable
Коли розблоковується наступний drip-content бар'єр

LessonProgress

lesson_id integer
Ідентифікатор уроку, до якого належить запис.
is_watched boolean
Учень відкривав цей урок.
is_completed boolean
Урок пройдено повністю — переглянуто і складено всі тести, що враховуються в прогресі.
Статус тестів цього уроку. Можливі значення (id / slug): 1 / WithoutQuizzes — тестів в уроці немає, 2 / NotStarted — не розпочаті, 3 / InProgress — розпочаті, 4 / Completed — складені, 5 / NotDetected — визначити не вдалося.
watching_start_at datetime nullable
Коли учень уперше відкрив урок. Пусто, якщо ще не відкривав.
watching_end_at datetime nullable
Коли учень переглянув урок до кінця. Пусто, поки перегляд не завершено.
quizzing_start_at datetime nullable
Коли учень почав перший тест цього уроку. Пусто, якщо тестів в уроці немає або він їх не починав.
quizzing_end_at datetime nullable
Коли учень склав останній тест уроку. Пусто, поки складено не всі.
completed_at datetime nullable
Коли урок було зараховано як пройдений. Пусто, поки він не пройдений.
created_at datetime nullable
Коли з’явився запис статистики по уроку — тобто коли учень звернувся до нього вперше.
updated_at datetime nullable
Коли статистика по уроку змінювалася останній раз.

ModuleProgress

module_id integer nullable
Ідентифікатор модуля. Якщо порожньо — це не модуль, а окремий рядок-зведення за уроками, які лежать у курсі самі по собі, поза модулями. Такі уроки входять у загальну цифру по курсу, тому без цього рядка сума за модулями не збіглася б із нею.
node_id integer nullable
Ідентифікатор місця модуля в дереві курсу — ті самі ідентифікатори, що у вузлів у course_program, тож за ними можна зібрати структуру предметів. Вкладеність виражена місцями в дереві, а не парами модулів, бо в базі зберігається саме дерево. Порожньо у рядка-зведення за уроками поза модулями: там немає модуля, а отже й місця в дереві.
parent_node_id integer nullable
Місце батьківського модуля в дереві. Порожньо у модуля верхнього рівня та у рядка-зведення. За цим полем відбирайте рядки для суми: складати треба лише ті, де воно порожнє, — інакше вкладений модуль порахується двічі, адже його уроки враховані і в ньому, і в батьківському.
title string nullable
Назва модуля. Порожньо у рядка-зведення за уроками поза модулями — у нього немає модуля, назви теж немає.
lessons_count integer
Скільки уроків модуля доступно цьому учню. Рахується так само, як загальна цифра по курсу: беруться лише уроки, вже відкриті учню та включені в його тариф. Урок, який учень ще не починав, теж входить у це число — тому новачок читається як «0 з N», а не «N з N».
Скільки з доступних уроків модуля учень уже відкривав.
Скільки уроків модуля учень пройшов повністю. З нього рахується відсоток нижче.
Частка пройдених уроків модуля, за тією ж формулою, що й однойменне поле в загальній цифрі по курсу.
quizzes_count integer
Скільки тестів модуля враховується в прогресі цього учня. Нуль може означати і те, що у тарифі учня тести взагалі не враховуються, — перш ніж читати його як «тестів немає», подивіться is_practice_configured поруч зі списком.
Скільки тестів модуля учень склав. Рахуються зараховані спроби, тому за кількох зарахованих спроб одного тесту число може перевищити quizzes_count — так само поводиться й загальна цифра по курсу.
Частка складених тестів модуля. Нуль, якщо тестів у модулі немає або вони не враховуються у цього тарифу.
Бали, набрані за тести цього модуля. Бали, нараховані вручну або автоматизаціями, привʼязані до покупки курсу цілком і до модуля не належать, тому сума цього поля за модулями може бути меншою за загальний scores по курсу.
scores_max float
Максимум балів за тести модуля, які враховуються в прогресі цього учня. Нуль, якщо тестів, що враховуються, у модулі немає.
is_watched boolean
Чи проглянуті всі уроки модуля. Рядок приходить на кожен модуль курсу, тому тут false і коли учень ще не починав модуль, і коли почав, але не закінчив. Розрізнити допомагає watching_start_at.
is_completed boolean
Модуль пройдено повністю: усі уроки проглянуті й усі враховувані тести складені.
Зведений статус тестів модуля — збирається зі статусів його уроків. Значення ті самі, що у lesson_progress.quizzing_status: 1 / WithoutQuizzes, 2 / NotStarted, 3 / InProgress, 4 / Completed, 5 / NotDetected.
watching_start_at datetime nullable
Коли учень уперше відкрив щось у цьому модулі. null — не починав узагалі.
watching_end_at datetime nullable
Коли учень проглянув останній урок модуля. Порожньо, поки проглянуті не всі.
quizzing_start_at datetime nullable
Коли учень уперше почав тест у цьому модулі.
quizzing_end_at datetime nullable
Коли учень завершив останній тест модуля. Порожньо, поки складені не всі враховувані тести.
completed_at datetime nullable
Коли модуль було зараховано пройденим. Порожньо, поки не пройдено.
created_at datetime nullable
Коли зʼявився і коли оновлювався запис про проходження цього модуля. Порожньо, якщо запису ще немає: учень не відкривав у модулі нічого. У рядка-зведення за уроками поза модулями теж порожньо.
updated_at datetime nullable
Коли статистика по модулю змінювалася останній раз. Пусто у модулів, які учень ще не відкривав — записи статистики для них не існує.

QuizProgress

Урок, у якому учень проходив тест. Разом з quiz_id утворює ключ запису — один тест може стояти в кількох уроках і в кожному рахується окремо.
quiz_id integer
Ідентифікатор тесту. Значення для фільтра за тестами можна взяти з /quiz-placements.
status Enum nullable
Статус останньої спроби (enum). Можливі кейси (id / slug): 1 / Passed, 2 / Failed, 3 / NeedRework, 4 / LastAttemptChangedStatus, 5 / InProcess, 6 / PendingCheck, 7 / DoesntStart.
scores float nullable
Набрані бали. Пусто, поки спробу не перевірено — у тестів, які перевіряє куратор, бали з’являються лише після перевірки.
scores_max float nullable
Максимально можливий бал, береться з поточної конфігурації квіза (а не з запису спроби).
started_at datetime nullable
Коли спробу було розпочато.
finished_at datetime nullable
Коли спробу було завершено. null, якщо спроба ще у процесі.
checked_at datetime nullable
Коли спробу було перевірено вручну. null для квізів без ручної перевірки або для неперевірених спроб.
last_activity_at datetime nullable
Остання активність учня у спробі (зміна відповіді, перехід тощо). Зручно для виявлення «завислих» in-progress спроб.
attempt_number integer
Порядковий номер повернутої спроби (від 1) серед чинних спроб учня по цій парі (course_lesson_id, quiz_id); скасовані новою спробою не рахуються. Оскільки ендпоінт завжди повертає останню чинну спробу, це значення дорівнює загальній кількості чинних спроб — корисно, коли потрібно знати, скільки разів учень намагався пройти квіз.

CourseUser

Акаунт учня, який бере участь у курсі — ідентифікатор, ім'я, email. Стабільно між запитами.
Пов'язаний запис CRM-контакту для цього учня.
course_points integer
Загальна кількість балів користувача в курсі
Зведення проходження уроків/модулів
Кількість уроків, доступних користувачу зараз
is_full_access boolean
True, якщо у користувача є доступ до всіх активних уроків курсу
Conditional: Included when the user has an aggregated subscription for the course.
Зведений доступ учня до продукту курсу — терміни, зібрані за всіма його оплатами разом. Відсутнє, якщо такого доступу немає.
Conditional: Included when include contains lesson_progress.
Масив об'єктів LessonProgress — per-lesson знімки перегляду/завершення для цього користувача. Повертається при include=lesson_progress.
Conditional: Included when include contains module_progress.
Масив об'єктів ModuleProgress — по рядку на КОЖНИЙ модуль курсу, одразу з метриками прогресу і знімком статусу перегляду. Модулі, які учень не відкривав, теж приходять — з нулями в метриках. Рядки не можна складати підряд — урок зарахований кожному модулю свого ланцюжка предків, тому з курсовими цифрами збігається лише сума рядків з parent_node_id = null. Повертається при include=module_progress.
Conditional: Included when include contains quiz_progress.
Масив об'єктів QuizProgress — по одному запису на пару (course_lesson_id, quiz_id) зі зведенням за останньою чинною спробою учня (скасовані новою спробою не враховуються). Повертається при include=quiz_progress.
is_practice_configured boolean conditional
Conditional: Included when include contains module_progress.
Чи увімкнена практика для цього учня хоча б одним із його тарифів. Ознака учня, а не модуля — налаштування живе на офері. При false усі модулі показують нуль квізів тому, що прогрес за квізами для нього не рахується взагалі (у тарифі практика вимкнена), а не тому, що квізів немає в курсі. При true нуль усе одно можливий з інших причин — у квізів знята галочка «враховувати в прогресі», вони лежать у розділах поза тарифом учня, або їхні уроки ще не відкриті. Розрізнити допомагає /quiz-placements із фільтром filters[is_used_in_progress]. Повертається при include = module_progress.

InfoUnit

id integer
Ідентифікатор інформаційного блоку.
title string
Заголовок блоку. Пусто, якщо заголовок не задано — наприклад, у блоку з одним зображенням.
description_formatted string nullable
Опис з rich-text форматуванням та обробленими посиланнями
True, якщо опис містить зовнішні посилання
position integer
Позиція елемента в батьківській колекції
type_id integer
Ідентифікатор типу info-unit

QuizIdentifier

id integer
Ідентифікатор тесту.
name string
Назва квіза
quiz_score QuizScore conditional
Conditional: Included where the endpoint explicitly loads quiz scoring — currently for quizzes inside `course_program`.
Бальна конфігурація квіза — актуальні значення з поточних налаштувань, а не історичний знімок спроби. Повертається лише там, де ендпоінт явно віддає бали (зараз — квізи всередині course_program). NULL, якщо у квіза немає запису балів. Усі значення нульові, якщо у квіза вимкнена бальна система.
is_used_in_progress boolean conditional
Conditional: Included where the quiz arrives together with its placement — inside `course_program` and inside course `checkpoints`. Absent where the quiz is loaded on its own.
Чи враховується це розміщення квіза в прогресі. Властивість розміщення, а не самого квіза — один квіз може рахуватися в одному уроці й не рахуватися в іншому. Приходить там, де квіз віддається разом зі своїм розміщенням (усередині course_program і в чекпоінтах курсу); відсутнє там, де квіз завантажений сам по собі.

QuizAttemptIdentifier

id integer
Ідентифікатор спроби.
course_id integer nullable
Ідентифікатор курсу, якому належить тест. Пусто, якщо тест не привʼязаний до курсу.
quiz_id integer
Ідентифікатор тесту.
user_id integer
Ідентифікатор учня, якому належить спроба.
status_id integer
Ідентифікатор статусу спроби (passed / failed / in-progress)
comment string nullable
Комментар до спроби, якщо він є.
quiz object conditional
Conditional: Included when the quiz relation is loaded.
Метадані квіза
course Course conditional
Conditional: Included when the parent course is loaded.
Курс, якому належить тест.
lesson object conditional
Conditional: Included when the parent lesson is loaded.
Метадані уроку

Купони

ExpertCoupon

id integer
Ідентифікатор купона.
code string
Код купона, який покупці вводять при оформленні
reward number nullable
Значення винагороди (сума знижки або відсоток залежно від is_fixed)
Тип знижки (enum-payload)
Тип правила закінчення (enum-payload)
Як обмежена загальна кількість застосувань купона.
Як купон обмежений за списком контактів — кому ним дозволено користуватися.
is_fixed boolean
True для знижки у валюті, false для відсоткової
is_active boolean
Купон увімкнений. Це налаштування, а не готовність до застосування: чи спрацює він, залежить ще від терміну і залишку застосувань.
used_amount integer
Скільки разів купон було використано
Кількість застосувань купона не обмежена.
Чи обмежено використання купона певними списками контактів
total_uses integer nullable
Максимальна загальна кількість використань купона
total_uses_per_user integer nullable
Максимум використань на один контакт
Conditional: Included when the coupon's contact-list restrictions are part of the response.
Списки контактів, яким купон доступний.
type CouponType conditional
Conditional: Included when the coupon-type metadata is part of the response.
Метадані типу купона
expert UserSimple conditional
Conditional: Included when the expert who owns the coupon is part of the response.
Експерт, якому належить купон.
user UserSimple conditional
Conditional: Included when the issuing user is part of the response.
Користувач, що створив купон.
contact_id integer nullable
Контакт, для якого купон випущено персонально. Пусто в купонів загального доступу.
Conditional: Included when the linked contact is part of the response.
Короткі ідентифікаційні дані пов'язаного контакту
Conditional: Included when the offers limited by this coupon are part of the response.
Ідентифікаційні дані пропозицій, на які діє купон
expires_at datetime nullable
Коли купон перестає діяти, у часовому поясі з timezone. Пусто, якщо термін не задано і купон діє без обмеження в часі.
expires_at_utc datetime nullable
Той самий термін дії, приведений до UTC. Пусто, якщо термін не задано.
timezone_id integer nullable
Ідентифікатор часового поясу, в якому задано термін. Пусто, якщо термін не задано.
timezone Timezone conditional
Conditional: Included when the coupon has timezone settings configured.
Часовий пояс, у якому задано термін дії.
Conditional: Included when the latest internal comment is part of the response.
Останній внутрішній коментар (для адміністраторів)
currency_id integer nullable
Ідентифікатор валюти знижки. Пусто в купонів, які дають знижку у відсотках.
currency Currency nullable
Валюта знижки. Пусто в купонів, які дають знижку у відсотках.
created_at datetime
Коли купон було створено.
updated_at datetime
Коли купон змінювали останній раз.
deleted_at datetime nullable
Коли купон видалили. Пусто в діючих купонів.

CouponType

id integer
Ідентифікатор типу купона.
title string
Локалізована назва типу купона

CouponCheck

can_use boolean
Чи можна застосувати купон до замовлення
discount number nullable
Обчислена сума знижки, якщо у запиті передавався price; інакше null

Сертифікати

Certificate

id integer
Ідентифікатор сертифіката.
uuid string
Універсальний унікальний ідентифікатор сертифіката
cabinet_id integer
Ідентифікатор кабінету, що видав сертифікат.
user_id integer nullable
Ідентифікатор учня, якому видано сертифікат. Пусто, якщо сертифікат ще ні за кем не закріплений.
number string
Публічний номер сертифіката для пошуку
url string nullable
Публічне посилання для перевірки; null поки сертифікат не випущено
issued_at datetime nullable
Коли сертифікат було видано. Пусто, поки видача не відбулася.
finished_at datetime conditional
Conditional: Included when both the user and the certificate target are available in the response.
Коли користувач завершив certificateble-сутність (курс тощо)
points number conditional
Conditional: Included when both the user and the certificate target are available in the response.
Бали, набрані користувачем за курсом, на який випущено сертифікат. Значення буває дробовим (наприклад 10.5) — раніше надходило лише цілим, тому якщо ви розбираєте його як ціле число, перейдіть на дробове. null — сертифікат випущено не на курс: за іншими сутностями бали не рахуються.
user UserSimple conditional
Conditional: Included when the related user is part of the response.
Учень, якому видано сертифікат.
creator UserSimple conditional
Conditional: Included when creator information is part of the response.
Хто видав сертифікат.
Conditional: Included when the certificate template is part of the response.
Шаблон, за яким сертифікат оформлено.
Назва сутності, на яку випущено сертифікат (курс, продукт тощо)

CertificateTemplate

id integer
Ідентифікатор шаблону сертифіката.
title string
Внутрішня назва шаблону
public_title string nullable
Публічна назва шаблону, що відображається на випущеному сертифікаті

StudentProductCertificateInfo

Тип сертифіката (enum-payload з id/title)
Conditional: Included when the certificate template is part of the response.
Шаблон, за яким буде оформлено сертифікат.
Поліморфний тип certificateble-сутності (курс, продукт тощо)
Ідентифікатор сутності, за яку видається сертификат — курсу або іншого продукту, залежно від того, що стоїть у certificateble_type.
certificateble_title string conditional
Conditional: Included when the certificate target is part of the response.
Назва certificateble-сутності
message string nullable
Готова фраза про статус видачі з датою — її можна показати учню як є. Дата приводиться до часового поясу того, хто запитує.
system_comment string nullable
Службове пояснення, чому сертифікат не видано. Пусто, якщо пояснення немає.
certificate Certificate conditional
Conditional: Returned only after the certificate has been issued.
Сам сертифікат, якщо він уже видано.

Закриті групи

ClosedGroup

id integer
Унікальний ідентифікатор закритої групи
product_id integer
Ідентифікатор продукту, до якого прив'язана закрита група
title string
Назва закритої групи
slug string
URL-сумісний слаг закритої групи
Тип закритої групи (enum). Можливі варіанти (id / slug): 1 / Telegram, 2 / Kwiga.
Статус закритої групи (enum). Можливі варіанти (id / slug): 1 / Draft, 2 / Active, 3 / NotActive, 4 / NotSpecified, 5 / NotConnected.
Telegram-бот, прив'язаний до групи. Присутній лише для групи типу Telegram із підключеним ботом; інакше — null.
Telegram-канал/чат, на якому побудована група. Присутній, лише коли групу підключено до Telegram-каналу; інакше — null.
created_at datetime
Момент створення закритої групи
updated_at datetime
Момент останнього оновлення закритої групи

ClosedGroupChatbot

name string
Ім'я користувача/відображуване ім'я Telegram-бота

ClosedGroupChannel

Ідентифікатор Telegram-каналу
linked_chat_id string nullable
Ідентифікатор пов'язаного Telegram-чату обговорень, якщо він є в каналу; інакше — null
url string nullable
Публічне запрошувальне посилання на Telegram-канал, якщо доступне; інакше — null

ClosedGroupParticipant

user_id integer
Ідентифікатор акаунту користувача-учасника
Запис контакту учасника; null, якщо в учасника немає контакту в цьому кабінеті
status Enum nullable
Статус участі в групі (enum). Можливі варіанти (id / slug): 1 / ActiveMember, 2 / Banned, 3 / NotJoined, 4 / LeftGroup, 5 / AccessExpired.
is_joined boolean
Чи приєднався учасник до Telegram-каналу/чату групи
is_banned boolean
Чи забанений учасник у групі
Telegram-акаунти, прив'язані до користувача учасника; порожній масив, якщо жодного не прив'язано
Агреговане вікно підписки/доступу для продукту, до якого прив'язана закрита група

Пропозиції

Offer

id integer
Ідентифікатор тарифу.
unique_offer_code string nullable
Стабільний код, що використовується в URL оплати
url string
Публічний URL для купівлі пропозиції
crm_url string
Глибоке посилання на редагування оферу в кабінеті експерта (CRM).
title string
Назва тарифу — його бачить покупець.
description string nullable
Повний опис тарифу. Пусто, якщо не заповнено.
short_description string nullable
Короткий опис для картки тарифу. Пусто, якщо не заповнено.
price_type string
Ідентифікатор моделі ціни пропозиції (free, fixed, recurring)
Звичайна ціна тарифу. Якщо діє знижка, ціну зі знижкою дивіться в price_discounted.
price_discounted Price conditional
Conditional: Included when the offer has an active discount.
Ціна із застосованою активною знижкою
discount OfferDiscount conditional
Conditional: Included when the offer has an active, currently valid discount.
Дійсна знижка. Приходить лише коли знижка є і вона зараз діє — за датами і за залишком продажів.
Тариф продається як підписка з регулярними платежами.
limit_type string nullable
Ідентифікатор типу обмеження продажів (unlimited, limited)
limit_of_sales integer nullable
Максимум дозволених покупок (null якщо без ліміту)
purchases_count integer conditional
Conditional: Included when purchase counts are available for the offer.
Скільки разів тариф купили.
sales_left integer conditional
Conditional: Included when purchase counts are available. Null when the offer has no sales limit.
Скільки покупок залишилося до ліміту. Пусто, якщо ліміт не задано і продажі не обмежені.
is_draft boolean
Тариф ще чернетка і покупцям не показується.
is_active boolean conditional
Conditional: Included when the offer has date settings configured.
Тариф зараз продається за термінами продажу — тобто період продажу почався і не закінчився. Ліміт продажів ця ознака НЕ враховує: залишок дивіться в sales_left.
Conditional: Returned together with `is_active` when the offer has date settings configured.
Коли починається продаж тарифу.
Conditional: Returned together with `is_active` when the offer has date settings configured.
Коли продаж тарифу закінчується.
products Product[] conditional
Conditional: Included when the offer's products are part of the response.
Продукти, які покупець отримує за цим тарифом.
is_paid boolean conditional
Conditional: Included when payment status is determined for the current viewer.
Тариф платний. Приходить лише там, де це вдалося визначити.

OfferIdentifier

id integer
Ідентифікатор тарифу.
title string
Назва тарифу.

OfferSimple

id integer
Ідентифікатор тарифу.
title string
Назва тарифу.
description string nullable
Опис тарифу. Пусто, якщо не заповнено.
url string
Публічний URL для купівлі пропозиції
type_id integer
Ідентифікатор типу пропозиції
status string nullable
Ідентифікатор статусу пропозиції
Чи тарифікує підписку мерчант
Conditional: Included when product identity entries are loaded for the offer.
Продукти, які входять у тариф, у скороченому вигляді.
curators object[] conditional
Conditional: Included when curator entries are loaded for the offer.
Куратори, закріплені за тарифом.
is_paid boolean conditional
Conditional: Included when payment status is determined for the current viewer.
Тариф платний. Приходить лише там, де це вдалося визначити.

OfferDiscount

id integer
Ідентифікатор знижки.
Фінальна ціна із застосованою знижкою
Тип обмеження знижки (enum-payload)
За цією знижкою дозволена оплата частинами.
start_at datetime nullable
Коли знижка починає діяти, у часовому поясі з start_timezone. Пусто, якщо початок не задано і знижка діє відразу.
start_at_utc datetime nullable
Той самий початок дії, приведений до UTC. Пусто, якщо початок не задано.
start_timezone_id integer nullable
Ідентифікатор часового поясу, в якому задано початок. Пусто, якщо початок не задано.
end_at datetime nullable
Коли знижка перестає діяти, у часовому поясі з end_timezone. Пусто, якщо закінчення не задано і знижка діє без терміну.
end_type string nullable
Як закінчується знижка (фіксована дата, після числа продажів тощо)
end_at_utc datetime nullable
Те саме закінчення дії, приведене до UTC. Пусто, якщо закінчення не задано.
end_timezone_id integer nullable
Ідентифікатор часового поясу, в якому задано закінчення. Пусто, якщо закінчення не задано.
sales_limit integer nullable
Максимальна кількість продажів зі знижкою
sales integer
Скільки продажів зі знижкою вже відбулося
is_active boolean
Знижка увімкнена. Це налаштування, а не факт дії зараз — чи діє вона в даний момент, визначають ще терміни й залишок продажів.
is_available boolean
Чи діє знижка зараз (не закінчилась, не вичерпана)
created_at datetime
Коли знижку було створено.
updated_at datetime
Коли знижку змінювали останній раз.
offer OfferIdentifier conditional
Conditional: Included when the parent offer is part of the response.
Короткі ідентифікаційні дані батьківської пропозиції
Часовий пояс, у якому задано початок дії. Пусто, якщо початок не задано.
Часовий пояс, у якому задано закінчення дії. Пусто, якщо закінчення не задано.

Замовлення

Order

id integer
Ідентифікатор замовлення.
type_id integer
Ідентифікатор типу замовлення
crm_url string
Глибоке посилання на замовлення в кабінеті експерта (CRM).
first_paid_at datetime nullable
Найраніша оплата за замовленням. Заповнено, як тільько пройшов перший платіж, навіть якщо замовлення оплачено не повністю.
paid_at datetime nullable
Коли замовлення стало повністю оплаченим. Пусто, поки залишаються неоплачені платежі — для часткової оплати дивіться first_paid_at.
created_at datetime
Коли замовлення було створено.
updated_at datetime
Коли замовлення змінювали останній раз.
products Product[] conditional
Conditional: Included when the order contains product information.
Продукти, що входять до замовлення.
payments Payment[] conditional
Conditional: Included when the order has payment records.
Платежі за замовленням. Їх може бути кілька — при оплаті частинами або за підпискою.
paid_status integer nullable
Ідентифікатор статусу оплати
paid_status_title string nullable
Локалізована назва статусу оплати
order_stage OrderStage conditional
Conditional: Included when the order has a stage assigned.
Етап воронки, на якому зараз замовлення.
Повна вартість замовлення з валютою
managers UserSimple[] conditional
Conditional: Included when the order has assigned managers.
Менеджери, закріплені за замовленням.
offers Offer[] conditional
Conditional: Included when the order contains offer information.
Тарифи, за якими зроблено замовлення.
utm UtmList conditional
Conditional: Included when UTM data is available for the order.
UTM-мітки, зібрані за візитами, що призвели до замовлення.

OrderFunnel

id integer
Ідентифікатор воронки.
title string
Локалізована назва воронки
created_at datetime
Коли воронку було створено.

OrderGroup

id integer
Ідентифікатор групи замовлень.
slug string
Символьний код групи — придатний для співставлення на вашому боці.
title string
Локалізована назва групи
order integer
Позиція групи всередині її воронки
created_at datetime
Коли групу було створено.

OrderStage

id integer
Ідентифікатор етапу.
title string
Локалізована назва етапу
order_group OrderGroup conditional
Conditional: Included when the parent group is part of the response.
Група, до якої належить етап.
Conditional: Included when the parent funnel is part of the response.
Воронка, до якої належить етап.
created_at datetime
Коли етап було створено.

Платежі

Payment

id integer
Ідентифікатор платежу.
status string
Ідентифікатор статусу оплати
status_title string
Локалізована назва статусу оплати
payment_type string nullable
Ідентифікатор типу оплати (разова, рекурентна тощо)
payment_type_title string nullable
Локалізована назва типу оплати
payment_form string nullable
Ідентифікатор форми оплати (онлайн, вручну тощо)
payment_form_title string nullable
Локалізована назва форми оплати
purchase_type string nullable
Ідентифікатор типу покупки
purchase_type_title string nullable
Локалізована назва типу покупки
Ціна з інформацією про валюту
paid_at datetime nullable
Коли платіж пройшов. Пусто, поки він не оплачений.
created_at datetime
Коли платіж було створено.
updated_at datetime
Коли платіж змінювали останній раз.
Conditional: Included when payment transactions are part of the response.
Транзакції за цим платежем — спроби списання в платіжній системі. Одному платежу може відповідати кілька спроб.

Transaction

id integer
Ідентифікатор транзакції.
merchant_id integer nullable
Ідентифікатор підключеної платіжної системи, через яку пройшла транзакція. Пусто, якщо систему не визначено.
payment_id integer
Платіж, до якого належить транзакція.
order_id integer nullable
Замовлення, до якого належить транзакція. Пусто, якщо транзакція не пов’язана із замовленням.
payment_system_status string nullable
Сирий рядок статусу від платіжної системи
failure_reason string nullable
Причина невдачі транзакції, якщо застосовно
price number
Сума транзакції у валюті з currency_code.
currency_code string nullable
Літерний код валюти транзакції. Пусто, якщо платіжна система його не передала.
payer_account string nullable
Маскований ідентифікатор платника (останні цифри картки / handle гаманця)
card_mask string nullable
Замаскований номер картки в тому вигляді, в якому його повернула платіжна система. Пусто для способів оплати без картки.
rrn string nullable
Retrieval Reference Number від банку
fee number nullable
Комісія платіжної системи
created_at datetime
Коли транзакцію було створено.

Price

amount number
Сире значення суми
Сума, округлена за налаштуваннями округлення кабінету
Сума з підставленим символом / кодом валюти
Сума з підставленим ISO-кодом валюти
Валюта, у якій вказано суму.

Currency

id integer
Ідентифікатор валюти.
code string
ISO-код валюти
html_code string
Локалізоване HTML-представлення символу валюти
Локалізоване HTML-представлення буквеного коду валюти

Продукти

Product

id integer
Ідентифікатор продукту.
productable_id integer
Ідентифікатор нижчележачої сутності (курс, info-unit тощо)
Поліморфний тип нижчележачої сутності (course, info_unit тощо)
title string
Локалізована назва productable-сутності
url string conditional
Conditional: Included when the underlying entity is part of the response.
Публічне посилання на productable-сутність

ProductIdentifier

id integer
Ідентифікатор продукту.
Поліморфний тип нижчележачої сутності (course, info_unit тощо)
productable_id integer
Ідентифікатор сутності, якою продукт є — курсу, закритої групи тощо, залежно від того, що стоїть у productable_type.
name string
Локалізована назва нижчележачої сутності
url string
Публічне посилання на нижчележачу сутність
course_type_id Enum conditional
Conditional: Included when the underlying entity is a course; carries the course type enum.
Тип курсу. Приходить лише у продуктів-курсів.
Conditional: Included when course lesson identity payloads are loaded for the product.
Уроки курсу в скороченому вигляді — ідентифікатор, назва і батьки.

ProductAggregatedSubscription

is_active boolean
Чи активний зараз доступ до продукту
is_paid boolean
Чи оплачена підписка (не пробна)
start_at datetime nullable
Початок доступу. Пусто, якщо доступ ще не почався.
end_at datetime nullable
Ефективна дата закінчення доступу (обчислюється за всіма активними підписками)
offer_end_at datetime nullable
Максимальний термін, до якого доступ дозволений тарифами учня. Кінець доступу — це end_at, він дорівнює найранішому з offer_end_at і order_end_at.
order_end_at datetime nullable
Дата найближчого платежу за підпискою або оплатою частинами — це НЕ кінець доступу. Кінець доступу беріть з end_at.
frozen_at datetime nullable
Коли підписку було заморожено; null, якщо не заморожена.
extended_at datetime nullable
Коли доступ продовжували востаннє; null, якщо не продовжували.
count_available_days integer nullable
Загальна кількість днів доступу контакту до продукту
count_left_days integer nullable
Залишилось днів доступу
Поточний стан (enum-payload)

ProductSubscription

id integer
Ідентифікатор запису про доступ.
creator_id integer nullable
Хто видав доступ — наприклад, експерт, що відкрив його вручну. Пусто, якщо доступ з’явився в результаті звичайної покупки.
user_id integer
Ідентифікатор учня, якому належить доступ.
product_id integer
Ідентифікатор продукту, до якого відкрито доступ.
order_id integer nullable
Замовлення, за яким доступ відкрився. Пусто, якщо доступ видано вручну, без замовлення.
offer_id integer nullable
Тариф, за яким відкрито доступ. Пусто, якщо доступ видано вручну, без тарифу.
is_active boolean
Доступ увімкнено. Це флаг самого запису — чи відкритий контент прямо зараз, залежить ще від start_at і end_at.
start_at datetime nullable
Початок доступу. Пусто, якщо доступ ще не почався.
order_end_at datetime nullable
Дата найближчого платежу за підпискою або оплатою частинами — це НЕ кінець доступу. Кінець доступу беріть з end_at.
end_at datetime nullable
Ефективна дата закінчення доступу для цієї підписки
frozen_at datetime nullable
Коли цю підписку було заморожено; null, якщо не заморожена.
extended_at datetime nullable
Коли цю підписку продовжували востаннє; null, якщо не продовжували.
paid_at datetime nullable
Коли доступ було оплачено. Пусто у неоплачених і у виданих вручну.
created_at datetime
Коли запис про доступ було створено.
updated_at datetime
Коли запис про доступ змінювали останній раз.

UserProduct

id integer
Ідентифікатор продукту.
productable_id integer
Ідентифікатор сутності, якою продукт є — курсу, закритої групи тощо.
Поліморфний тип нижчележачої сутності
title string
Назва продукту.
image_url string nullable
URL превʼю-картинки productable-сутності
url string
Публічне посилання на сторінку продукту.
is_published boolean
Продукт опублікований і доступний учням.
Conditional: Included when subscription summary is part of the response.
Агрегована інформація про доступ (загальний is_active/start/end за всіма підписками)
Conditional: Included when individual subscription records are part of the response.
Окремі записи підписок (по одній на замовлення/пропозицію)

Email

EmailCampaign

id integer
Ідентифікатор розсилки.
title string
Назва розсилки; для системних розсилок фолбек на локалізовану підпис «Транзакційна»
state_id integer
Ідентифікатор стану розсилки
is_restricted boolean
True для вбудованих системних розсилок, які не можна редагувати
not_verified boolean
True, якщо у власника акаунту немає підтвердженого способу оплати / поповнення
Службовий лист, який відправляє сама платформа за подією, а не розсилка, складена експертом.
is_system boolean
Розсилка створена платформою, а не експертом. У таких розсилок частина налаштувань недоступна для правки.
is_segment boolean
True, якщо розсилка націлена на динамічний сегмент
cabinet_id integer
Ідентифікатор кабінету, якому належить розсилка.
user_id integer
Ідентифікатор користувача, що створив розсилку.
subject string nullable
Тема листа. Пусто, якщо не заповнена.
subsubject string nullable
Прехедер / прев'ю-текст, що відображається під темою листа
start_type string nullable
Ідентифікатор типу розкладу (immediate, scheduled, recurring тощо)
start_at_utc datetime nullable
Момент запуску, приведений до UTC. Пусто, якщо запуск не запланований.
start_at datetime nullable
Момент запуску в часовому поясі з timezone. Пусто, якщо запуск не запланований.
timezone_id integer nullable
Ідентифікатор часового поясу, в якому задано запуск. Пусто, якщо пояс не задано.
paused_at datetime nullable
Коли розсилку поставили на паузу. Пусто, якщо вона не на паузі.
rejected_at datetime nullable
Коли розсилку відхилила модерація. Пусто, якщо її не відхиляли.
finished_at datetime nullable
Коли розсилка завершилася. Пусто, поки вона не відправлена до кінця.
timezone Timezone nullable
Часовий пояс, у якому задано запуск. Пусто, якщо пояс не задано.
Conditional: Included when the campaign's contact lists are loaded.
Списки контактів, за якими йде розсилка.
bounced_limit number
Поріг, вище якого частка bounce вважається проблемною
Поріг, вище якого частка скарг вважається проблемною
count_recipients integer conditional
Conditional: Included when the campaign statistic is loaded.
Скільки отримувачів у розсилки. У звичайних розсилок це кількість придатних для відправки контактів, у системних — кількість фактично відправлених листів.
complaint_rate number conditional
Conditional: Included when the campaign statistic is loaded.
Частка отримувачів, що скаржилися на лист як на спам. Допустимий порог приходить поруч у complaint_limit.
bounced_rate number conditional
Conditional: Included when the campaign statistic is loaded.
Частка листів, які не вдалося доставити. Допустимий порог приходить поруч у bounced_limit.
count_opened integer conditional
Conditional: Included when the campaign statistic is loaded.
Скільки отримувачів відкрили лист.
count_clicks integer conditional
Conditional: Included when the campaign statistic is loaded.
Скільки отримувачів перейшли за посиланням з листа.
percent_opened number conditional
Conditional: Included when the campaign statistic is loaded.
Частка тих, хто відкрив, рахується від загальної кількості контактів розсилки — не від кількості доставлених листів.
percent_clicks number conditional
Conditional: Included when the campaign statistic is loaded.
Частка тих, хто перейшов за посиланням, рахується від загальної кількості контактів розсилки — не від кількості тих, хто відкрив.
percent_completed number conditional
Conditional: Included when the campaign statistic is loaded.
Наскільки розсилка відправлена: частка відправлених листів від загальної кількості її контактів.
distributor_id integer conditional
Conditional: Returned only for viewers with extended mailing access.
Ідентифікатор відправника, від імені якого йде лист. Пусто, якщо відправника не задано.
templatable_type string conditional
Conditional: Returned only for viewers with extended mailing access.
Тип сутності, до якої привʼязаний шаблон листа. Пусто, якщо шаблон не привʼязаний.
templatable_id integer conditional
Conditional: Returned only for viewers with extended mailing access.
Ідентифікатор сутності, до якої привʼязаний шаблон листа. Пусто, якщо шаблон не привʼязаний.
template object conditional
Conditional: Returned only for viewers with extended mailing access when the templatable relation is loaded.
Дані email-шаблону (системний або кастомний)
distributor object conditional
Conditional: Returned only for viewers with extended mailing access when the distributor relation is loaded.
Відправник, від імені якого йде лист.
statistic object conditional
Conditional: Returned only for viewers with extended mailing access when the statistic relation is loaded.
Повний знімок статистики розсилки

EmailCampaignIdentifier

id integer
Ідентифікатор розсилки.
title string
Назва розсилки; для системних розсилок фолбек на локалізовану підпис

Візити

Visit

id integer
Ідентифікатор візиту.
ip string nullable
IP-адреса відвідувача. Пусто, якщо адресу не збережено.
landing_url string nullable
Повний URL приземлення (auth-хеш видалено)
landing_domain string nullable
Домен сторінки, на яку прийшов відвідувач. Пусто, якщо визначити не вдалося.
landing_path string nullable
Шлях сторінки, на яку прийшов відвідувач, без домену. Пусто, якщо визначити не вдалося.
landing_params string nullable
Рядок query landing-URL без auth-хеша
referrer_url string nullable
Повна адреса сторінки, з якої відвідувач перейшов. Пусто при прямому заході.
referrer_domain string nullable
Домен сторінки, з якої відвідувач перейшов. Пусто при прямому заході.
utm_source string nullable
Мітка utm_source з адреси. Пусто, якщо її не було.
utm_campaign string nullable
Мітка utm_campaign з адреси. Пусто, якщо її не було.
utm_medium string nullable
Мітка utm_medium з адреси. Пусто, якщо її не було.
utm_term string nullable
Мітка utm_term з адреси. Пусто, якщо її не було.
utm_content string nullable
Мітка utm_content з адреси. Пусто, якщо її не було.
location string nullable
Розпізнана мітка локації (місто, країна)
Детальна розбивка за геолокацією
device VisitUserAgent conditional
Conditional: Included when user agent / device data is available for the visit.
Інформація про user agent / пристрій
created_at datetime
Коли візит було зафіксовано.

VisitLocation

Локалізований payload країни
state_name string nullable
Назва штату / регіону
city string nullable
Місто, визначене за IP-адресою. Пусто, якщо визначити не вдалося.
full_location string
Шлях city, state, country через кому, порожні частини пропускаються

VisitUserAgent

user_agent string nullable
Сирий заголовок User-Agent, зафіксований на візиті
browser string nullable
Браузер відвідувача. Пусто, якщо визначити не вдалося.
browser_version string nullable
Версія браузера. Пусто, якщо визначити не вдалося.
platform string nullable
Назва операційної системи
platform_version string nullable
Версія операційної системи. Пусто, якщо визначити не вдалося.
Тип пристрою (enum-payload: desktop, mobile, tablet)
title string
Читаюче резюме user agent

UtmList

utm_source string[]
Унікальні значення utm_source за всіма візитами контакту
utm_campaign string[]
Усі значення utm_campaign, зустрінуті у візитах цієї сутності.
utm_medium string[]
Усі значення utm_medium, зустрінуті у візитах цієї сутності.
utm_term string[]
Усі значення utm_term, зустрінуті у візитах цієї сутності.
utm_content string[]
Усі значення utm_content, зустрінуті у візитах цієї сутності.

Коментарі

CommentIdentifier

id integer
Ідентифікатор комментаря.
text string
Сирий текст коментаря
commentable_id integer
Ідентифікатор сутності, до якої прикріплено коментар
Поліморфний тип цільової сутності
commentable object conditional
Conditional: Included when the target entity is part of the response.
Короткий payload цільової сутності

Спроби квізів

QuizAttempt

id integer
Ідентифікатор спроби.
root_id integer nullable
ID найпершої спроби у ланцюжку перездач. Для кореневої спроби — NULL.
previous_id integer nullable
ID попередньої спроби у ланцюжку перездач. Для першої спроби — NULL.
number_version integer
Порядковий номер спроби всередині ланцюжка перездач, починаючи з 1.
user_id integer
Ідентифікатор учня, який проходив тест.
product_id integer
Ідентифікатор продукту, у межах якого учень проходив тест.
quiz_id integer
Ідентифікатор тесту.
course_id integer nullable
Ідентифікатор курсу, якому належить тест.
course_lesson_id integer nullable
Урок, у якому учень відкрив тест. Один тест може стояти в кількох уроках, і спроби в них враховуються окремо.
Стисла інфа про секцію інфоблоку, до якої прив'язаний квіз. З'являється, тільки коли квіз знаходиться всередині секції уроку (а не на самому уроці). Інакше NULL.
status Enum nullable

Статус спроби у вигляді enum-об'єкта — id (числове значення), slug (стабільне ім'я кейса) та title (підпис, уже перекладений під локаль кабінету).

  • 1 — Пройдено
  • 2 — Не пройдено
  • 3 — Потрібне доопрацювання
  • 4 — Остання версія змінила статус
  • 5 — У процесі
  • 6 — Очікує перевірки
  • 7 — Не розпочав
scores number nullable
Сума балів, набрана учнем у цій спробі.
scores_max number nullable
Максимально можлива кількість балів для спроби. Якщо у самій спробі значення не збережене (наприклад, для virtual-рядків «не розпочав») — береться з квіза. Для квізів-рандомайзерів збережене у спробі значення — це максимум, який учень міг набрати саме в цій спробі, виходячи з питань, що йому випали. NULL, якщо у квіза немає бальної системи (наприклад, завдання).
count_questions integer
Скільки питань було в цій спробі. Може відрізнятися від загальної кількості питань тесту, якщо тест видає випадкову вибірку.
count_questions_correct integer nullable
Скільки питань учень пройшов правильно.
count_questions_incorrect integer nullable
Скільки питань учень пройшов неправильно. Питання, залишені без відповіді, сюди не потрапляють.
Ознака того, що куратор уручну перебив автоматичну оцінку спроби.
is_read boolean
Ознака, що куратор прочитав спробу. Актуально лише для квізів із відкритими питаннями — інші типи позначаються прочитаними автоматично.
started_at datetime nullable
Коли учень почав спробу.
last_activity_at datetime nullable
Коли учень останній раз щось робив у цій спробі.
finished_at datetime nullable
Коли учень завершив спробу. Пусто, якщо спроба ще триває або була покинута.
deadline_at datetime nullable
Крайній термін завершення спроби, якщо у тесті задано обмеження за часом. Пусто, якщо обмеження немає.
status_updated_at datetime nullable
Час останньої зміни статусу спроби.
checked_at datetime nullable
Коли куратор перевірив спробу. Пусто у тестів з автоматичною перевіркою і поки спробу не перевірено.
commented_at datetime nullable
Коли до спроби останній раз залишили комментар. Пусто, якщо комментарів немає.
canceled_at datetime nullable
Коли спробу було скасовано — тобто замінено новішою. Пусто у чинної спроби.
Тривалість спроби в секундах (finished_at мінус started_at; для незавершених — now() мінус started_at). Мінімум 1.
crm_url string
Глибоке посилання на CRM-картку спроби в кабінеті експерта (на піддомені кабінету). Дає команді одразу відкрити спробу в дашборді.
user UserSimple conditional
Conditional: Included when the user relation is loaded.
Учень, якому належить спроба.
quiz QuizIdentifier conditional
Conditional: Included when the quiz relation is loaded.
Тест, до якого належить спроба.
course Course conditional
Conditional: Included when the parent course is loaded.
Курс, якому належить тест.
Conditional: Included when the parent lesson is loaded.
Урок, у якому стоїть тест.
Conditional: Included when the product relation is loaded.
Продукт, у межах якого учень проходив тест.

QuizScore

Мінімально можлива сума балів за квіз (з урахуванням обов'язкових питань).
Максимально можлива сума балів за квіз. Для квізів-рандомайзерів значення вже перераховане під кількість питань, що випадають у спробі.
quiz_scores number
Бонусні бали за проходження самого квіза (входять до common_scores_max).
Сума максимальних балів на рівні питань.
Сума максимальних балів тверджень (для типів питань із твердженнями).
Мінімально можлива сума балів за відповідями.
Максимально можлива сума балів за відповідями.

InfoSectionReport

id integer
Ідентифікатор розділу уроку.
name string
Відображувана назва секції (якщо у секції немає власного заголовку — генерується автоматично).
order integer
Позиція секції всередині батьківського уроку, нумерація з 1.
url string
Публічне посилання на секцію — як її бачить учасник.
crm_url string
Посилання на конструктор секції в кабінеті експерта.

QuizDetailed

id integer
Ідентифікатор тесту. Той самий тест може стояти в курсі в кількох місцях, тому в списку розміщень це значення повторюється.
name string
Назва квіза
type Enum nullable
Тип квіза (enum).
questions_type Enum nullable
Тип питань квіза (enum) — один варіант, декілька варіантів і так далі.
questions_count integer conditional
Conditional: Returned where the endpoint loads the counter — currently in `/quiz-placements`.
Кількість питань у квізі.
Бальна конфігурація квіза — актуальні значення з поточних налаштувань, а не історичний знімок спроби. Повертається лише там, де ендпоінт явно віддає бали (зараз — квізи всередині course_program). NULL, якщо у квіза немає запису балів. Усі значення нульові, якщо у квіза вимкнена бальна система.
is_estimate_on boolean
Чи увімкнена бальна система. Якщо false, усі значення всередині quiz_score нульові.
is_auto_approve boolean
True — спроби перевіряються автоматично; false — перевіряє куратор вручну.
can_retry boolean
Чи дозволені повторні спроби. Скільки саме — у count_tries.
Перескладання доступне лише після невдалої спроби.
count_tries integer nullable
Скільки спроб дозволено всього. NULL — без обмеження.

QuizPlacement

id integer
Ідентифікатор розміщення (рядка таблиці прив'язок квізів до курсу).
Розміщений квіз — ідентифікатор, назва та бальна конфігурація.
product_id integer
Ідентифікатор продукту, якому належить розміщення.
Продукт, якому належить розміщення.
course_lesson_id integer nullable
Урок, якому належить розміщення.
Урок, якому належить розміщення. Відсутній у практики-інфоблоку, прикріпленого просто до модуля.
Розділ уроку, у якому лежить інфоблок-носій, із посиланнями для учня та на конструктор експерта. NULL, коли розділу немає — інфоблок стоїть просто в модулі, або розміщення історичне, привʼязане до самого уроку.
quizable_type string
Носій розміщення — сам урок або практика-блок усередині нього.
quizable_id integer
Ідентифікатор носія з поля quizable_type: блоку практики або самого уроку.
order integer nullable
Порядок тесту всередині свого носія — у якому місці він показується учню.
Чи враховується це розміщення в прогресі. Прапорець задається на розміщенні, тому один і той самий квіз може рахуватися в одному уроці й не рахуватися в іншому.
Чи показується розміщення в програмі курсу для учнів.
is_checkpoint boolean
Чекпоінт: поки квіз не пройдено, подальші уроки учню не відкриваються. Резолвиться за парою (інфоблок, квіз), тому визначається правильно навіть для тих рядків налаштувань, у яких посилання на прикріплення не заповнене.
Налаштування відкриття та закриття цього розміщення або NULL, якщо налаштувань немає і квіз доступний разом з уроком. Це налаштування, а не дати доступу конкретного учня.
module_ids array conditional
Conditional: Returned only when `include` contains `module_ids`.
Ланцюжок модулів-предків уроку, найближчий модуль першим. Порожній масив — урок лежить просто в корені курсу.

QuizPlacementDripping

Як відкривається квіз — одразу разом з уроком, через N днів або в конкретну дату. Решта полів несуть параметри вибраного типу; незастосовні залишаються NULL.
accessible_after_days integer nullable
Через скільки днів після відкриття доступу до курсу відкриється тест. Заповнено, якщо вибрано тип відкриття «через N днів».
accessible_after_hours integer nullable
Години на додаток до днів для того ж типу відкриття.
accessible_after_minutes integer nullable
Хвилини на додаток до днів і годин.
accessible_start_at datetime nullable
Конкретна дата відкриття. Заповнено, якщо вибрано тип відкриття «у вказану дату».
Як закривається квіз — безстроково відкритий, через N днів або в конкретну дату.
closed_after_days integer nullable
Через скільки днів тест закриється. Заповнено, якщо вибрано тип закриття «через N днів».
closed_after_hours integer nullable
Години на додаток до днів для того ж типу закриття.
closed_after_minutes integer nullable
Хвилини на додаток до днів і годин.
closed_start_at datetime nullable
Конкретна дата закриття. Заповнено, якщо вибрано тип закриття «у вказану дату».

Нагороди (гейміфікація)

FlowRewardLog

id integer
Ідентифікатор запису в журналі балів.
points number
Скільки балів зсунув цей запис журналу. Додатнє — нарахування, від'ємне — списання.
Напрямок операції, обчислюється за знаком pointsaccrued або deducted. Збігається зі значеннями фільтра accrual_type у списку.

Чому з''явився цей запис. Можливі id:

  • 1 — Практика пройдена
  • 2 — Скасування результатів практики
  • 3 — Скидання балів за спробу
  • 4 — Нові бали за спробу
  • 5 — Оплата балами
  • 6 — Вручну
  • 7 — Автоматизація
  • 8 — Змінено бали за спробу
  • 9 — Оплата балами за подарунок
event string nullable
Стабільний рядковий ключ вихідного flow-події (тільки для автоматичних/event-driven записів). Для ручних — NULL.
event_name string nullable
Людиночитана назва вихідної flow-події (те ж джерело, що і event). Для ручних — NULL.
eventable_type string nullable
Поліморфний тип вихідної сутності (наприклад, quiz_attempt, order, gift). Для ручних — NULL.
eventable_id integer nullable
ID вихідної сутності всередині eventable_type. Для ручних — NULL.
event_comment string nullable
Вільний коментар, зафіксований у момент події (наприклад, причина куратора при скасуванні квіза). NULL, якщо не задано.
manual_comment string nullable
Коментар куратора до ручного нарахування/списання (reason_type=Manual). До 255 символів. NULL для інших причин.
is_visible_to_student boolean nullable
Чи показується manual_comment учневі в його власному журналі балів. Для не-ручних записів — NULL. Куратори (включно з цим API) бачать коментар завжди, незалежно від прапору.
message string
Готовий текст запису для відображення в UI. Той же шаблон, що і в CRM, тільки без HTML-розмітки. Для ручних записів сюди дописується коментар куратора.
Conditional: Included when the reward is tied to a product.
Продукт, у межах якого нараховані бали.
creator UserSimple conditional
Conditional: Included for rewards added by a curator/expert (manual or automation triggered by a user).
Хто нарахував бали. Пусто, якщо нарахування зробила автоматизація.
created_at datetime
Коли бали були нараховані.

Загальне

DateSetting

type string
Тип налаштування — fixed, relative, recurring тощо
duration_type_id integer nullable
Ідентифікатор одиниці тривалості (дні/тижні/місяці)
date_at datetime nullable
Фіксована дата в таймзоні кабінету
date_at_utc datetime nullable
Та сама дата, переведена в UTC
timezone_id integer nullable
Ідентифікатор часового поясу, в якому рахується час. Пусто, якщо пояс не задано.
timezone Timezone nullable
Часовий пояс, у якому рахується час. Пусто, якщо пояс не задано.
after_months integer nullable
Відносний зсув у місяцях (для relative-типів)
after_days integer nullable
Скільки днів додається до початкової дати — разом з after_months, коли дата задана періодом. Пусто, якщо період не використовується.
specific_time string nullable
Конкретний час дня (HH:MM)
specific_day_of_week integer nullable
1 (понеділок) – 7 (неділя), якщо привʼязано до конкретного дня тижня
is_current_week boolean nullable
Дозволяє потрапити в потрібний день тижня вже на поточному тижні, якщо він ще не минув; інакше дата переноситься на наступний тиждень. Застосовується, коли дата задана днем тижня.
is_current_day boolean nullable
Дозволяє взяти сам день покупки, якщо він збігся з потрібним днем тижня. Працює разом з is_current_week.

Timezone

id integer
Ідентифікатор часового поясу.
name string
Ідентифікатор таймзони IANA
name_full string
Назва таймзони зі зміщенням UTC для відображення
value string
Рядок UTC-зміщення

Enum

id integer
Числове значення enum-кейса, стабільне між релізами (в деяких місцях називається value)
slug string
PHP-ім'я кейса у PascalCase — стабільний машинний ключ для логіки switch/match на клієнті
title string
Локалізований підпис enum — залежить від заголовка X-Language запиту

FileSimple

id integer
Ідентифікатор файлу.
uuid string
Універсальний унікальний ідентифікатор файлу
name string
Внутрішнє збережене ім'я файлу
original_name string
Оригінальне ім'я файлу, завантаженого користувачем
url string nullable
Публічне посилання на файл (null, якщо сховище недоступне або у читача немає доступу)
thumbnails object nullable
Карта розмірів прев'ю на публічні посилання
extension string nullable
Розширення файлу без провідної крапки
type_id integer
Ідентифікатор типу файлу
mime_type string nullable
MIME-тип файлу. Пусто, якщо тип не визначено.

UserSimple

id integer
Ідентифікатор користувача.
name string nullable
Повне імʼя користувача (імʼя + прізвище)
email string nullable
Email користувача (може бути приховано від не-власників)

CountrySimple

id integer
Ідентифікатор країни.
code string
ISO 3166-1 alpha-2 код країни
name string
Локалізована назва країни в поточному X-Language

TelegramAccount

telegram_id integer
Ідентифікатор користувача Telegram. Стабільний при зміні імені — використовуйте його, а не username, як ключ під час зіставлення акаунтів.
username string nullable
Ім'я користувача Telegram, без префіксу @. Не в кожного Telegram-акаунту задано username — у такому разі null. Не покладайтеся на його наявність; вважайте telegram_id стабільним ідентифікатором.

Обгортки та пагінація

Success

success boolean
Завжди true — підтвердження, що операцію виконано. Ендпоінти з такою відповіддю даних не повертають.

AffectedSubscriptions

ID видалених записів підписок
affected_orders integer[]
ID замовлень, у яких було порушено доступ (стали custom)
Розбивка за offer_id; кожне значення містить масиви affected_subscriptions і affected_orders. Порожній обʼєкт, якщо offers не передавалися.
Розбивка за product_id; та сама структура, що в affected_by_offers. Порожній обʼєкт, якщо product_id не передавався.

first string nullable
URL першої сторінки
last string nullable
URL останньої сторінки
prev string nullable
URL попередньої сторінки (null на першій)
next string nullable
URL наступної сторінки (null на останній)

PaginationMeta

current_page integer
Номер поточної сторінки.
from integer nullable
Індекс першого елемента на поточній сторінці (з 1)
last_page integer
Номер останньої сторінки.
Елементи навігації (prev / кнопки-номери сторінок / next) для відмалювання пагінатора
path string
Базовий шлях для побудови URL пагінації
per_page integer
Скільки елементів на сторінці — те, що запрошено параметром per_page.
to integer nullable
Індекс останнього елемента на поточній сторінці (з 1)
total integer
Загальна кількість елементів за всіма сторінками

url string nullable
URL посилання (null для поточної сторінки)
label string
Відображуваний підпис — номер сторінки, "« Previous", "Next »" тощо
active boolean
True для елемента, що представляє поточну сторінку