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.
https://api.vetox.io/public/vito/v1Saldos 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.
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:readdeduct:createcredit:createtransfer:createtransactions:readAutenticaçã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_.
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxxDuas 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.
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.
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:
- 1Seu app chama POST /deduct com o usuário, o valor, o guildId de origem e os detalhes do item.
- 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.
- 3O usuário abre a confirmUrl em vetox.io e aprova a cobrança com o PIN da carteira.
- 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.
- 5Seu app verifica a assinatura e conclui sua ação (por exemplo, desbloqueia o conteúdo).
Parâmetros da solicitação (/deduct)
/v1/deductO endpoint de dedução aceita os seguintes campos de corpo:
discordIdamountguildIdreasonmerchantRefproductproduct.imageUrlmetadata* campo obrigatório.
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
}Adicionar Vito — créditos (/add)
/v1/addPOST /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.
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
}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.
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.
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
});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.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
}
}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:
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
}Exemplos de integração
Criar uma solicitação de cobrança:
/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" }
}'Verificar o saldo de um usuário:
/v1/balance/:discordIdcurl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
-H "Authorization: Bearer $VITO_API_KEY"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.
- 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.
- 2Execute npm install && npm start e abra http://localhost:4000.
- 3Procure um usuário pelo ID do Discord, escolha um item e inicie uma cobrança — a demo chama /deduct e mostra a confirmUrl.
- 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.
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.