Documentazione Vito API

Ultimo aggiornamento: 26 giugno 2026
01

Panoramica

L’API Vito consente alla tua applicazione di lavorare con il saldo Vito di un utente da dentro Discord — leggerlo, addebitarlo o riaccreditarlo. L’utente conferma ogni addebito con il proprio PIN su vetox.io prima che venga mosso qualsiasi Vito, e la tua app viene notificata dell’esito. Vito è la valuta virtuale interna all’ecosistema Vetox; usa l’API per creare esperienze basate su Vito — sbloccare contenuti, funzionalità, vantaggi o premi. Non avviene alcun acquisto o vendita con denaro reale.

È un’API REST autenticata con una chiave segreta. Tutti gli endpoint restituiscono JSON e sono versionati sotto /v1.

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

Saldi e liquidazione

La tua chiave API è legata al tuo account Vetox. Ogni addebito viene liquidato a tuo favore: quando la tua app deduce Vito da un utente, quei Vito vengono accreditati sul tuo saldo (al netto della commissione della piattaforma), mai distrutti. Quando la tua app aggiunge Vito a un utente, sono finanziati dal tuo saldo.

I Vito si muovono sempre tra saldi — mai da o verso denaro reale. All'utente viene addebitato l'intero importo richiesto; tu ricevi l'importo al netto della commissione della piattaforma; accrediti e premi sono finanziati dal tuo saldo tramite /add.

Commissione della piattaforma — la stessa tabella dei trasferimenti Vito in-app, in base al tuo livello di abbonamento: 7% (Silver / Gold), 6% (Platinum), 5% (Diamond). Gli addebiti di 5 Vito o meno sono senza commissione. Gli accrediti tramite /add non sono mai soggetti a commissione.
03

Requisiti

L’accesso all’API Vito è concesso per progetto. Prima di poterla chiamare ti serve:

  • Un abbonamento utente attivo (livello Silver o superiore). L’API si congela automaticamente se l’abbonamento del proprietario scade e si ripristina quando si riabbona.
  • Una domanda da sviluppatore approvata. Inviane una da questa pagina; il team Vetox la esamina manualmente.
  • Termini per sviluppatori API accettati — confermati quando invii la tua domanda da sviluppatore.
  • Gli ambiti di cui il tuo progetto ha bisogno. Sono concessi dal team Vetox in base alla tua domanda.

Ambiti disponibili

balance:read
Leggere il saldo Vito di un utente.
deduct:create
Creare una detrazione — addebitare il saldo Vito di un utente.
credit:create
Aggiungere Vito a un utente — finanziare premi o accrediti dal tuo saldo.
transfer:create
Creare un trasferimento tra due utenti.
transactions:read
Elencare e leggere le transazioni del tuo progetto.
04

Autenticazione

Autentica ogni richiesta con la tua chiave API segreta nell’header Authorization come token Bearer. Le chiavi hanno il prefisso vito_live_.

http
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx

Due livelli opzionali proteggono ulteriormente il tuo progetto:

  • Lista IP consentiti — limita le richieste a IP di server specifici (configurala nella scheda Impostazioni).
  • Limiti di frequenza — tetti di richieste per progetto che crescono con il tuo livello di abbonamento.
05

Chiavi API — archiviazione, rotazione e sicurezza

La tua chiave viene generata all’approvazione della domanda. Viene mostrata una sola volta — rivelala dalla scheda Chiavi API e conservala immediatamente.

Vetox non memorizza mai la tua chiave in forma leggibile (solo un hash con sale). Conservala come segreto lato server — una variabile d’ambiente o un gestore di segreti — mai nel codice client, in un repository git o in un browser.

Ruotala dalla scheda Chiavi API. La chiave precedente continua a funzionare per un periodo di tolleranza di 24 ore così da distribuire la nuova senza interruzioni, poi si disattiva.

Tratta la chiave come una password — chiunque la possieda può addebitare i tuoi utenti. In caso di fuga, ruotala subito e controlla le transazioni recenti.
06

Flusso di conferma dell’addebito

Ogni addebito passa da una conferma che l’utente approva con il proprio PIN — la tua chiave da sola non muove mai Vito:

  1. 1La tua app chiama POST /deduct con l’utente, l’importo, il guildId di origine e i dettagli dell’articolo.
  2. 2Vito crea una conferma in attesa (valida 10 minuti) e restituisce una confirmUrl. Se viene fornito un guildId, l’utente riceve anche un DM con i dettagli e un pulsante verso la pagina di conferma.
  3. 3L’utente apre la confirmUrl su vetox.io e approva l’addebito con il PIN del proprio portafoglio.
  4. 4Vito detrae i Vito, registra la transazione e — se hai un webhook configurato — invia un evento confirmation.completed firmato al tuo URL di callback.
  5. 5La tua app verifica la firma e completa la sua azione (ad esempio, sblocca il contenuto).
I PIN si inseriscono solo su vetox.io — mai nella tua app o in Discord. Se l’utente non conferma, la richiesta scade dopo 10 minuti e ricevi un evento confirmation.expired.
07

Parametri della richiesta (/deduct)

POST/v1/deduct

L’endpoint di detrazione accetta i seguenti campi del body:

discordId
L’ID utente Discord dell’utente — uno snowflake di 17–20 cifre. Obbligatorio.
amount
Quantità di Vito da addebitare — un intero positivo. Obbligatorio.
guildId
Il server Discord da cui ha origine l’addebito (snowflake). Obbligatorio — ogni addebito deve provenire da un server specifico, il che abilita anche il DM all’utente.
reason
Una breve descrizione leggibile (≤ 256 caratteri), mostrata all’utente e ripetuta nel webhook.
merchantRef
La tua stringa di riferimento (≤ 128 caratteri). Usala per associare il webhook ai tuoi registri.
product
Descrittore dell’articolo — { type, name, description?, imageUrl? }. Mostrato all’utente nella pagina di conferma e nel DM, e ripetuto in ogni webhook.
product.imageUrl
Immagine del prodotto facoltativa — deve essere un URL https://.
metadata
Fino a 10 coppie chiave/valore di testo, trasmesse invariate nel webhook.

* campo obbligatorio.

Richiesta di esempio
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" }
}
Risposta di esempio
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

Aggiungere Vito — accrediti (/add)

POST/v1/add

POST /add accredita Vito a un utente dal tuo saldo — per premi o accrediti. Accetta gli stessi campi di /deduct tranne guildId e product. A differenza di un addebito è immediato e non richiede il PIN dell'utente — la tua chiave API è la tua autorità — quindi la risposta riflette già il trasferimento completato. L'utente riceve l'intero importo; non si applica alcuna commissione di piattaforma.

Richiede l'ambito credit:create e Vito sufficienti sul tuo saldo — altrimenti la chiamata restituisce 402 INSUFFICIENT_OWNER_FUNDS.
Richiesta di esempio
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" }
}
Risposta di esempio
json
{
  "success": true,
  "data": {
    "transactionId": "txn_91b2",
    "toDiscordId": "123456789012345678",
    "amount": 250,
    "ownerBalance": 48250,
    "recipientBalance": 1250,
    "status": "completed"
  },
  "requestId": "req_def456",
  "timestamp": 1735689300000
}
09

Webhook e verifica della firma

Aggiungi uno o più URL di callback https nella scheda Impostazioni. Vito consegna un POST firmato a ogni URL configurato ogni volta che una conferma raggiunge uno stato finale, riprovando fino a 5 volte con backoff esponenziale.

I webhook si attivano solo quando il tuo progetto ha sia un URL di callback configurato SIA un segreto di firma. Rivela il tuo segreto di firma (whsec_…) una volta dalla scheda Chiavi API.

Verificare la firma

Ogni consegna porta un header X-Vito-Signature nella forma ts=<unix>;h1=<hex>. Ricalcola l’HMAC su "<ts>:<rawBody>" con il tuo segreto di firma e confronta a tempo costante. Verifica sempre sul body grezzo della richiesta, prima del parsing JSON.

http
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header    = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>
Verificatore di riferimento (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

Eventi webhook e payload

Vito invia uno di quattro tipi di evento. Condividono tutti la stessa forma di payload; data.status riflette l’esito.

confirmation.completed
L’utente ha confermato e i Vito sono stati detratti. Completa la tua azione. Porta transactionId.
confirmation.failed
Non è stato possibile completare l’addebito (ad esempio, saldo insufficiente o un errore interno). Porta failureReason.
confirmation.expired
L’utente non ha confermato entro 10 minuti. Nessun Vito mosso.
confirmation.cancelled
L’utente ha annullato la richiesta. Nessun Vito mosso.
credit.completed
Un accredito /add finanziato dal proprietario è stato liquidato. Contiene il destinatario e transactionId e viene attivato immediatamente — non c’è alcun passaggio di conferma.
Payload di esempio (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

Codici di errore

Gli errori restituiscono uno stato non 2xx con un body JSON che porta un error.code di primo livello. Casi comuni:

400VITO_VALIDATION_ERROR
Un campo della richiesta è mancante o malformato (ad esempio, un guildId non valido).
401invalid key
Chiave API mancante, malformata o sconosciuta.
403OWNER_MEMBERSHIP_INACTIVE
L’abbonamento del proprietario del progetto è scaduto — l’API è congelata finché non si riabbona.
403scope / IP
Alla chiave manca l’ambito richiesto, oppure l’IP della richiesta non è nella lista consentita.
404not found
L’utente, la transazione o la risorsa non esiste.
402insufficient balance
L’utente non ha abbastanza Vito per l’addebito.
402INSUFFICIENT_OWNER_FUNDS
Il tuo saldo (del proprietario) è troppo basso per finanziare un accredito /add.
429rate limited
Hai superato il limite di frequenza del tuo progetto — attendi e riprova.
Forma della risposta di errore
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

Esempi di integrazione

Creare una richiesta di addebito:

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

Controllare il saldo di un utente:

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

Test con l’integrazione dimostrativa

Viene fornita un’integrazione dimostrativa autonoma per esercitare l’intero flusso end-to-end senza scrivere codice. Percorre richiesta → conferma → PIN → webhook → completamento.

  1. 1Apri la cartella dell’integrazione dimostrativa e aggiungi la tua chiave API Vito, un token bot Discord e l’ID del tuo server (guild) di test in config.json.
  2. 2Esegui npm install && npm start, poi apri http://localhost:4000.
  3. 3Cerca un utente per ID Discord, scegli un articolo e avvia un addebito — la demo chiama /deduct e mostra la confirmUrl.
  4. 4Approva il PIN su vetox.io; la demo rileva il completamento (tramite webhook se configurato, altrimenti con polling) e completa l’azione di esempio.
Ricevere il webhook richiede un callback https pubblico e un segreto di firma; senza di essi la demo ripiega sul polling delle tue transazioni, quindi funziona senza configurazione.
14

Best practice, limiti e sicurezza

Sicurezza

  • Mantieni la chiave API e il segreto di firma solo lato server; ruota subito l’uno o l’altro in caso di fuga.
  • Verifica sempre la firma del webhook sul body grezzo e rifiuta le consegne più vecchie di ~5 minuti (protezione dai replay).
  • Completa la tua azione solo su confirmation.completed — mai sulla risposta di /deduct (a quel punto l’addebito non è definitivo).
  • Usa la lista IP consentiti e richiedi solo gli ambiti che ti servono davvero.

Limiti

  • Le conferme scadono dopo 10 minuti; tratta le richieste non confermate come abbandonate.
  • I limiti di frequenza sono per progetto e crescono con il livello di abbonamento del proprietario.
  • amount deve essere un intero positivo; metadata è limitato a 10 chiavi.
Idempotency-Key deduplica i ritenttiviI webhook riprovano 5× con backoffRispondi 2xx subito, evadi in asincrono