Documentation de l'API Vito

Dernière mise à jour : 26 juin 2026
01

Aperçu

L’API Vito permet à votre application de travailler avec le solde Vito d’un utilisateur depuis Discord — le lire, le débiter ou le recréditer. L’utilisateur confirme chaque débit avec son code PIN sur vetox.io avant que le moindre Vito ne bouge, et votre app est notifiée du résultat. Vito est la monnaie virtuelle interne de l’écosystème Vetox ; utilisez l’API pour créer des expériences basées sur Vito — débloquer du contenu, des fonctionnalités, des avantages ou des récompenses. Aucun achat ni vente avec de l’argent réel n’a lieu.

C’est une API REST authentifiée par une clé secrète. Tous les endpoints renvoient du JSON et sont versionnés sous /v1.

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

Soldes et règlement

Votre clé API est liée à votre compte Vetox. Chaque débit est réglé à votre profit : lorsque votre application déduit des Vito à un utilisateur, ces Vito sont crédités sur votre propre solde (déduction faite des frais de plateforme), jamais détruits. Lorsque votre application ajoute des Vito à un utilisateur, ils sont financés depuis votre solde.

Les Vito circulent toujours entre les soldes — jamais depuis ou vers de l’argent réel. L’utilisateur est débité du montant total que vous demandez ; vous recevez le montant après les frais de plateforme ; les crédits et récompenses sont financés depuis votre solde via /add.

Frais de plateforme — le même barème que les transferts Vito intégrés, selon votre niveau d’adhésion : 7 % (Silver / Gold), 6 % (Platinum), 5 % (Diamond). Les débits de 5 Vito ou moins sont sans frais. Les crédits via /add ne sont jamais facturés.
03

Prérequis

L’accès à l’API Vito est accordé par projet. Avant de pouvoir l’appeler, il vous faut :

  • Un abonnement utilisateur actif (niveau Silver ou supérieur). L’API se gèle automatiquement si l’abonnement du propriétaire expire, et se rétablit lorsqu’il se réabonne.
  • Une demande de développeur approuvée. Soumettez-en une depuis cette page ; l’équipe Vetox l’examine manuellement.
  • Conditions développeur de l'API acceptées — validées lors de l'envoi de votre demande de développeur.
  • Les portées dont votre projet a besoin. Elles sont accordées par l’équipe Vetox selon votre demande.

Portées disponibles

balance:read
Lire le solde Vito d’un utilisateur.
deduct:create
Créer une déduction — débiter le solde Vito d’un utilisateur.
credit:create
Ajouter des Vito à un utilisateur — financer des récompenses ou des crédits depuis votre solde.
transfer:create
Créer un transfert entre deux utilisateurs.
transactions:read
Lister et lire les transactions de votre projet.
04

Authentification

Authentifiez chaque requête avec votre clé API secrète dans l’en-tête Authorization sous forme de jeton Bearer. Les clés portent le préfixe vito_live_.

http
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx

Deux couches facultatives protègent davantage votre projet :

  • Liste d’IP autorisées — restreignez les requêtes à des IP de serveur précises (configurez-la dans l’onglet Paramètres).
  • Limites de débit — plafonds de requêtes par projet qui évoluent selon votre niveau d’abonnement.
05

Clés API — stockage, rotation et sécurité

Votre clé est générée à l’approbation de votre demande. Elle n’est affichée qu’une seule fois — révélez-la depuis l’onglet Clés API et stockez-la immédiatement.

Vetox ne stocke jamais votre clé sous forme lisible (seulement un hachage salé). Conservez-la comme un secret côté serveur — une variable d’environnement ou un gestionnaire de secrets — jamais dans le code client, un dépôt git ou un navigateur.

Faites-la tourner depuis l’onglet Clés API. L’ancienne clé continue de fonctionner pendant une période de grâce de 24 heures pour déployer la nouvelle sans interruption, puis elle s’arrête.

Traitez la clé comme un mot de passe — quiconque la détient peut débiter vos utilisateurs. En cas de fuite, faites-la tourner immédiatement et passez en revue vos transactions récentes.
06

Parcours de confirmation du débit

Chaque débit passe par une confirmation que l’utilisateur approuve avec son code PIN — votre clé seule ne déplace jamais de Vito :

  1. 1Votre app appelle POST /deduct avec l’utilisateur, le montant, le guildId d’origine et les détails de l’élément.
  2. 2Vito crée une confirmation en attente (valable 10 minutes) et renvoie une confirmUrl. Si un guildId est fourni, l’utilisateur reçoit aussi un DM avec les détails et un bouton vers la page de confirmation.
  3. 3L’utilisateur ouvre la confirmUrl sur vetox.io et approuve le débit avec le PIN de son portefeuille.
  4. 4Vito déduit les Vito, enregistre la transaction et — si un webhook est configuré — envoie un événement confirmation.completed signé à votre URL de callback.
  5. 5Votre app vérifie la signature et effectue son action (par exemple, débloque le contenu).
Les codes PIN sont saisis uniquement sur vetox.io — jamais dans votre app ni dans Discord. Si l’utilisateur ne confirme pas, la demande expire après 10 minutes et vous recevez un événement confirmation.expired.
07

Paramètres de la requête (/deduct)

POST/v1/deduct

L’endpoint de déduction accepte les champs de corps suivants :

discordId
L’ID utilisateur Discord de l’utilisateur — un snowflake de 17 à 20 chiffres. Obligatoire.
amount
Montant de Vito à débiter — un entier positif. Obligatoire.
guildId
Le serveur Discord d’où provient le débit (snowflake). Obligatoire — chaque débit doit provenir d’un serveur précis, ce qui active aussi le DM à l’utilisateur.
reason
Une courte description lisible (≤ 256 caractères), montrée à l’utilisateur et répétée dans le webhook.
merchantRef
Votre propre chaîne de référence (≤ 128 caractères). Utilisez-la pour relier le webhook à vos enregistrements.
product
Descripteur de l’élément — { type, name, description?, imageUrl? }. Montré à l’utilisateur sur la page de confirmation et le DM, et répété dans chaque webhook.
product.imageUrl
Image de produit facultative — doit être une URL https://.
metadata
Jusqu’à 10 paires clé/valeur textuelles, transmises telles quelles dans le webhook.

* champ obligatoire.

Exemple de requête
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" }
}
Exemple de réponse
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

Ajouter des Vito — crédits (/add)

POST/v1/add

POST /add crédite un utilisateur de Vito depuis votre propre solde — pour des récompenses ou des crédits. Il prend les mêmes champs que /deduct sauf guildId et product. Contrairement à un débit, il est immédiat et ne nécessite pas le PIN de l’utilisateur — votre clé API est votre autorité — la réponse reflète donc déjà le transfert effectué. L’utilisateur reçoit le montant total ; aucuns frais de plateforme ne s’appliquent.

Nécessite la portée credit:create et suffisamment de Vito sur votre propre solde — sinon l’appel renvoie 402 INSUFFICIENT_OWNER_FUNDS.
Exemple de requête
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" }
}
Exemple de réponse
json
{
  "success": true,
  "data": {
    "transactionId": "txn_91b2",
    "toDiscordId": "123456789012345678",
    "amount": 250,
    "ownerBalance": 48250,
    "recipientBalance": 1250,
    "status": "completed"
  },
  "requestId": "req_def456",
  "timestamp": 1735689300000
}
09

Webhooks et vérification de signature

Ajoutez une ou plusieurs URLs de callback https dans l’onglet Paramètres. Vito livre un POST signé à chaque URL configurée dès qu’une confirmation atteint un état final, avec jusqu’à 5 tentatives en repli exponentiel.

Les webhooks ne se déclenchent que lorsque votre projet a à la fois une URL de callback configurée ET un secret de signature. Révélez votre secret de signature (whsec_…) une fois depuis l’onglet Clés API.

Vérifier la signature

Chaque livraison porte un en-tête X-Vito-Signature de la forme ts=<unix>;h1=<hex>. Recalculez le HMAC sur "<ts>:<rawBody>" avec votre secret de signature et comparez en temps constant. Vérifiez toujours le corps brut de la requête, avant l’analyse JSON.

http
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header    = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>
Vérificateur de référence (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

Événements de webhook et charges utiles

Vito envoie l’un des quatre types d’événement. Ils partagent tous la même forme de charge utile ; data.status reflète le résultat.

confirmation.completed
L’utilisateur a confirmé et les Vito ont été déduits. Effectuez votre action. Porte transactionId.
confirmation.failed
Le débit n’a pas pu être effectué (par exemple, solde insuffisant ou erreur interne). Porte failureReason.
confirmation.expired
L’utilisateur n’a pas confirmé en 10 minutes. Aucun Vito déplacé.
confirmation.cancelled
L’utilisateur a annulé la demande. Aucun Vito déplacé.
credit.completed
Un crédit /add financé par le propriétaire a été réglé. Contient le destinataire et transactionId, et se déclenche immédiatement — il n’y a pas d’étape de confirmation.
Exemple de charge utile (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

Codes d’erreur

Les erreurs renvoient un statut non 2xx avec un corps JSON portant un error.code de premier niveau. Cas courants :

400VITO_VALIDATION_ERROR
Un champ de la requête est manquant ou mal formé (par exemple, un guildId invalide).
401invalid key
Clé API manquante, mal formée ou inconnue.
403OWNER_MEMBERSHIP_INACTIVE
L’abonnement du propriétaire du projet a expiré — l’API est gelée jusqu’à son réabonnement.
403scope / IP
La clé n’a pas la portée requise, ou l’IP de la requête n’est pas autorisée.
404not found
L’utilisateur, la transaction ou la ressource n’existe pas.
402insufficient balance
L’utilisateur n’a pas assez de Vito pour le débit.
402INSUFFICIENT_OWNER_FUNDS
Votre propre solde (propriétaire) est trop faible pour financer un crédit /add.
429rate limited
Vous avez dépassé la limite de débit de votre projet — patientez et réessayez.
Forme de la réponse d’erreur
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

Exemples d’intégration

Créer une demande de débit :

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

Vérifier le solde d’un utilisateur :

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

Tester avec l’intégration de démonstration

Une intégration de démonstration autonome est fournie pour exercer le parcours complet de bout en bout sans écrire de code. Elle parcourt demande → confirmation → PIN → webhook → finalisation.

  1. 1Ouvrez le dossier de l’intégration de démonstration et ajoutez votre clé d’API Vito, un token de bot Discord et l’ID de votre serveur (guild) de test dans config.json.
  2. 2Lancez npm install && npm start, puis ouvrez http://localhost:4000.
  3. 3Recherchez un utilisateur par son ID Discord, choisissez un élément et démarrez un débit — la démo appelle /deduct et affiche la confirmUrl.
  4. 4Approuvez le PIN sur vetox.io ; la démo détecte la finalisation (par webhook si configuré, sinon par sondage) et effectue l’action d’exemple.
Recevoir le webhook nécessite un callback https public et un secret de signature ; sans eux, la démo se rabat sur le sondage de vos transactions, et fonctionne donc sans configuration.
14

Bonnes pratiques, limites et sécurité

Sécurité

  • Gardez votre clé API et le secret de signature uniquement côté serveur ; faites tourner l’un ou l’autre immédiatement en cas de fuite.
  • Vérifiez toujours la signature du webhook contre le corps brut et rejetez les livraisons de plus de ~5 minutes (protection contre le rejeu).
  • N’effectuez votre action que sur confirmation.completed — jamais sur la réponse de /deduct (à ce stade, le débit n’est pas définitif).
  • Utilisez la liste d’IP autorisées et ne demandez que les portées dont vous avez réellement besoin.

Limites

  • Les confirmations expirent après 10 minutes ; traitez les demandes non confirmées comme abandonnées.
  • Les limites de débit sont par projet et évoluent selon le niveau d’abonnement du propriétaire.
  • amount doit être un entier positif ; metadata est plafonné à 10 clés.
Idempotency-Key déduplique les tentativesLes webhooks réessaient 5× avec repliAccusez 2xx vite, honorez en asynchrone