Documentação da Vito API

Última atualização: 26 de junho de 2026
01

Visão geral

A API Vito permite que seu aplicativo trabalhe com o saldo Vito de um usuário de dentro do Discord — lê-lo, cobrá-lo ou creditá-lo de volta. O usuário confirma cada cobrança com o PIN em vetox.io antes de qualquer Vito ser movido, e seu app é notificado do resultado. O Vito é a moeda virtual interna do ecossistema Vetox; use a API para criar experiências baseadas em Vito — desbloquear conteúdo, recursos, vantagens ou recompensas. Nenhuma compra ou venda com dinheiro real ocorre.

É uma API REST autenticada com uma chave secreta. Todos os endpoints retornam JSON e são versionados em /v1.

URL basehttps://api.vetox.io/public/vito/v1
02

Saldos e liquidação

A sua chave de API está vinculada à sua conta Vetox. Cada cobrança é liquidada a seu favor: quando o seu app deduz Vito de um usuário, esse Vito é creditado no seu próprio saldo (menos a taxa da plataforma), nunca destruído. Quando o seu app adiciona Vito a um usuário, é financiado a partir do seu saldo.

O Vito sempre se move entre saldos — nunca de ou para dinheiro real. O usuário é cobrado pelo valor total que você solicita; você recebe o valor após a taxa da plataforma; créditos e recompensas são financiados do seu saldo via /add.

Taxa da plataforma — a mesma tabela das transferências de Vito no app, conforme o seu nível de assinatura: 7% (Silver / Gold), 6% (Platinum), 5% (Diamond). Cobranças de 5 Vito ou menos não têm taxa. Créditos via /add nunca são cobrados.
03

Requisitos

O acesso à API Vito é concedido por projeto. Antes de poder chamá-la, você precisa de:

  • Uma assinatura de usuário ativa (nível Silver ou superior). A API congela automaticamente se a assinatura do proprietário expirar e é restaurada quando ele assina novamente.
  • Uma solicitação de desenvolvedor aprovada. Envie uma a partir desta página; a equipe Vetox a analisa manualmente.
  • Termos de Desenvolvedor da API aceitos — confirmados ao enviar sua candidatura de desenvolvedor.
  • Os escopos de que seu projeto precisa. São concedidos pela equipe Vetox com base na sua solicitação.

Escopos disponíveis

balance:read
Ler o saldo Vito de um usuário.
deduct:create
Criar uma dedução — cobrar o saldo Vito de um usuário.
credit:create
Adicionar Vito a um usuário — financiar recompensas ou créditos a partir do seu saldo.
transfer:create
Criar uma transferência entre dois usuários.
transactions:read
Listar e ler as transações do seu projeto.
04

Autenticação

Autentique cada solicitação com sua chave de API secreta no cabeçalho Authorization como token Bearer. As chaves têm o prefixo vito_live_.

http
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx

Duas camadas opcionais protegem ainda mais seu projeto:

  • Lista de IPs permitidos — restrinja as solicitações a IPs de servidor específicos (configure na aba Configurações).
  • Limites de taxa — tetos de solicitações por projeto que aumentam com o seu nível de assinatura.
05

Chaves de API — armazenamento, rotação e segurança

Sua chave é gerada quando sua solicitação é aprovada. Ela é mostrada apenas uma vez — revele-a na aba Chaves de API e guarde-a imediatamente.

O Vetox nunca armazena sua chave em forma legível (apenas um hash com sal). Guarde-a como um segredo do lado do servidor — uma variável de ambiente ou um gerenciador de segredos — nunca no código do cliente, em um repositório git ou em um navegador.

Rotacione-a na aba Chaves de API. A chave anterior continua funcionando por um período de carência de 24 horas para você implantar a nova sem tempo de inatividade, e então ela para.

Trate a chave como uma senha — qualquer um que a tenha pode cobrar dos seus usuários. Se vazar, rotacione imediatamente e revise suas transações recentes.
06

Fluxo de confirmação da cobrança

Toda cobrança passa por uma confirmação que o usuário aprova com o PIN — sua chave sozinha nunca move Vito:

  1. 1Seu app chama POST /deduct com o usuário, o valor, o guildId de origem e os detalhes do item.
  2. 2O Vito cria uma confirmação pendente (válida por 10 minutos) e retorna uma confirmUrl. Se um guildId for fornecido, o usuário também recebe uma DM com os detalhes e um botão para a página de confirmação.
  3. 3O usuário abre a confirmUrl em vetox.io e aprova a cobrança com o PIN da carteira.
  4. 4O Vito deduz os Vito, registra a transação e — se você tiver um webhook configurado — envia um evento confirmation.completed assinado para sua URL de callback.
  5. 5Seu app verifica a assinatura e conclui sua ação (por exemplo, desbloqueia o conteúdo).
Os PINs são inseridos apenas em vetox.io — nunca dentro do seu app ou do Discord. Se o usuário nunca confirmar, a solicitação expira após 10 minutos e você recebe um evento confirmation.expired.
07

Parâmetros da solicitação (/deduct)

POST/v1/deduct

O endpoint de dedução aceita os seguintes campos de corpo:

discordId
O ID de usuário do Discord do usuário — um snowflake de 17 a 20 dígitos. Obrigatório.
amount
Quantidade de Vito a cobrar — um inteiro positivo. Obrigatório.
guildId
O servidor do Discord de onde a cobrança se origina (snowflake). Obrigatório — toda cobrança deve vir de um servidor específico, o que também habilita a DM ao usuário.
reason
Uma breve descrição legível (≤ 256 caracteres), mostrada ao usuário e repetida no webhook.
merchantRef
Sua própria string de referência (≤ 128 caracteres). Use-a para associar o webhook aos seus registros.
product
Descritor do item — { type, name, description?, imageUrl? }. Mostrado ao usuário na página de confirmação e na DM, e repetido em cada webhook.
product.imageUrl
Imagem do produto opcional — deve ser uma URL https://.
metadata
Até 10 pares de chave/valor de texto, repassados literalmente no webhook.

* campo obrigatório.

Exemplo de solicitação
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" }
}
Exemplo de resposta
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

Adicionar Vito — créditos (/add)

POST/v1/add

POST /add credita Vito a um usuário a partir do seu próprio saldo — para recompensas ou créditos. Aceita os mesmos campos que /deduct, exceto guildId e product. Ao contrário de uma cobrança, é imediato e não requer o PIN do usuário — a sua chave de API é a sua autoridade — então a resposta já reflete a transferência concluída. O usuário recebe o valor total; nenhuma taxa de plataforma se aplica.

Requer o escopo credit:create e Vito suficiente no seu próprio saldo — caso contrário, a chamada retorna 402 INSUFFICIENT_OWNER_FUNDS.
Exemplo de solicitação
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" }
}
Exemplo de resposta
json
{
  "success": true,
  "data": {
    "transactionId": "txn_91b2",
    "toDiscordId": "123456789012345678",
    "amount": 250,
    "ownerBalance": 48250,
    "recipientBalance": 1250,
    "status": "completed"
  },
  "requestId": "req_def456",
  "timestamp": 1735689300000
}
09

Webhooks e verificação de assinatura

Adicione uma ou mais URLs de callback https na aba Configurações. O Vito entrega um POST assinado a cada URL configurada sempre que uma confirmação atinge um estado final, tentando novamente até 5 vezes com recuo exponencial.

Os webhooks só disparam quando seu projeto tem AO MESMO TEMPO uma URL de callback configurada E um segredo de assinatura. Revele seu segredo de assinatura (whsec_…) uma vez na aba Chaves de API.

Verificar a assinatura

Cada entrega traz um cabeçalho X-Vito-Signature na forma ts=<unix>;h1=<hex>. Recalcule o HMAC sobre "<ts>:<rawBody>" com seu segredo de assinatura e compare em tempo constante. Sempre verifique no corpo bruto da solicitação, antes de analisar o JSON.

http
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header    = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>
Verificador de referência (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

Eventos de webhook e payloads

O Vito envia um de quatro tipos de evento. Todos compartilham o mesmo formato de payload; data.status reflete o resultado.

confirmation.completed
O usuário confirmou e os Vito foram deduzidos. Conclua sua ação. Traz transactionId.
confirmation.failed
A cobrança não pôde ser concluída (por exemplo, saldo insuficiente ou um erro interno). Traz failureReason.
confirmation.expired
O usuário não confirmou em 10 minutos. Nenhum Vito movido.
confirmation.cancelled
O usuário cancelou a solicitação. Nenhum Vito movido.
credit.completed
Um crédito /add financiado pelo proprietário foi liquidado. Contém o destinatário e transactionId, e dispara imediatamente — não há etapa de confirmação.
Payload de exemplo (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

Códigos de erro

Os erros retornam um status diferente de 2xx com um corpo JSON que traz um error.code de nível superior. Casos comuns:

400VITO_VALIDATION_ERROR
Um campo da solicitação está ausente ou malformado (por exemplo, um guildId inválido).
401invalid key
Chave de API ausente, malformada ou desconhecida.
403OWNER_MEMBERSHIP_INACTIVE
A assinatura do proprietário do projeto expirou — a API está congelada até ele assinar novamente.
403scope / IP
A chave não tem o escopo necessário, ou o IP da solicitação não está na lista permitida.
404not found
O usuário, a transação ou o recurso não existe.
402insufficient balance
O usuário não tem Vito suficiente para a cobrança.
402INSUFFICIENT_OWNER_FUNDS
O seu próprio saldo (do proprietário) é baixo demais para financiar um crédito /add.
429rate limited
Você excedeu o limite de taxa do seu projeto — aguarde e tente novamente.
Formato da resposta de erro
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

Exemplos de integração

Criar uma solicitação de cobrança:

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" }
  }'

Verificar o saldo de um usuário:

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

Testando com a integração de demonstração

Uma integração de demonstração independente é fornecida para exercitar o fluxo completo de ponta a ponta sem escrever código. Ela percorre solicitação → confirmação → PIN → webhook → conclusão.

  1. 1Abra a pasta da integração de demonstração e adicione sua chave de API Vito, um token de bot do Discord e o ID do seu servidor (guild) de teste em config.json.
  2. 2Execute npm install && npm start e abra http://localhost:4000.
  3. 3Procure um usuário pelo ID do Discord, escolha um item e inicie uma cobrança — a demo chama /deduct e mostra a confirmUrl.
  4. 4Aprove o PIN em vetox.io; a demo detecta a conclusão (por webhook se configurado, senão por polling) e conclui a ação de exemplo.
Receber o webhook exige um callback https público e um segredo de assinatura; sem eles, a demo recorre ao polling das suas transações, então funciona sem configuração.
14

Boas práticas, limites e segurança

Segurança

  • Mantenha sua chave de API e o segredo de assinatura apenas no servidor; rotacione qualquer um deles imediatamente se vazar.
  • Sempre verifique a assinatura do webhook no corpo bruto e rejeite entregas com mais de ~5 minutos (proteção contra repetição).
  • Conclua sua ação apenas em confirmation.completed — nunca na resposta de /deduct (nesse ponto a cobrança não é definitiva).
  • Use a lista de IPs permitidos e solicite apenas os escopos de que realmente precisa.

Limites

  • As confirmações expiram após 10 minutos; trate as solicitações não confirmadas como abandonadas.
  • Os limites de taxa são por projeto e aumentam com o nível de assinatura do proprietário.
  • amount deve ser um inteiro positivo; metadata é limitado a 10 chaves.
Idempotency-Key elimina retentativas duplicadasWebhooks tentam 5× com recuoResponda 2xx rápido, atenda de forma assíncrona