Документация Vito API

Последнее обновление: 26 июня 2026 г.
01

Обзор

Vito API позволяет вашему приложению работать с балансом Vito пользователя прямо внутри Discord — читать его, списывать с него или зачислять обратно. Пользователь подтверждает каждое списание своим PIN-кодом на vetox.io, прежде чем какие-либо Vito будут перемещены, а ваше приложение уведомляется о результате. Vito — это виртуальная валюта внутри экосистемы Vetox; используйте API, чтобы создавать опыт на основе Vito — разблокировку контента, функций, преимуществ или вознаграждений. Никакой покупки или продажи за реальные деньги не происходит.

Это REST API с аутентификацией по секретному ключу. Все эндпоинты возвращают JSON и версионируются под /v1.

Базовый URLhttps://api.vetox.io/public/vito/v1
02

Балансы и расчёты

Ваш API-ключ привязан к вашему аккаунту Vetox. Каждое списание зачисляется в вашу пользу: когда ваше приложение списывает Vito у пользователя, эти Vito зачисляются на ваш собственный баланс (за вычетом комиссии платформы) и никогда не сгорают. Когда ваше приложение начисляет Vito пользователю, это финансируется с вашего баланса.

Vito всегда перемещается между балансами — никогда из реальных денег или в них. С пользователя взимается полная запрошенная вами сумма; вы получаете сумму за вычетом комиссии платформы; зачисления и вознаграждения финансируются с вашего баланса через /add.

Комиссия платформы — та же шкала, что и у внутренних переводов Vito, в зависимости от вашего уровня членства: 7% (Silver / Gold), 6% (Platinum), 5% (Diamond). Списания на 5 Vito и менее без комиссии. Зачисления через /add никогда не облагаются комиссией.
03

Требования

Доступ к Vito API предоставляется для каждого проекта. Прежде чем вызывать его, вам нужно:

  • Активное членство пользователя (уровень Silver или выше). API автоматически замораживается, если членство владельца истекает, и восстанавливается при повторной подписке.
  • Одобренная заявка разработчика. Подайте её с этой страницы; команда Vetox проверяет её вручную.
  • Приняты условия для разработчиков API — подтверждаются при отправке заявки разработчика.
  • Области доступа, которые нужны вашему проекту. Их предоставляет команда Vetox на основе вашей заявки.

Доступные области

balance:read
Чтение баланса Vito пользователя.
deduct:create
Создание списания — взимание платы с баланса Vito пользователя.
credit:create
Начислить Vito пользователю — финансировать вознаграждения или зачисления со своего баланса.
transfer:create
Создание перевода между двумя пользователями.
transactions:read
Просмотр и чтение транзакций вашего проекта.
04

Аутентификация

Аутентифицируйте каждый запрос секретным ключом API в заголовке Authorization как токен Bearer. Ключи имеют префикс vito_live_.

http
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx

Два необязательных уровня дополнительно защищают ваш проект:

  • Список разрешённых IP — ограничьте запросы конкретными IP сервера (настройте на вкладке Настройки).
  • Лимиты запросов — потолки запросов на проект, которые растут вместе с уровнем членства.
05

Ключи API — хранение, ротация и безопасность

Ваш ключ создаётся при одобрении заявки. Он показывается только один раз — раскройте его на вкладке Ключи API и сразу сохраните.

Vetox никогда не хранит ваш ключ в читаемом виде (только солёный хеш). Храните его как секрет на стороне сервера — переменную окружения или менеджер секретов — никогда в клиентском коде, git-репозитории или браузере.

Ротируйте его на вкладке Ключи API. Прежний ключ работает ещё 24-часовой льготный период, чтобы вы развернули новый без простоя, а затем перестаёт.

Относитесь к ключу как к паролю — любой, у кого он есть, может взимать плату с ваших пользователей. При утечке немедленно ротируйте и проверьте недавние транзакции.
06

Процесс подтверждения списания

Каждое списание проходит через подтверждение, которое пользователь одобряет PIN-кодом — один ваш ключ никогда не перемещает Vito:

  1. 1Ваше приложение вызывает POST /deduct с пользователем, суммой, исходным guildId и данными об элементе.
  2. 2Vito создаёт ожидающее подтверждение (действительно 10 минут) и возвращает confirmUrl. Если указан guildId, пользователю также отправляется ЛС с данными и кнопкой на страницу подтверждения.
  3. 3Пользователь открывает confirmUrl на vetox.io и одобряет списание PIN-кодом своего кошелька.
  4. 4Vito списывает Vito, записывает транзакцию и — если у вас настроен вебхук — отправляет подписанное событие confirmation.completed на ваш URL обратного вызова.
  5. 5Ваше приложение проверяет подпись и выполняет своё действие (например, разблокирует контент).
PIN-коды вводятся только на vetox.io — никогда в вашем приложении или в Discord. Если пользователь не подтвердит, запрос истекает через 10 минут, и вы получаете событие confirmation.expired.
07

Параметры запроса (/deduct)

POST/v1/deduct

Эндпоинт списания принимает следующие поля тела:

discordId
Discord-идентификатор пользователя — snowflake из 17–20 цифр. Обязательно.
amount
Сумма Vito для списания — положительное целое число. Обязательно.
guildId
Discord-сервер, с которого происходит списание (snowflake). Обязательно — каждое списание должно исходить с конкретного сервера, что также включает ЛС пользователю.
reason
Краткое читаемое описание (≤ 256 символов), показываемое пользователю и повторяемое в вебхуке.
merchantRef
Ваша собственная строка-ссылка (≤ 128 символов). Используйте её, чтобы сопоставить вебхук с вашими записями.
product
Дескриптор элемента — { type, name, description?, imageUrl? }. Показывается пользователю на странице подтверждения и в ЛС и повторяется в каждом вебхуке.
product.imageUrl
Необязательное изображение продукта — должно быть URL https://.
metadata
До 10 строковых пар ключ/значение, передаваемых без изменений в вебхуке.

* обязательное поле.

Пример запроса
http
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" }
}
Пример ответа
json
{
  "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
}
08

Начисление Vito — зачисления (/add)

POST/v1/add

POST /add зачисляет Vito пользователю с вашего собственного баланса — для вознаграждений или зачислений. Принимает те же поля, что и /deduct, кроме guildId и product. В отличие от списания, оно мгновенное и не требует PIN пользователя — ваш API-ключ и есть ваше полномочие — поэтому ответ уже отражает завершённый перевод. Пользователь получает полную сумму; комиссия платформы не взимается.

Требует область credit:create и достаточный баланс Vito у вас — иначе вызов возвращает 402 INSUFFICIENT_OWNER_FUNDS.
Пример запроса
http
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" }
}
Пример ответа
json
{
  "success": true,
  "data": {
    "transactionId": "txn_91b2",
    "toDiscordId": "123456789012345678",
    "amount": 250,
    "ownerBalance": 48250,
    "recipientBalance": 1250,
    "status": "completed"
  },
  "requestId": "req_def456",
  "timestamp": 1735689300000
}
09

Вебхуки и проверка подписи

Добавьте один или несколько https-URL обратного вызова на вкладке Настройки. Vito доставляет подписанный POST на каждый настроенный URL, когда подтверждение достигает конечного состояния, повторяя до 5 раз с экспоненциальной задержкой.

Вебхуки срабатывают только когда у вашего проекта есть ОДНОВРЕМЕННО настроенный URL обратного вызова И секрет подписи. Раскройте секрет подписи (whsec_…) один раз на вкладке Ключи API.

Проверка подписи

Каждая доставка содержит заголовок X-Vito-Signature вида ts=<unix>;h1=<hex>. Пересчитайте HMAC по "<ts>:<rawBody>" вашим секретом подписи и сравните за постоянное время. Всегда проверяйте по необработанному телу запроса, до разбора JSON.

http
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header    = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>
Эталонный проверяющий (Node / Express):
javascript
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
});
10

События вебхуков и полезные нагрузки

Vito отправляет один из четырёх типов событий. Все они имеют одинаковую форму полезной нагрузки; data.status отражает результат.

confirmation.completed
Пользователь подтвердил, и Vito списано. Выполните своё действие. Содержит transactionId.
confirmation.failed
Списание не удалось завершить (например, недостаточно баланса или внутренняя ошибка). Содержит failureReason.
confirmation.expired
Пользователь не подтвердил в течение 10 минут. Vito не перемещены.
confirmation.cancelled
Пользователь отменил запрос. Vito не перемещены.
credit.completed
Начисление /add, профинансированное владельцем, рассчитано. Содержит получателя и transactionId и срабатывает немедленно — шага подтверждения нет.
Пример полезной нагрузки (confirmation.completed)
http
POST 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
  }
}
11

Коды ошибок

Ошибки возвращают статус, отличный от 2xx, с телом JSON, содержащим error.code верхнего уровня. Частые случаи:

400VITO_VALIDATION_ERROR
Поле запроса отсутствует или некорректно (например, неверный guildId).
401invalid key
Отсутствующий, некорректный или неизвестный ключ API.
403OWNER_MEMBERSHIP_INACTIVE
Членство владельца проекта истекло — API заморожен до повторной подписки.
403scope / IP
У ключа нет нужной области доступа, или IP запроса не в списке разрешённых.
404not found
Пользователь, транзакция или ресурс не существует.
402insufficient balance
У пользователя недостаточно Vito для списания.
402INSUFFICIENT_OWNER_FUNDS
Вашего собственного баланса (владельца) слишком мало для финансирования начисления /add.
429rate limited
Вы превысили лимит запросов проекта — подождите и повторите.
Форма ответа об ошибке
json
{
  "success": false,
  "error": { "type": "Bad Request", "message": "guildId must be a valid Discord server ID", "code": "VITO_VALIDATION_ERROR" },
  "requestId": "req_abc123",
  "timestamp": 1735689300000
}
12

Примеры интеграции

Создать запрос на списание:

POST/v1/deduct
bash
curl -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" }
  }'

Проверить баланс пользователя:

GET/v1/balance/:discordId
bash
curl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
  -H "Authorization: Bearer $VITO_API_KEY"
13

Тестирование с демо-интеграцией

Предоставляется автономная демо-интеграция для отработки полного процесса от начала до конца без написания кода. Она проходит запрос → подтверждение → PIN → вебхук → завершение.

  1. 1Откройте папку демо-интеграции и добавьте ваш ключ Vito API, токен бота Discord и ID тестового сервера (guild) в config.json.
  2. 2Выполните npm install && npm start, затем откройте http://localhost:4000.
  3. 3Найдите пользователя по Discord ID, выберите элемент и начните списание — демо вызывает /deduct и показывает confirmUrl.
  4. 4Одобрите PIN на vetox.io; демо обнаруживает завершение (через вебхук, если настроен, иначе опросом) и выполняет демонстрационное действие.
Для приёма вебхука нужны публичный https-callback и секрет подписи; без них демо переходит к опросу ваших транзакций, поэтому работает без настройки.
14

Лучшие практики, лимиты и безопасность

Безопасность

  • Храните ключ API и секрет подписи только на стороне сервера; при утечке немедленно ротируйте любой из них.
  • Всегда проверяйте подпись вебхука по необработанному телу и отклоняйте доставки старше ~5 минут (защита от повторов).
  • Выполняйте своё действие только при confirmation.completed — никогда по ответу /deduct (в этот момент списание ещё не окончательно).
  • Используйте список разрешённых IP и запрашивайте только действительно нужные области доступа.

Лимиты

  • Подтверждения истекают через 10 минут; считайте неподтверждённые запросы брошенными.
  • Лимиты запросов действуют на проект и растут с уровнем членства владельца.
  • amount должно быть положительным целым числом; metadata ограничено 10 ключами.
Idempotency-Key устраняет дубли повторовВебхуки повторяют 5× с задержкойБыстро отвечайте 2xx, выполняйте асинхронно