Introduction
This is the KWIGA API documentation page. You can use this API to perform actions on your behalf inside your KWIGA cabinets β a cabinet is the workspace (your account / tenant) where your courses, contacts and other data live.
In order to enable the API you should click on the corresponding checkbox in the settings section of your account. After that a token will be generated and account hash will be created, that will be used for subsequent API calls.
If a token is compromised, it can be re-issued, that will make the old token inactive and generate a new one.
API is only available for those users who have API enabled in their account.
Rate limiting
The API has a rate limit of 200 requests per minute. The limit is counted per API token.
Every response includes the following rate-limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
maximum number of requests allowed per minute. |
X-RateLimit-Remaining |
number of requests still allowed in the current minute. |
Retry-After |
seconds to wait before retrying. Returned only when the limit is exceeded (HTTP 429). |
X-RateLimit-Reset |
UNIX timestamp indicating when the current window resets. Returned only when the limit is exceeded (HTTP 429). |
Idempotency
Mutating endpoints in the public API (currently POST /contacts/:contact/rewards; more will be marked individually) accept an optional Idempotency-Key request header. Pass a unique value once and reuse it on every retry of the same logical operation β the server will collapse retries to a single side-effect and return the same response on every replay.
Endpoints that support idempotency are marked with this badge in the reference: Idempotent
Contract:
| Header | Behavior |
|---|---|
Idempotency-Key (request) |
Client-generated unique string (1β255 chars). UUIDv4 or a stable business key like reward-for-quiz-attempt-4821 both work. Empty/missing header disables idempotency for that call. Clients that cannot set custom headers (form-based integrations) may pass the same value as a body/query field named idempotency_key β header takes precedence if both are present. |
Idempotent-Replayed: true (response) |
Returned on the second and subsequent calls with the same key. When you see this header, the server did NOT execute the operation again β the body contains the same object that was created on the first call (the entity is re-read and serialized again, so it reflects its current state). If that entity has since been permanently deleted, the body is a minimal acknowledgement with its type and id. |
Scope of a key is (cabinet, endpoint, key): the same business key on a different endpoint or in another cabinet is treated as a fresh operation. We recommend UUIDv4 anyway β collisions become statistically impossible.
Basic work with 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"
}
}
All requests must be directed to the domain:
https://api.kwiga.com
For authorization the API token should be send in one of the next variants:
in headers:
Token: {token}in GET/POST parameters:
token={token}
To identify the account in which the actions take place, the hash of the account must be sent along in one of the next variants:
in headers:
Cabinet-Hash: {cabinet_hash}in GET/POST parameters:
cabinet_hash={cabinet_hash}
PUT and DELETE requests can be sent as POST requests by specifying an additional _method parameter with the required method (PUT or DELETE).
You can set the localization of directories and validation messages with the header:
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) |
Errors
| Error Code | Meaning |
|---|---|
| 400 | Bad Request - Your request is invalid. |
| 401 | Unauthorized - Your API key is wrong. |
| 403 | Forbidden - The requested resource is hidden for administrators only. |
| 404 | Not Found - Server cannot find the requested resource. |
| 405 | Method Not Allowed -- Server knows the request method, but the target resource doesn't support this method. |
| 422 | Unprocessable entity. |
| 429 | Too Many Requests - You're sending too many requests! Slow down! |
| 500 | Internal Server Error - We had a problem with our server. Try again later. |
| 503 | Service Unavailable - We're temporarily offline for maintenance. Please try again later. |
CRM. Contacts
Contact records
List of contacts
Request example:
<?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_offers=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_offers=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 --request GET 'https://api.kwiga.com/contacts?page=1&per_page=15&filters[date_from]=2022-04-27&filters[search]=example.com&with_offers=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_offers=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_offers=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_offers=1",
"headers": {
"Token": "<Token>",
"Cabinet-Hash": "<Cabinet-Hash>",
"Accept": "application/json"
}
}
Example of response:
{
"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"
}
],
"offers": [
{
"id": 8,
"unique_offer_code": "ptJmPPXVYs0t",
"title": "ΠΡΠ΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠ΅ #8",
"limit_type": {
"id": 1,
"name": "ΠΠ΅ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΎ"
},
"limit_of_sales": null
}
]
},
"utm": {
"utm_source": [
"test-source"
],
"utm_campaign": [],
"utm_medium": [],
"utm_term": [
"test-term"
],
"utm_content": []
},
{
"id": 2,
"created_at": "2022-02-04T12:17:32.000000Z",
"email": "test2@example.com",
"first_name": "Petr",
"last_name": "Ivanov",
"phone": "+380983234512",
"tags": [],
"offers": []
}
],
"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": "« translation missing: en.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: en.pagination_next »",
"active": false
}
],
"path": "https://api.kwiga.com/contacts",
"per_page": 15,
"to": 2,
"total": 3
}
}
GET https://api.kwiga.com/contacts
URL Parameters
115asc, descDefault: descResponse envelope
POST alias
POST https://api.kwiga.com/contacts/query
A functional alias for the GET endpoint above. Accepts the same parameters in the request body instead of the query string β useful when the filter list is too large to fit a URL (or when JSON is simply easier to build on the client). Body has priority over query string, response shape is identical.
Get contact
Request example:
<?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"
}
}
Example of response:
{
"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"
}
]
}
}
GET https://api.kwiga.com/contacts/:contact
URL Parameters
Response envelope
Create contact
Request example:
<?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"
}
]
}
}
Example of response:
{
"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
falseenResponse envelope
Contact update
Request example:
<?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"
]
}
}
Example of response:
{
"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
Response envelope
Add purchase
This method creates or finds contact, as well as adds purchase of the specified offer
Request example:
<?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());
?>
<?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);
?>
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"}]}'
// 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);
});
# 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()
{
"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"
}
]
}
}
Example of response:
{
"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"
}
]
}
]
}
}
POST https://api.kwiga.com/contacts/purchases
Request
falsefalsefalseenhttps://sample-school.kwiga.com/expert/payments/offers/edit/3858). If not provided, the request falls back to the product_ids field.
If offer_id and product_ids are missing, an empty order will be created
trueResponse envelope
Tags
Contact tags adding
Request example:
<?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"
]
}
}
Example of response:
{
"success": true
}
POST https://api.kwiga.com/contacts/tags
Request
contact_ids. Kept for backwards compatibility; new integrations should use contact_ids.
Response envelope
Contact tags removing
Request example:
<?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"
]
}
}
Example of response:
{
"success": true
}
DELETE https://api.kwiga.com/contacts/tags
Request
contact_ids. Kept for backwards compatibility; new integrations should use contact_ids.
Response envelope
Products & subscriptions
Contact products list
Request example:
<?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"
}
}
Example of response:
{
"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
Returns a list of contact products with subscription and access period information. The aggregated_subscription parameter contains summary information about the terms and activity of access based on all user subscriptions for the product. The subscriptions parameter contains an array of all user subscriptions for the product.
Response envelope
Delete product from contact
Request example:
<?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"
}
}
Example of response:
{
"success": true
}
DELETE https://api.kwiga.com/contacts/:contact/products/:product
Deletes contact subscriptions for the specified product. Orders become custom without access to deleted products.
Response envelope
Delete products from contact (bulk)
Request example:
<?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
]
}
}
Example of response:
{
"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
Deletes subscriptions by specified conditions, and orders become custom without access to deleted products. You can combine product_id and offers parameters to delete part of products by specific offer.
Parameters
Response envelope
Freeze contact subscription
Request example:
<?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
}
}
Example of response:
{
"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
Freezes all of the contact's active subscriptions for the given product for count_frozen_days calendar days. The freeze shifts start_at / end_at / order_end_at forward, marks the subscription inactive, and notifies the student. On unfreeze (manual or automatic when the freeze period elapses) start_at is restored from the contact's order data, not just moved back by the same delta.
URL Parameters
Response envelope
Unfreeze contact subscription
Request example:
<?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"
}
}
Example of response:
{
"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
Unfreezes all of the contact's frozen subscriptions on the given product. The unfreeze restores start_at, recomputes end_at / order_end_at (subtracting any unused part of the freeze period), reactivates access where applicable, and notifies the student. Body is empty.
Response envelope
Extend contact subscription
Request example:
<?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
}
}
Example of response:
{
"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
Adds count_extend_days calendar days to end_at / order_end_at of every subscription the contact has on this product. For automation-style use, the same endpoint powers re-activation of expired subscriptions in CRM.
URL Parameters
Response envelope
Change subscription end date
Request example:
<?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
}
}
Example of response:
{
"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
Replaces end_at (and order_end_at when present) with an explicit datetime. The provided value is interpreted in the cabinet timezone identified by timezone_id (see the Timezones list endpoint); when timezone_id is omitted, end_at is treated as UTC.
URL Parameters
timezone_id.
end_at is treated as UTC.
Response envelope
Rewards (points)
List contact rewards (points)
Request example:
<?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 --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"
}
}
Example of response:
{
"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
Returns the contact's points journal β every accrual and deduction tied to quizzes, orders, gifts, automations or manual actions by curators. The response envelope adds sum_points, sum_accrued_points, sum_deducted_points for the filtered window.
URL Parameters
151Response envelope
Add a manual reward (points) to a contact Idempotent
Request example:
<?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
}
}
Example of response:
{
"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
Adds a manual reward to the contact's points journal. Positive points = accrual, negative = deduction. Optional product_id ties the reward to a specific product (otherwise it is cabinet-wide). Reason type is always Manual; the API user is recorded as the curator who issued it.
URL Parameters
message in the response. The student sees the comment in their points journal only when is_visible_to_student is true.
falseResponse envelope
Products
Cabinet products list
Request example:
<?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"
}
}
Example of response:
{
"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
Cabinet products list. Pagination is available, default is 15.
URL Parameters
asc, descDefault: desc151Response envelope
Courses
Cabinet courses list
Request example:
<?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 --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"
}
}
Example of response:
{
"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": "« Previous",
"active": false
},
{
"url": "http://api.kwiga.local/courses?page=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "http://api.kwiga.local/courses",
"per_page": 15,
"to": 1,
"total": 1
}
}
GET https://api.kwiga.com/courses
Cabinet courses list. Pagination is available, default is 15 (max: 15).
URL Parameters
offers, description, programasc, descDefault: desc151Response envelope
Course participants list
Request example:
<?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"
}
}
Example of response:
{
"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": "« Previous",
"active": false
},
{
"url": "http://api.kwiga.local/courses/20/users?page=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"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
Returns a list of course participants and their progress. Pagination is available, default is 15 (max: 250). Replace :course with the course id that can be obtained from the course editing/management URL or from the course list endpoint.
URL Parameters
151Optional comma-separated list of response extensions. Each token enables one section of the response β clients only pay for what they ask for.
course_programβ adds the course program tree at the top level of the response ascourse_program(array of CourseProgram nodes; lesson nodes contain CourseProgramSection children with attached quizzes). Returned once next todata/links/metabecause it is the same for every user on the page.lesson_progressβ addslesson_progressinside everydata[]row: array of LessonProgress objects (one per lesson the user has progress on).module_progressβ addsmodule_progressinside everydata[]row: array of ModuleProgress objects.quiz_progressβ addsquiz_progressinside everydata[]row: array of QuizProgress objects, one per(course_lesson_id, quiz_id)pair summarised from the user's latest non-cancelled attempt.
Note: the same quiz can be attached to several lessons of the same course, so quiz_progress is keyed by the lesson/quiz pair, not by quiz_id alone. The client stitches per-user progress to the program tree by course_nodeble_id of lesson/module nodes and by the (lesson.course_nodeble_id, quizzes[i].id) pair for section nodes.
Response envelope
include contains course_program.Course program tree returned at the top level when `include` contains `course_program`. Same shape as the existing `CourseProgram` schema, with lesson nodes containing `CourseProgramSection` children. Identical for all users on the page, so the API returns it once instead of repeating it per row.
POST alias
POST https://api.kwiga.com/courses/:course/users/query
A functional alias for the GET endpoint above. Accepts the same parameters in the request body instead of the query string β useful when the filter list is too large to fit a URL (or when JSON is simply easier to build on the client). Body has priority over query string, response shape is identical.
Receiving lesson-level analytics
In addition to the per-user course summary the endpoint can return
a detailed lesson-level progress breakdown. Opt in with the
include query parameter (see its description in the URL
parameters above for the full list of tokens and the response shape
each one enables). The course program is returned once at the top
level under course_program β it is the same for every user on the
page, so this keeps the response size proportional to the number of
users multiplied by the number of lessons/quizzes, not by the size
of the whole tree.
Response example with
?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,
"is_watched": true,
"is_completed": false,
"quizzing_status": {
"id": 3,
"slug": "InProgress",
"title": "In progress"
},
"watching_start_at": "2026-05-20T11:20: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-20T11:20:00.000000Z",
"updated_at": "2026-05-21T09:10:00.000000Z"
}
],
"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"}
],
"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": []
}
]
}
]
}
]
}
Stitching progress to the tree
The endpoint returns the course program once at the top level and
the per-user progress arrays inside each data[] row. To render
a user's progress on top of the program tree, do the following.
1. Index this user's progress arrays for fast lookup.
lesson_progressβ index bylesson_idmodule_progressβ index bymodule_idquiz_progressβ index by the pair(course_lesson_id, quiz_id), because the same quiz can be attached to several lessons
A note on names: progress entries use lesson_id/module_id/course_lesson_id,
while program nodes use course_nodeble_id. These are the same value
from different angles β see step 2.
2. Walk course_program and pick the right entry per node.
Every node carries course_nodeble_type + course_nodeble_id. Use
course_nodeble_id as the lookup key:
course_modulenode βmodule_progress[node.course_nodeble_id]course_lessonnode βlesson_progress[node.course_nodeble_id]info_sectionnode β sections don't carry their own progress. Walknode.quizzesand look upquiz_progress[(parent_lesson.course_nodeble_id, quiz.id)]for each quiz, whereparent_lessonis the closest enclosingcourse_lessonnode.
If the lookup returns nothing, the user has no progress on that node yet β render the empty state. The course program is the same for every user on the page, so it's safe to build it once and reuse across rows.
Quiz attempts
Cabinet quiz attempts list
Request example:
<?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 --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"
}
}
Example of response:
{
"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": "« 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 »", "active": false }
]
},
"attempts_unread_count": 12
}
GET https://api.kwiga.com/quiz-attempts
Returns a paginated list of student quiz attempts visible to the
authenticated curator across the whole cabinet (no product is
scoped from the URL β pass products if you need to narrow the
result). Pagination defaults to 15 items per page, max 250.
attempts_unread_count at the top level reflects the
unread count for the same filter set, suitable for a "new attempts"
badge.
URL Parameters
151last_activity_atPossible values: last_activity_at, status_updated_at, finished_at, statusDefault: last_activity_atdescPossible values: asc, descDefault: descResponse envelope
POST alias
POST https://api.kwiga.com/quiz-attempts/query
A functional alias for the GET endpoint above. Accepts the same parameters in the request body instead of the query string β useful when the filter list is too large to fit a URL (or when JSON is simply easier to build on the client). Body has priority over query string, response shape is identical.
Offers
Get offers
Request example:
<?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"
}
}
Example of response:
{
"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": "« Previous",
"active": false
},
{
"url": "http://api.kwiga.local/offers?page=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "http://api.kwiga.local/offers",
"per_page": 15,
"to": 1,
"total": 2
}
}
GET https://api.kwiga.com/offers
URL Parameters
Response envelope
Get offer
Request example:
<?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"
}
}
Example of response:
{
"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
Response envelope
Marketing. Mailing
Contact List List
Request example:
<?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"
}
}
Example of response:
{
"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": "« translation missing: en.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: en.pagination_next »",
"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
Response envelope
Getting a contact list
Request example:
<?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"
}
}
Example of response:
{
"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
Response envelope
Creating a contact list
Request example:
<?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": "ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅"
}
}
Example of response:
{
"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
Response envelope
Adding Contacts to a List
Request example:
<?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
}
}
Example of response:
{
"success": true
}
POST https://api.kwiga.com/mailing/contact-lists/bulk-contacts
URL Parameters
Response envelope
Payments. Coupons
Coupon list
Request example:
<?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 --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"
}
}
Example of response:
{
"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
Response envelope
Getting a coupon
Request example:
<?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"
}
}
Example of response:
{
"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
Response envelope
Create coupon
Request example:
<?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
}
}
Example of response:
{
"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
discount_with_percent or discount_with_currency2024-12-31 or 2024-12-31 23:59:59Response envelope
Check coupon
Request example:
<?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
}
}
Example of response:
{
"data": {
"can_use": true,
"discount": 6.5
}
}
POST https://api.kwiga.com/coupons/check
Checks coupon for existence and usability (limit not exhausted / not expired).
Note: when used on checkout page, the coupon may not be applied under the following conditions: if the use of coupons/this coupon is disabled on the offer; there are restrictions on the coupon by contact lists; the user has already used the coupon and there is a limit on the number of uses on the coupon.
Parameters
Response envelope
Certificates
Receiving a certificate by number
Request example:
<?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"
}
}
Example of response:
{
"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,
"user": {
"id": 273,
"name": "test name",
"email": "test-student@kwiga.com"
},
"certificateble_title": "Test course"
}
}
GET https://api.kwiga.com/certificates/by-number/:number
Response envelope
Timezones
Getting a list of time zones
Request example:
<?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"
}
}
Example of response:
{
"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
Response envelope
Schemas
Reusable response objects referenced across endpoints. Fields marked as conditional only appear in the response under the documented circumstance (a query flag, an optional include, or a specific endpoint).
Contacts
Contact
Tags attached to the contact
Timestamp of the contact's last activity within the cabinet
First recorded visit of the contact
Aggregated UTM attribution across the contact's visits
All UTM-tracked visits of the contact
Offers the contact has purchased
Orders placed by the contact
Cabinet-specific custom fields filled in for this contact
Certificate issuance details per product the contact owns
ContactIdentifier
ContactAdditionalField
Definition of the cabinet-specific custom field
ContactField
Field input format token
Validation rule expression
Field type identifier (text, number, date, enum, etc.)
Display order within the cabinet
Allowed values for enum-style fields
Cabinet-specific ordering for first/last name display
ContactList
Owning cabinet identifier
Owning user identifier
Email campaigns associated with the list
Filter rules driving the segment membership
ContactListStatistic
FlowTag
Number of contacts the tag is attached to
Courses
Course
CourseIdentify
Compact identity entries for the course's lessons
URL to the course preview image (or default placeholder when missing)
Compact identity entries for offers linked to the course
CourseLessonIdentify
CourseLessonSimple
Quizzes attached to the lesson
Identity payload of the parent course
CourseModuleSimple
CourseProgram
CourseProgress
CourseUser
include contains lesson_progress.Array of `LessonProgress` objects β per-lesson watched/completed snapshots for this user. Returned when `include` contains `lesson_progress`.
include contains module_progress.Array of `ModuleProgress` objects β per-module watched/completed snapshots for this user (aggregated server-side from the module's lessons). Returned when `include` contains `module_progress`.
include contains quiz_progress.Array of `QuizProgress` objects β one entry per `(course_lesson_id, quiz_id)` pair summarised from the user's latest non-cancelled attempt. Returned when `include` contains `quiz_progress`.
InfoUnit
QuizIdentifier
QuizAttemptIdentifier
Quiz metadata payload
Lesson metadata payload
Coupons
ExpertCoupon
Coupon type metadata
Compact identity payload of the linked contact
Identity payloads of offers limited by this coupon
Latest internal comment payload (admin-facing)
CouponType
CouponCheck
Certificates
Certificate
When the user completed the certificateble entity (course, etc.)
Points earned by the user toward the certificateble
CertificateTemplate
StudentProductCertificateInfo
Title of the certificateble entity
Offers
Offer
Price after the active discount is applied
OfferIdentifier
OfferSimple
OfferDiscount
Compact identity payload of the parent offer
Orders
Order
OrderFunnel
OrderGroup
OrderStage
Payments
Payment
Transaction
Price
Currency
Products
Product
Public link to the productable entity
ProductIdentifier
ProductAggregatedSubscription
ProductSubscription
UserProduct
Aggregated access info (overall is_active/start/end across all subscriptions)
Individual subscription records (one per order/offer)
EmailCampaign
Email template payload (system or custom)
Full campaign statistic snapshot
EmailCampaignIdentifier
Visitors
Visit
User agent / device information
VisitLocation
VisitUserAgent
UtmList
Comments
CommentIdentifier
Compact payload of the target entity
Quiz attempts
QuizAttempt
Attempt status as an enum object — id (numeric), slug (stable case name) and title (label already translated for the cabinet locale).
1— Completed2— Not completed3— Revision required4— The latest version changed the status5— In progress6— Pending review7— Not started
InfoSectionReport
Rewards (gamification)
FlowRewardLog
points β accrued or deducted. Mirrors the accrual_type filter on the list endpoint.
Why this points entry exists. Possible ids:
1— Quiz passed2— Quiz canceled3— Quiz attempt reset previous scores4— Quiz attempt set new scores5— Pay by points6— Manual7— Automation8— Quiz scores changing9— Pay by points for gift
event). NULL for manual entries.
quiz_attempt, order, gift). NULL for manual entries.
eventable_type. NULL for manual entries.
manual_comment is shown to the student in their own points journal. NULL for non-manual entries. Curators (this API included) always see the comment regardless of the flag.