Ваші клієнти та їхні кредити
Додавайте своїх клієнтів, ведіть їхні рахунки кредитів, поповнюйте, списуйте й коригуйте їхні кредити, блокуйте клієнта й задавайте, скільки кредитів коштує операція.
Клієнт тут — це клієнт вашого власного продукту, названий ідентифікатором, який йому дає ваша система. Slen веде рахунки кредитів ваших клієнтів, бере з них кредити, коли флоу виконується для них, і відхиляє запуск, за який вони не можуть заплатити. Операції цієї сторінки додають і видаляють клієнтів, налаштовують їхні рахунки, рухають їхні кредити, блокують клієнта й задають, скільки кредитів коштує операція. Консоль показує тих самих клієнтів на екрані Клієнти.
Клієнти та рахунки
Клієнта називає
external_user_id— той самий ідентифікатор, який запит, що запускає флоу, надсилає вcontext.user_id. Це непрозорий ідентифікатор вашої системи, а не адреса пошти й не ім’я.PUT /v1/customers/{externalUserId}додає клієнта без кредитів і без рахунку. Він відповідає201, коли створив клієнта, і200, коли клієнт уже був, тому повтору не потрібен ключ.Рахунок належить одному клієнту й одному типу кредитів. Тип кредитів — назва, яку ви вибираєте самі; якщо її не вказати, це
default. Клієнт із двома типами кредитів має два рахунки.Рахунок створює перше поповнення його типу кредитів або його налаштування. Списання й коригування нічого не створюють.
Рахунок несе
balance,reservedіavailable.reserved— це те, що тримають поточні виклики, аavailable— цеbalanceмінусreserved.Для клієнта, якого проєкт ніколи не бачив, відповідь —
404з кодомcustomer_not_found. Для клієнта без рахунківaccountsу відповіді порожній.
Читання потребує customers:read, а кожна зміна — customers:write, крім задання ціни, яке потребує management:write. Ключ вашого застосунку з gateway:write і customers:write запускає флоу й веде своїх клієнтів і більше нічого не змінює.
Налаштування рахунку
PUT /v1/customers/{externalUserId}/accounts/{creditType} задає обидва налаштування рахунку одразу. Він створює клієнта й рахунок, коли їх немає, і відповідає 201, а інакше змінює налаштування й відповідає 200.
allow_negativeдозволяє рахунку йти нижче нуля. Типово дозвіл вимкнено, і тоді холд або списання, які рахунок не може покрити, відхиляються зinsufficient_credits. Його можна вимкнути, поки баланс нижче нуля: захист діє з наступного руху.low_balance_thresholdзадає рівень події вебхукаcustomer.balance_low. Подія надсилається, коли рух переводитьavailableз цього рівня або вище на значення нижче нього. Нуль, типове значення, вимикає подію.Поповнення й коригування не змінюють налаштувань: тіло, у якому назване будь-яке з цих полів, відхиляється з
422.
Як запуск витрачає кредити
Запуск витрачає кредити, коли він називає клієнта в context.user_id і проєкт має ціну в кредитах для його операції. context.credit_type називає тип кредитів, а без нього тип — default. Тип кредитів вирішує, який рахунок платить і яка ціна застосовується.
Операція без ціни для цього типу кредитів або з нульовою ціною не бере кредитів.
Кожен виклик моделі в запуску резервує ціну на рахунку до того, як модель викликано. Коли виклик завершено й розраховано, холд стає витратою, а виклик, який не вдався, повертає його.
Рахунок, який не може покрити ціну, відхиляє виклик із кодом
insufficient_credits. Запуск для клієнта, який не має рахунку цього типу кредитів, відхиляється так само.Запуск для клієнта, якого проєкт не знає, відхиляється з кодом
customer_not_found.Запуск для заблокованого клієнта відхиляється з кодом
customer_blocked— Заблокувати клієнта.
Ціна задається для коду операції й типу кредитів, і наступний холд уже використовує її. Повторне задання ціни замінює її, а вже взятий холд зберігає ціну, за якою його взято.
Рухи кредитів
Кредити рухають три операції. Кожна потребує заголовок Idempotency-Key довжиною не більше 190 символів, а запит без нього відповідає 400 з кодом idempotency_key_required.
Поповнення додає кредити. Воно створює клієнта й рахунок, коли їх немає.
Списання (charge) знімає кредити за те, що ваш застосунок продає сам, — вивантаження, місце. Виписка показує його як
charge. Для клієнта, якого проєкт не знає, відповідь —404з кодомcustomer_not_found, а для клієнта без рахунку цього типу кредитів —402з кодомinsufficient_credits.Коригування виправляє баланс. Його
creditsмає знак, а нуль відхиляється. Якщо рахунку немає, відповідь —404з кодомcustomer_not_found.
Що спільне в трьох:
У
creditsне більше чотирьох знаків після крапки. П’ятий знак відхиляється з422, а не округлюється.note, до 255 символів, зберігається в записі, і його видно у виписці: номер рахунку на оплату, причина.Списання або коригування не можуть опустити баланс нижче того, що тримає рахунок, якщо рахунок не допускає від’ємного балансу. Такий запит відповідає
402з кодомinsufficient_credits. Поповнення так не відхиляється ніколи, зокрема на рахунку нижче нуля.Заблокованого клієнта можна поповнювати, з нього можна списувати, і його можна коригувати.
Списання — не витрата: звіт маржі зіставляє кредити виклику з тим, скільки виклик коштував, а за списанням не стоїть жоден виклик.
Повтор із ключем нічого не рухає.
Повтор того самого запиту з його ключем відповідає
200. Протягом 24 годин, якщо сервіс не налаштовано інакше, відповіддю є перша відповідь, з балансом того моменту. Потім — рахунок у його теперішньому вигляді.Якщо ключ уже називає інше поповнення, списання або коригування проєкту, відповідь —
409з кодомidempotency_conflict. У ці 24 години будь-яке інше тіло під цим ключем — інший запит, зокрема іншийnote. Потім інший клієнт, інший тип кредитів, інший вид руху або інша сума — це інший рух. Новому руху потрібен новий ключ.Відхилений рух не зберігається: той самий запит можна надіслати знову з його ключем.
Заблокувати клієнта
POST /v1/customers/{externalUserId}/block блокує клієнта, а POST /v1/customers/{externalUserId}/unblock знову його пускає.
Новий запуск заблокованого клієнта відхиляється з
403і кодомcustomer_blockedдо того, як щось створено.Його запуск, який уже йде, — у черзі або в очікуванні погодження, — відхиляється на наступному холді кредитів виклику моделі. Вузол моделі виходить через Відмова, а без гілки там виконання закінчується зі статусом
rejectedі кодомcustomer_blocked. Запуск операції без ціни в кредитах не резервує кредитів і доходить до кінця.Повтор запиту з ключем, з яким його надіслали до блокування, отримує у відповідь те виконання.
Кредити, як і раніше, рухаються, налаштування змінюються, а виписка читається, тож із заблокованим клієнтом можна розрахуватися.
blocked_at— момент першого блокування. Повторне блокування його зберігає й замінюєblock_reason, лише коли переданоreason. Розблокування очищає обидва поля.
Видалити клієнта
DELETE /v1/customers/{externalUserId} видаляє клієнта та його рахунки, лише коли з ним нічого не відбувалося: кожен рахунок на нулі й нічого не зарезервовано, а у виписці немає жодного запису. Інакше клієнт лишається, а відповідь пояснює чому.
{
"error": {
"code": "customer_has_history",
"message": "The customer has credits or a credit history and cannot be deleted; block it instead",
"request_id": "5d8a3f1c-2b6e-4c9a-8f17-3e0b9d4c6a25"
}
}Виписка пояснює баланс, тому вона зберігається. Такого клієнта заблокуйте. Видалення й рух того самого клієнта ніколи не обходять одне одного: видалення, яке зустріло поточне поповнення, чекає й потім відмовляє, а поповнення, яке зустріло поточне видалення, чекає й потім створює клієнта заново.
Читати клієнтів і виписки
Список ділиться на сторінки, найновіший клієнт — першим, і кожен клієнт приходить з усіма своїми рахунками.
statusлишає активних або заблокованих клієнтів,q— клієнтів, чийexternal_user_idйого містить, аcredit_type— тих, хто має рахунок цього типу кредитів.low_balance— це число, а не прапорець: він лишає клієнтів, які мають рахунок зavailableнижче нього, — рахунок типуcredit_type, коли передано обидва. Фільтри вибирають клієнтів і ніколи не скорочують список їхніх рахунків.Виписка клієнта перелічує кожен рух його кредитів, найновіший — першим. Запис несе рахунок таким, яким його лишив рух, у
balance_afterіavailable_after, іnoteзапиту.entry_typeлишає один вид рухів, аcredit_type— рухи одного рахунку. Клієнт без рахунків має порожню виписку.
entry_type | Що зрушило | credits |
|---|---|---|
topup | Поповнення | Додатне |
charge | Списання вашого застосунку | Від’ємне |
adjust | Коригування | З будь-яким знаком |
hold | Виклик моделі зарезервував свою ціну з available | Від’ємне |
commit | Виклик розраховано, і зарезервована ціна пішла з balance | Від’ємне |
release | Холд повернувся в available | Додатне |
Не підсумовуйте credits, щоб дізнатися, скільки витратив клієнт. Виклик, який витратив кредити, має два від’ємні записи на ту саму ціну — свій hold і свій commit, і витрата — це лише записи commit.
Операції
Описи операцій і полів подано англійською.
Клієнти та їхні рахунки
GET /v1/customers
- Дозвіл:
customers:read - Доступно в SDK для PHP і Node.js
One cursor page of the project's end customers, newest first, each with every credit account it holds (accounts, by credit_type; empty for a customer created by PUT /v1/customers/{externalUserId} that holds none yet). The filters choose customers: credit_type — those holding an account of that type; low_balance — those holding an account with fewer credits still spendable (balance - reserved) than the threshold, of credit_type when it is given too; status, q. The nested list is never narrowed: a customer that matches comes with all its accounts.
Параметри
| Параметр | Опис |
|---|---|
cursor необовʼязковоquery | The |
view необовʼязковоquery |
|
limit необовʼязковоquery | Page size.
|
status необовʼязковоquery | Only customers in this status.
|
credit_type необовʼязковоquery | Customers holding an account of this credit type.
|
low_balance необовʼязковоquery | A threshold, not a flag: |
q необовʼязковоquery | Narrows the list to the customers whose
|
Відповіді
One cursor page of customers
{
"data": [
{
"customer_id": "74438200973594624",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": "2026-10-02T09:30:00Z",
"block_reason": "string",
"accounts": [
{
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"meta": {
"limit": 1,
"next_cursor": "string"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Клієнт і його рахунки
GET /v1/customers/{externalUserId}
- Дозвіл:
customers:read - Доступно в SDK для PHP і Node.js
The customer and every credit account it holds, by credit_type. A customer with no account yet answers with accounts: []; an external_user_id the project has never seen is 404 customer_not_found, and so is one of another project. The external_user_id is the caller's opaque identifier — not an email address or a name.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Відповіді
The customer
{
"data": {
"customer_id": "74438200973594624",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": "2026-10-02T09:30:00Z",
"block_reason": "string",
"accounts": [
{
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Створити клієнта, якщо його ще немає
PUT /v1/customers/{externalUserId}
- Дозвіл:
customers:write - Доступно в SDK для PHP і Node.js
Creates the customer when the project has none by this external_user_id (201) and answers the one it has otherwise (200) — idempotent by nature, so no Idempotency-Key: a repeat, or two first calls at once, end with one customer. No credit account is created: a customer without accounts is a legal state, and the first top-up or account setting creates the account of its credit type. The body is an empty object; any field is 422.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Тіло запиту необовʼязково
{}- Максимум ключів:
0
Відповіді
The customer already existed
{
"data": {
"customer_id": "74438200973594624",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": "2026-10-02T09:30:00Z",
"block_reason": "string",
"accounts": [
{
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}The customer was created
{
"data": {
"customer_id": "74438200973594624",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": "2026-10-02T09:30:00Z",
"block_reason": "string",
"accounts": [
{
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Видалити клієнта без історії
DELETE /v1/customers/{externalUserId}
- Дозвіл:
customers:write - Доступно в SDK для PHP і Node.js
Removes the customer and its credit accounts — only when nothing ever happened to them: every account at a zero balance with nothing reserved, and not one entry in its statement. Otherwise 409 customer_has_history: the journal is append-only, its entries explain the balance, so a customer with history is blocked (POST /v1/customers/{externalUserId}/block) rather than deleted. A delete that meets a top-up or a charge of the same customer under way waits for it, and then refuses; a top-up that meets a delete under way waits, and then creates the customer again. An external_user_id the project has never seen is 404 customer_not_found. The external_user_id is the caller's opaque identifier — not an email address or a name: erasing one with history is not offered. Audited.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Відповіді
The customer and its accounts are gone
Без тіла
Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 409 | State conflict: idempotency clash, stale draft, illegal transition; the last owner of an organization ( |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Задати налаштування рахунку
PUT /v1/customers/{externalUserId}/accounts/{creditType}
- Дозвіл:
customers:write - Доступно в SDK для PHP і Node.js
The settings of the customer's account of this credit_type: whether it may go below zero (allow_negative) and the level under which a movement sends customer.balance_low (low_balance_threshold, 0 switches the warning off). Both fields are required — the body is the whole of the settings. Creates the customer and the account when they do not exist yet (201); otherwise changes the settings and answers 200. Not a movement of credits, so no Idempotency-Key: the same settings put twice are the same settings. allow_negative: false on an account already below zero is allowed — the guard holds from the next movement on. A blocked customer's settings can be changed.
This is the one place the settings change: a top-up or an adjustment that names either field is 422.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
creditType обовʼязковоpath | The credit type of the account — tenant-defined text,
|
Тіло запиту
{
"allow_negative": true,
"low_balance_threshold": "12.5000"
}Відповіді
The settings were changed
{
"data": {
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}The account was created with these settings
{
"data": {
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Заблокувати клієнта
POST /v1/customers/{externalUserId}/block
- Дозвіл:
customers:write - Доступно в SDK для PHP і Node.js
A blocked customer's new runs are refused 403 customer_blocked before anything is created (POST /v1/operations/execute with its context.user_id), and a run of theirs already under way is refused at its next hold of credits. The balance can still be moved — top-up, charge, adjustment and the account settings stay open, so the customer can be settled with — and the statement still reads. blocked_at is set the first time; blocking again answers 200 without moving it, and replaces block_reason only when a reason is given. An external_user_id the project has never seen is 404 customer_not_found. Audited.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Тіло запиту необовʼязково
{
"reason": "string"
}Відповіді
The customer, blocked
{
"data": {
"customer_id": "74438200973594624",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": "2026-10-02T09:30:00Z",
"block_reason": "string",
"accounts": [
{
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Розблокувати клієнта
POST /v1/customers/{externalUserId}/unblock
- Дозвіл:
customers:write - Доступно в SDK для PHP і Node.js
The customer is active again: blocked_at and block_reason are cleared and new runs are accepted. Unblocking a customer that is not blocked answers 200 with the customer as it is. The body is empty; any field is 422. An external_user_id the project has never seen is 404 customer_not_found. Audited.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Тіло запиту необовʼязково
{}- Максимум ключів:
0
Відповіді
The customer, active
{
"data": {
"customer_id": "74438200973594624",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": "2026-10-02T09:30:00Z",
"block_reason": "string",
"accounts": [
{
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
],
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Виписка за кредитами клієнта
GET /v1/customers/{externalUserId}/entries
- Дозвіл:
customers:read - Доступно в SDK для PHP і Node.js
Every movement of the customer's credits, newest first, each carrying the balance it left behind — so a reader never has to replay the feed to know where the customer stood — and the note its caller gave. Cursor-paged, no date window: a statement is read from the top. An external_user_id the project has never seen is 404 customer_not_found; a customer without an account (or without one of the credit_type asked for) has an empty statement.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
cursor необовʼязковоquery | The |
limit необовʼязковоquery | Page size (default 30).
|
credit_type необовʼязковоquery | Narrows a customer who holds several pots down to one.
|
entry_type необовʼязковоquery | Only the movements of this type — the top-ups, say, or the charges.
|
Відповіді
One cursor page of credit movements
{
"data": [
{
"entry_id": "74438200973594624",
"customer_account_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"entry_type": "hold",
"credits": "12.5000",
"balance_after": "12.5000",
"available_after": "12.5000",
"reservation_id": "74438200973594624",
"note": "string",
"created_at": "2026-10-02T09:30:00Z"
}
],
"meta": {
"limit": 1,
"next_cursor": "string"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Поповнити кредити клієнта
POST /v1/customers/{externalUserId}/topup
- Дозвіл:
customers:write - Потрібен Idempotency-Key
- Доступно в SDK для PHP і Node.js
Creates the account when it does not exist yet. Moves money, so it is behind the idempotency middleware.
The Idempotency-Key is also written on the journal entry, where it is unique within the project for as long as the entry exists. A key that already names this top-up — same customer, same credit_type, same credits — answers 200 with the account as it stands now and credits nothing, however long ago the first call was and even while it is still running. A key that names any other top-up, adjustment or charge of the project answers 409 idempotency_conflict and moves nothing. The note does not take part in that comparison; while the stored answer of the first call lasts, any other body under its key — another note included — is 409 idempotency_conflict as for every financial write.
An account the top-up creates starts with allow_negative: false and no low-balance warning. The settings are PUT /v1/customers/{externalUserId}/accounts/{creditType}'s: a body that names allow_negative or low_balance_threshold is 422, as is any field this body does not describe.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Idempotency-Key обовʼязковоheader | Replay guard for a financial write. The same key returns the original result with the original status and never charges twice; a different body under a used key answers
|
Тіло запиту
{
"credits": "12.5000",
"credit_type": "default",
"note": "string"
}Відповіді
The account after the top-up
{
"data": {
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 400 | Malformed request — e.g. the | |
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 409 | State conflict: idempotency clash, stale draft, illegal transition; the last owner of an organization ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Скоригувати баланс клієнта
POST /v1/customers/{externalUserId}/adjust
- Дозвіл:
customers:write - Потрібен Idempotency-Key
- Доступно в SDK для PHP і Node.js
Signed: an adjustment may take credits away. Zero is refused — it would be an audit row saying nothing.
The Idempotency-Key is unique within the project on the journal entry, exactly as for a top-up: repeating the same adjustment under its key answers 200 with the account as it stands now and moves nothing — also when the balance could no longer cover it a second time — and a key that names any other top-up, adjustment or charge answers 409 idempotency_conflict. The note takes no part in that comparison, exactly as for a top-up.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Idempotency-Key обовʼязковоheader | Replay guard for a financial write. The same key returns the original result with the original status and never charges twice; a different body under a used key answers
|
Тіло запиту
{
"credits": "12.5000",
"credit_type": "default",
"note": "string"
}Відповіді
The account after the adjustment
{
"data": {
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 400 | Malformed request — e.g. the | |
| 401 | No credential, or one that is not valid | |
| 402 | Refused because of money: | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 409 | State conflict: idempotency clash, stale draft, illegal transition; the last owner of an organization ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Списати кредити клієнта за послугу застосунку
POST /v1/customers/{externalUserId}/charge
- Дозвіл:
customers:write - Потрібен Idempotency-Key
- Доступно в SDK для PHP і Node.js
The application's own charge — a PDF export, a seat, anything it sells outside an operation. credits is the positive amount to take; the journal entry is a charge with the amount negative and the note the caller gave. Guarded like every debit: an account with allow_negative off cannot go below what is held on it (402 insufficient_credits). A charge is not spend: the margin report sets credits against provider dollars, and no provider stands behind it. Crossing the account's low_balance_threshold sends customer.balance_low like any movement.
An external_user_id the project has never seen is 404 customer_not_found — nothing is created; a known customer without an account of the credit_type holds no such credits: 402 insufficient_credits. A blocked customer can be charged. Moves money, so it is behind the idempotency middleware, and the Idempotency-Key is unique within the project on the journal entry exactly as for a top-up: the same charge repeated under its key answers 200 with the account as it stands now and takes nothing — also when the balance could no longer cover it a second time — and a key that names any other top-up, adjustment or charge answers 409 idempotency_conflict.
Параметри
| Параметр | Опис |
|---|---|
externalUserId обовʼязковоpath | The end customer's identifier in the caller's own system.
|
Idempotency-Key обовʼязковоheader | Replay guard for a financial write. The same key returns the original result with the original status and never charges twice; a different body under a used key answers
|
Тіло запиту
{
"credits": "12.5000",
"credit_type": "default",
"note": "string"
}Відповіді
The account after the charge
{
"data": {
"customer_account_id": "74438200973594624",
"customer_id": "74438200973594624",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "12.5000",
"reserved": "12.5000",
"available": "12.5000",
"allow_negative": true,
"low_balance_threshold": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 400 | Malformed request — e.g. the | |
| 401 | No credential, or one that is not valid | |
| 402 | Refused because of money: | |
| 403 | Authenticated, but not allowed: scope ( | |
| 404 | No such resource for this tenant. Somebody else's resource — and a resource of a project the caller does not reach — answers the same way on purpose: the two must not be distinguishable. | |
| 409 | State conflict: idempotency clash, stale draft, illegal transition; the last owner of an organization ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Ціни операцій у кредитах
GET /v1/credit-prices
- Дозвіл:
customers:read - Доступно в SDK для PHP і Node.js
The project's price list: how many credits of which type one run of an operation holds from the customer named by context.user_id. An operation without a price does not touch credits at all. A handful of rows per project, so no cursor; the filters are equality.
Параметри
| Параметр | Опис |
|---|---|
operation_code необовʼязковоquery | Only the prices of this operation code (equality).
|
credit_type необовʼязковоquery | Only the prices in this credit type (equality).
|
Відповіді
Credit prices of the project, by operation code and credit type
{
"data": [
{
"credit_price_id": "74438200973594624",
"project_id": "74438200973594624",
"operation_code": "generate_email",
"credit_type": "default",
"credits": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
]
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Задати ціну операції в кредитах
PUT /v1/credit-prices
- Дозвіл:
management:write
Upsert by (operation_code, credit_type). Zero is allowed — it marks an operation free without deleting the row. A new price takes effect on the next hold; holds already taken keep theirs.
Тіло запиту
{
"operation_code": "generate_email",
"credits": "12.5000",
"credit_type": "default"
}Відповіді
The stored price
{
"data": {
"credit_price_id": "74438200973594624",
"project_id": "74438200973594624",
"operation_code": "generate_email",
"credit_type": "default",
"credits": "12.5000",
"created_at": "2026-10-02T09:30:00Z",
"updated_at": "2026-10-02T09:30:00Z"
}
}Помилки
| Статус | Коди | Коли |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Заголовки
X-Request-Id
Тіло кожної помилки — конверт помилки.
Приклади
Знайти клієнтів, у яких закінчуються кредити
low_balance лишає клієнтів, які мають рахунок, де доступно менше 20 кредитів. Цей клієнт має на балансі 18, і 4 з них зарезервовано.
curl "https://api.slen.ai/api/v1/customers?low_balance=20" \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": [
{
"customer_id": "74439840149372928",
"external_user_id": "user_3107",
"status": "active",
"blocked_at": null,
"block_reason": null,
"accounts": [
{
"customer_account_id": "74439840153567232",
"customer_id": "74439840149372928",
"project_id": "74438200973594624",
"external_user_id": "user_3107",
"credit_type": "default",
"balance": "18.0000",
"reserved": "4.0000",
"available": "14.0000",
"allow_negative": false,
"low_balance_threshold": "0.0000",
"created_at": "2026-09-28T11:02:17+00:00",
"updated_at": "2026-10-02T08:39:51+00:00"
}
],
"created_at": "2026-09-28T11:02:17+00:00",
"updated_at": "2026-10-02T08:39:51+00:00"
}
],
"meta": {
"limit": 50,
"next_cursor": null
}
}Прочитати клієнта
Клієнт має один рахунок, типу кредитів default. Рух, який опускає його available нижче 10, надсилає customer.balance_low.
curl https://api.slen.ai/api/v1/customers/user_8421 \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": {
"customer_id": "74439117522108416",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": null,
"block_reason": null,
"accounts": [
{
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "82.5000",
"reserved": "0.0000",
"available": "82.5000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T09:20:05+00:00"
}
],
"created_at": "2026-10-02T08:40:00+00:00",
"updated_at": "2026-10-02T08:40:00+00:00"
}
}Додати клієнта
Тіло порожнє. Клієнт новий, тому відповідь — 201, а accounts порожній, доки поповнення або налаштування не створять рахунок.
curl -X PUT https://api.slen.ai/api/v1/customers/user_8421 \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{
"data": {
"customer_id": "74439117522108416",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": null,
"block_reason": null,
"accounts": [],
"created_at": "2026-10-02T08:40:00+00:00",
"updated_at": "2026-10-02T08:40:00+00:00"
}
}Видалити клієнта
Цього клієнта додали й жодного разу не поповнили, тому клієнт видаляється. Відповідь не має тіла.
curl -X DELETE https://api.slen.ai/api/v1/customers/user_9050 \
-H "Authorization: Bearer $SLEN_API_KEY"204 No Content
Налаштувати рахунок
Налаштування створюють рахунок типу кредитів default з попередженням нижче 10 кредитів. Рахунок не може піти нижче нуля.
curl -X PUT https://api.slen.ai/api/v1/customers/user_8421/accounts/default \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"allow_negative": false,
"low_balance_threshold": "10"
}'{
"data": {
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "0.0000",
"reserved": "0.0000",
"available": "0.0000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T08:40:30+00:00"
}
}Заблокувати клієнта
Причину видно як block_reason. Рахунки зберігають свої кредити.
curl https://api.slen.ai/api/v1/customers/user_8421/block \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Chargeback on invoice 1042"
}'{
"data": {
"customer_id": "74439117522108416",
"external_user_id": "user_8421",
"status": "blocked",
"blocked_at": "2026-10-02T09:31:10+00:00",
"block_reason": "Chargeback on invoice 1042",
"accounts": [
{
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "82.5000",
"reserved": "0.0000",
"available": "82.5000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T09:20:05+00:00"
}
],
"created_at": "2026-10-02T08:40:00+00:00",
"updated_at": "2026-10-02T09:31:10+00:00"
}
}Розблокувати клієнта
Запит не має тіла. Клієнт знову active, а blocked_at і block_reason очищено.
curl -X POST https://api.slen.ai/api/v1/customers/user_8421/unblock \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": {
"customer_id": "74439117522108416",
"external_user_id": "user_8421",
"status": "active",
"blocked_at": null,
"block_reason": null,
"accounts": [
{
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "82.5000",
"reserved": "0.0000",
"available": "82.5000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T09:20:05+00:00"
}
],
"created_at": "2026-10-02T08:40:00+00:00",
"updated_at": "2026-10-02T10:02:00+00:00"
}
}Прочитати виписку клієнта
У відповіді три рухи, найновіший — першим: списання за вивантаження, коригування, яке зняло 12.5 кредиту, і поповнення перед ними.
curl https://api.slen.ai/api/v1/customers/user_8421/entries \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": [
{
"entry_id": "74439673397166080",
"customer_account_id": "74439117526302720",
"external_user_id": "user_8421",
"credit_type": "default",
"entry_type": "charge",
"credits": "-5.0000",
"balance_after": "82.5000",
"available_after": "82.5000",
"reservation_id": null,
"note": "PDF export 311",
"created_at": "2026-10-02T09:20:05+00:00"
},
{
"entry_id": "74439652312547328",
"customer_account_id": "74439117526302720",
"external_user_id": "user_8421",
"credit_type": "default",
"entry_type": "adjust",
"credits": "-12.5000",
"balance_after": "87.5000",
"available_after": "87.5000",
"reservation_id": null,
"note": null,
"created_at": "2026-10-02T09:15:42+00:00"
},
{
"entry_id": "74439117530497024",
"customer_account_id": "74439117526302720",
"external_user_id": "user_8421",
"credit_type": "default",
"entry_type": "topup",
"credits": "100.0000",
"balance_after": "100.0000",
"available_after": "100.0000",
"reservation_id": null,
"note": "Invoice 1042",
"created_at": "2026-10-02T08:41:00+00:00"
}
],
"meta": {
"limit": 30,
"next_cursor": null
}
}Поповнити клієнта
Ключ називає це поповнення, і повтор із тим самим ключем нічого не додає. Примітка називає рахунок на оплату, яким його оплачено.
curl https://api.slen.ai/api/v1/customers/user_8421/topup \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Idempotency-Key: topup-user_8421-invoice-1042" \
-H "Content-Type: application/json" \
-d '{
"credits": "100",
"note": "Invoice 1042"
}'{
"data": {
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "100.0000",
"reserved": "0.0000",
"available": "100.0000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T08:41:00+00:00"
}
}Скоригувати баланс
Від’ємна сума знімає кредити.
curl https://api.slen.ai/api/v1/customers/user_8421/adjust \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Idempotency-Key: adjust-user_8421-refund-77" \
-H "Content-Type: application/json" \
-d '{
"credits": "-12.5"
}'{
"data": {
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "87.5000",
"reserved": "0.0000",
"available": "87.5000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T09:15:42+00:00"
}
}Списати з клієнта
Ваш застосунок знімає 5 кредитів за зроблене вивантаження. Ключ називає вивантаження, тож повтор більше нічого не знімає.
curl https://api.slen.ai/api/v1/customers/user_8421/charge \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Idempotency-Key: charge-user_8421-export-311" \
-H "Content-Type: application/json" \
-d '{
"credits": "5",
"note": "PDF export 311"
}'{
"data": {
"customer_account_id": "74439117526302720",
"customer_id": "74439117522108416",
"project_id": "74438200973594624",
"external_user_id": "user_8421",
"credit_type": "default",
"balance": "82.5000",
"reserved": "0.0000",
"available": "82.5000",
"allow_negative": false,
"low_balance_threshold": "10.0000",
"created_at": "2026-10-02T08:40:30+00:00",
"updated_at": "2026-10-02T09:20:05+00:00"
}
}Отримати список цін у кредитах
Проєкт має одну ціну.
curl https://api.slen.ai/api/v1/credit-prices \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": [
{
"credit_price_id": "74440031913676800",
"project_id": "74438200973594624",
"operation_code": "generate_email",
"credit_type": "default",
"credits": "2.0000",
"created_at": "2026-10-02T08:30:00+00:00",
"updated_at": "2026-10-02T08:30:00+00:00"
}
]
}Задати ціну в кредитах
Починаючи з наступного холду один виклик моделі в цій операції коштує клієнту два кредити.
curl -X PUT https://api.slen.ai/api/v1/credit-prices \
-H "Authorization: Bearer $SLEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation_code": "generate_email",
"credits": "2"
}'{
"data": {
"credit_price_id": "74440031913676800",
"project_id": "74438200973594624",
"operation_code": "generate_email",
"credit_type": "default",
"credits": "2.0000",
"created_at": "2026-10-02T08:30:00+00:00",
"updated_at": "2026-10-02T08:30:00+00:00"
}
}Що читати далі
Кредити клієнтів — ті самі клієнти в консолі.
Клієнти та кредити — ті самі операції з SDK.
Аналітика — кредити, списані з ваших клієнтів, проти того, скільки коштували їхні виклики.
Автентифікація — дозволи, які потрібні ключу, щоб читати й змінювати клієнтів.
Огляд — як кредити записуються в запиті й у відповіді.
Коди помилок — кожен код API: що він означає і що з ним робити.
Вебхуки — як приходить
customer.balance_low.
Зібрано з openapi b070e1f, sdk contract 5cc7055, php ae4a53b, node 84bc62d