Огляд
Vito API дозволяє вашому застосунку працювати з балансом Vito користувача прямо в Discord — читати його, списувати з нього або зараховувати назад. Користувач підтверджує кожне списання своїм PIN-кодом на vetox.io, перш ніж будь-які Vito перемістяться, а ваш застосунок отримує сповіщення про результат. Vito — це віртуальна валюта в межах екосистеми Vetox; використовуйте API, щоб створювати досвід на основі Vito — розблокування контенту, функцій, переваг або винагород. Жодної купівлі чи продажу за реальні гроші не відбувається.
Це REST API з автентифікацією за секретним ключем. Усі кінцеві точки повертають JSON і версіонуються під /v1.
https://api.vetox.io/public/vito/v1Баланси та розрахунки
Ваш API-ключ прив’язаний до вашого облікового запису Vetox. Кожне списання зараховується на вашу користь: коли ваш застосунок списує Vito в користувача, ці Vito зараховуються на ваш власний баланс (за вирахуванням комісії платформи) і ніколи не згоряють. Коли ваш застосунок нараховує Vito користувачу, це фінансується з вашого балансу.
Vito завжди переміщується між балансами — ніколи з реальних грошей або в них. З користувача стягується повна запитана вами сума; ви отримуєте суму за вирахуванням комісії платформи; зарахування та винагороди фінансуються з вашого балансу через /add.
Вимоги
Доступ до Vito API надається для кожного проєкту. Перш ніж викликати його, вам потрібно:
- Активне членство користувача (рівень Silver або вище). API автоматично заморожується, якщо членство власника закінчується, і відновлюється при повторній підписці.
- Схвалену заявку розробника. Подайте її з цієї сторінки; команда Vetox перевіряє її вручну.
- Прийнято умови для розробників API — підтверджуються під час подання заявки розробника.
- Області доступу, потрібні вашому проєкту. Їх надає команда Vetox на основі вашої заявки.
Доступні області
balance:readdeduct:createcredit:createtransfer:createtransactions:readАвтентифікація
Автентифікуйте кожен запит секретним ключем API в заголовку Authorization як токен Bearer. Ключі мають префікс vito_live_.
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxxДва необов’язкові рівні додатково захищають ваш проєкт:
- Список дозволених IP — обмежте запити конкретними IP сервера (налаштуйте на вкладці Налаштування).
- Ліміти запитів — стелі запитів на проєкт, що масштабуються з вашим рівнем членства.
Ключі API — зберігання, ротація та безпека
Ваш ключ створюється при схваленні заявки. Він показується лише один раз — розкрийте його на вкладці Ключі API та одразу збережіть.
Vetox ніколи не зберігає ваш ключ у читабельному вигляді (лише солений хеш). Зберігайте його як секрет на стороні сервера — змінну середовища або менеджер секретів — ніколи в клієнтському коді, git-репозиторії чи браузері.
Ротуйте його на вкладці Ключі API. Попередній ключ працює ще 24-годинний пільговий період, щоб ви розгорнули новий без простою, а потім перестає.
Процес підтвердження списання
Кожне списання проходить через підтвердження, яке користувач схвалює PIN-кодом — сам лише ваш ключ ніколи не переміщує Vito:
- 1Ваш застосунок викликає POST /deduct з користувачем, сумою, вихідним guildId і деталями елемента.
- 2Vito створює очікуване підтвердження (дійсне 10 хвилин) і повертає confirmUrl. Якщо вказано guildId, користувачу також надсилається ПП з деталями та кнопкою на сторінку підтвердження.
- 3Користувач відкриває confirmUrl на vetox.io і схвалює списання PIN-кодом свого гаманця.
- 4Vito списує Vito, записує транзакцію і — якщо у вас налаштований вебхук — надсилає підписану подію confirmation.completed на ваш URL зворотного виклику.
- 5Ваш застосунок перевіряє підпис і виконує свою дію (наприклад, розблоковує контент).
Параметри запиту (/deduct)
/v1/deductКінцева точка списання приймає такі поля тіла:
discordIdamountguildIdreasonmerchantRefproductproduct.imageUrlmetadata* обов’язкове поле.
POST https://api.vetox.io/public/vito/v1/deduct
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: req_9a1f2b # optional, dedupes retries for 24h
{
"discordId": "123456789012345678",
"amount": 500,
"guildId": "1470389021162209280",
"reason": "Unlock premium theme",
"merchantRef": "ref_1042",
"product": {
"type": "feature",
"name": "Premium theme",
"description": "Unlocks the premium theme pack",
"imageUrl": "https://cdn.example.com/theme.png"
},
"metadata": { "ref": "1042" }
}{
"success": true,
"data": {
"confirmationId": "f3c9...e1",
"confirmUrl": "https://vetox.io/confirm/vito/f3c9...e1",
"amount": 500,
"expiresAt": 1735689600000,
"status": "pending"
},
"requestId": "req_abc123",
"timestamp": 1735689300000
}Нарахування Vito — зарахування (/add)
/v1/addPOST /add зараховує Vito користувачу з вашого власного балансу — для винагород або зарахувань. Приймає ті самі поля, що й /deduct, окрім guildId та product. На відміну від списання, воно миттєве й не потребує PIN користувача — ваш API-ключ є вашим повноваженням — тож відповідь уже відображає завершений переказ. Користувач отримує повну суму; комісія платформи не застосовується.
POST https://api.vetox.io/public/vito/v1/add
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: credit_7f3a # optional, dedupes retries for 24h
{
"discordId": "123456789012345678",
"amount": 250,
"reason": "Event reward",
"merchantRef": "reward_1042",
"metadata": { "ref": "1042" }
}{
"success": true,
"data": {
"transactionId": "txn_91b2",
"toDiscordId": "123456789012345678",
"amount": 250,
"ownerBalance": 48250,
"recipientBalance": 1250,
"status": "completed"
},
"requestId": "req_def456",
"timestamp": 1735689300000
}Вебхуки та перевірка підпису
Додайте один або кілька https-URL зворотного виклику на вкладці Налаштування. Vito доставляє підписаний POST на кожен налаштований URL, коли підтвердження досягає кінцевого стану, повторюючи до 5 разів з експоненційною затримкою.
Перевірка підпису
Кожна доставка містить заголовок X-Vito-Signature вигляду ts=<unix>;h1=<hex>. Перерахуйте HMAC за "<ts>:<rawBody>" вашим секретом підпису та порівняйте за сталий час. Завжди перевіряйте за необробленим тілом запиту, до розбору JSON.
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>import crypto from 'crypto';
import express from 'express';
const app = express();
// IMPORTANT: verify against the RAW body, before JSON parsing.
app.post('/webhooks/vito', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.header('X-Vito-Signature') || ''; // "ts=<unix>;h1=<hmac>[;h1=<hmac>]"
const pairs = sig.split(';').map(p => p.split('='));
const ts = pairs.find(([k]) => k === 'ts')?.[1];
const sigs = pairs.filter(([k]) => k === 'h1').map(([, v]) => v);
const rawBody = req.body.toString('utf8');
const expected = crypto
.createHmac('sha256', process.env.VITO_SIGNING_SECRET) // whsec_...
.update(`${ts}:${rawBody}`)
.digest('hex');
const expectedBuf = Buffer.from(expected, 'hex');
// During a 24h secret rotation the header carries TWO h1= values — accept ANY.
// Length-guard FIRST: crypto.timingSafeEqual throws on a length mismatch.
const ok = sigs.some(h => {
const got = Buffer.from(h, 'hex');
return (
got.length === expectedBuf.length &&
crypto.timingSafeEqual(got, expectedBuf)
);
});
// Reject anything older than ~5 minutes to stop replays.
const fresh = ts && Math.abs(Date.now() / 1000 - Number(ts)) < 300;
if (!ok || !fresh) return res.status(401).end();
const event = JSON.parse(rawBody);
if (event.eventType === 'confirmation.completed') {
grantPremiumTheme(event.data.merchantRef, event.data.fromDiscordId);
}
res.status(200).end(); // ack fast; run your action async if slow
});Події вебхуків та корисні навантаження
Vito надсилає один із чотирьох типів подій. Усі вони мають однакову форму корисного навантаження; data.status відображає результат.
confirmation.completedconfirmation.failedconfirmation.expiredconfirmation.cancelledcredit.completedPOST https://your-app.com/webhooks/vito
X-Vito-Signature: ts=1735689600;h1=4f8b2c...e9
X-Vito-Event-Id: evt_3a2b1c
X-Vito-Event-Type: confirmation.completed
Content-Type: application/json
{
"eventId": "evt_3a2b1c",
"eventType": "confirmation.completed",
"timestamp": 1735689600000,
"data": {
"confirmationId": "f3c9...e1",
"type": "deduct",
"fromDiscordId": "123456789012345678",
"toDiscordId": null,
"amount": 500,
"reason": "Unlock premium theme",
"merchantRef": "ref_1042",
"product": { "type": "feature", "name": "Premium theme" },
"status": "completed",
"transactionId": "txn_77a2",
"metadata": { "ref": "1042" },
"failureReason": null
}
}Коди помилок
Помилки повертають статус, відмінний від 2xx, з тілом JSON, що містить error.code верхнього рівня. Поширені випадки:
VITO_VALIDATION_ERRORinvalid keyOWNER_MEMBERSHIP_INACTIVEscope / IPnot foundinsufficient balanceINSUFFICIENT_OWNER_FUNDSrate limited{
"success": false,
"error": { "type": "Bad Request", "message": "guildId must be a valid Discord server ID", "code": "VITO_VALIDATION_ERROR" },
"requestId": "req_abc123",
"timestamp": 1735689300000
}Приклади інтеграції
Створити запит на списання:
/v1/deductcurl -X POST https://api.vetox.io/public/vito/v1/deduct \
-H "Authorization: Bearer $VITO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"discordId": "123456789012345678",
"amount": 500,
"guildId": "1470389021162209280",
"merchantRef": "ref_1042",
"product": { "type": "feature", "name": "Premium theme" }
}'Перевірити баланс користувача:
/v1/balance/:discordIdcurl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
-H "Authorization: Bearer $VITO_API_KEY"Тестування з демонстраційною інтеграцією
Надається автономна демонстраційна інтеграція для відпрацювання повного процесу від початку до кінця без написання коду. Вона проходить запит → підтвердження → PIN → вебхук → завершення.
- 1Відкрийте папку демонстраційної інтеграції і додайте ваш ключ Vito API, токен бота Discord та ID тестового сервера (guild) до config.json.
- 2Виконайте npm install && npm start, потім відкрийте http://localhost:4000.
- 3Знайдіть користувача за Discord ID, виберіть елемент і почніть списання — демо викликає /deduct і показує confirmUrl.
- 4Підтвердьте PIN на vetox.io; демо виявляє завершення (через вебхук, якщо налаштовано, інакше опитуванням) і виконує демонстраційну дію.
Найкращі практики, ліміти та безпека
Безпека
- Тримайте ключ API та секрет підпису лише на стороні сервера; у разі витоку негайно ротуйте будь-який із них.
- Завжди перевіряйте підпис вебхука за необробленим тілом і відхиляйте доставки, старші за ~5 хвилин (захист від повторів).
- Виконуйте свою дію лише при confirmation.completed — ніколи за відповіддю /deduct (у цей момент списання ще не остаточне).
- Використовуйте список дозволених IP і запитуйте лише ті області доступу, які справді потрібні.
Ліміти
- Підтвердження закінчуються через 10 хвилин; вважайте непідтверджені запити покинутими.
- Ліміти запитів діють на проєкт і масштабуються з рівнем членства власника.
- amount має бути додатним цілим числом; metadata обмежено 10 ключами.