Vito API-documentatie

Laatst bijgewerkt: 26 juni 2026
01

Overzicht

Met de Vito-API kan je applicatie werken met het Vito-saldo van een gebruiker vanuit Discord — het lezen, afschrijven of weer bijschrijven. De gebruiker bevestigt elke afschrijving met zijn pincode op vetox.io voordat er Vito wordt verplaatst, en je app krijgt de uitkomst doorgegeven. Vito is de virtuele valuta binnen het Vetox-ecosysteem; gebruik de API om Vito-gebaseerde ervaringen te bouwen — het ontgrendelen van content, functies, voordelen of beloningen. Er vindt geen koop of verkoop met echt geld plaats.

Het is een REST-API die wordt geauthenticeerd met een geheime sleutel. Alle endpoints geven JSON terug en zijn geversioneerd onder /v1.

Basis-URLhttps://api.vetox.io/public/vito/v1
02

Saldi & afwikkeling

Je API-sleutel is gekoppeld aan je Vetox-account. Elke afschrijving wordt in jouw voordeel afgewikkeld: wanneer je app Vito van een gebruiker aftrekt, wordt die Vito op je eigen saldo bijgeschreven (minus de platformkosten), nooit vernietigd. Wanneer je app Vito aan een gebruiker toevoegt, wordt dit vanuit je saldo gefinancierd.

Vito verplaatst altijd tussen saldi — nooit van of naar echt geld. De gebruiker wordt het volledige bedrag dat je vraagt in rekening gebracht; jij ontvangt het bedrag na de platformkosten; bijschrijvingen en beloningen worden vanuit je saldo via /add gefinancierd.

Platformkosten — dezelfde staffel als in-app Vito-overboekingen, op basis van je lidmaatschapsniveau: 7% (Silver / Gold), 6% (Platinum), 5% (Diamond). Afschrijvingen van 5 Vito of minder zijn kosteloos. Bijschrijvingen via /add worden nooit belast.
03

Vereisten

Toegang tot de Vito-API wordt per project verleend. Voordat je hem kunt aanroepen, heb je nodig:

  • Een actief gebruikerslidmaatschap (niveau Silver of hoger). De API bevriest automatisch als het lidmaatschap van de eigenaar verloopt, en wordt hersteld wanneer hij opnieuw abonneert.
  • Een goedgekeurde ontwikkelaarsaanvraag. Dien er een in via deze pagina; het Vetox-team beoordeelt deze handmatig.
  • Geaccepteerde API-ontwikkelaarsvoorwaarden — bevestigd wanneer je je ontwikkelaarsaanvraag indient.
  • De scopes die je project nodig heeft. Ze worden door het Vetox-team verleend op basis van je aanvraag.

Beschikbare scopes

balance:read
Het Vito-saldo van een gebruiker lezen.
deduct:create
Een afschrijving maken — het Vito-saldo van een gebruiker belasten.
credit:create
Vito aan een gebruiker toevoegen — beloningen of bijschrijvingen vanuit je saldo financieren.
transfer:create
Een overdracht tussen twee gebruikers maken.
transactions:read
De transacties van je project opsommen en lezen.
04

Authenticatie

Authenticeer elk verzoek met je geheime API-sleutel in de Authorization-header als Bearer-token. Sleutels hebben het prefix vito_live_.

http
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx

Twee optionele lagen beschermen je project verder:

  • IP-toelatingslijst — beperk verzoeken tot specifieke server-IP’s (configureer dit op het tabblad Instellingen).
  • Snelheidslimieten — verzoekplafonds per project die meeschalen met je lidmaatschapsniveau.
05

API-sleutels — opslag, rotatie en beveiliging

Je sleutel wordt gegenereerd wanneer je aanvraag wordt goedgekeurd. Hij wordt maar één keer getoond — onthul hem op het tabblad API-sleutels en sla hem direct op.

Vetox slaat je sleutel nooit in leesbare vorm op (alleen een gesalte hash). Bewaar hem als een server-side geheim — een omgevingsvariabele of een secrets-manager — nooit in clientcode, een git-repository of een browser.

Roteer hem op het tabblad API-sleutels. De vorige sleutel blijft nog 24 uur als overgangsperiode werken zodat je de nieuwe zonder downtime kunt uitrollen, daarna stopt hij.

Behandel de sleutel als een wachtwoord — iedereen die hem heeft, kan je gebruikers belasten. Roteer bij een lek onmiddellijk en bekijk je recente transacties.
06

Bevestigingsflow van de afschrijving

Elke afschrijving verloopt via een bevestiging die de gebruiker met zijn pincode goedkeurt — je sleutel alleen verplaatst nooit Vito:

  1. 1Je app roept POST /deduct aan met de gebruiker, het bedrag, de oorspronkelijke guildId en de itemgegevens.
  2. 2Vito maakt een openstaande bevestiging (10 minuten geldig) en retourneert een confirmUrl. Als een guildId wordt opgegeven, ontvangt de gebruiker ook een DM met de gegevens en een knop naar de bevestigingspagina.
  3. 3De gebruiker opent de confirmUrl op vetox.io en keurt de afschrijving goed met de pincode van zijn wallet.
  4. 4Vito schrijft de Vito af, registreert de transactie en stuurt — als je een webhook hebt geconfigureerd — een ondertekende confirmation.completed-gebeurtenis naar je callback-URL.
  5. 5Je app verifieert de handtekening en voert zijn actie uit (bijvoorbeeld ontgrendelt de content).
Pincodes worden alleen op vetox.io ingevoerd — nooit in je app of in Discord. Als de gebruiker nooit bevestigt, verloopt het verzoek na 10 minuten en ontvang je een confirmation.expired-gebeurtenis.
07

Verzoekparameters (/deduct)

POST/v1/deduct

Het afschrijvings-endpoint accepteert de volgende body-velden:

discordId
De Discord-gebruikers-ID van de gebruiker — een snowflake van 17 tot 20 cijfers. Verplicht.
amount
Hoeveelheid Vito om af te schrijven — een positief geheel getal. Verplicht.
guildId
De Discord-server waar de afschrijving vandaan komt (snowflake). Verplicht — elke afschrijving moet van een specifieke server komen, wat ook de DM naar de gebruiker inschakelt.
reason
Een korte leesbare beschrijving (≤ 256 tekens), getoond aan de gebruiker en herhaald in de webhook.
merchantRef
Je eigen referentietekst (≤ 128 tekens). Gebruik deze om de webhook aan je administratie te koppelen.
product
Itembeschrijving — { type, name, description?, imageUrl? }. Getoond aan de gebruiker op de bevestigingspagina en de DM, en herhaald in elke webhook.
product.imageUrl
Optionele productafbeelding — moet een https://-URL zijn.
metadata
Tot 10 string-sleutel/waardeparen, ongewijzigd doorgegeven in de webhook.

* verplicht veld.

Voorbeeldverzoek
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" }
}
Voorbeeldreactie
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 toevoegen — bijschrijvingen (/add)

POST/v1/add

POST /add schrijft Vito bij een gebruiker bij vanuit je eigen saldo — voor beloningen of bijschrijvingen. Het neemt dezelfde velden als /deduct, behalve guildId en product. Anders dan een afschrijving is het direct en heeft het geen gebruikers-PIN nodig — je API-sleutel is je bevoegdheid — dus het antwoord weerspiegelt al de voltooide overdracht. De gebruiker ontvangt het volledige bedrag; er zijn geen platformkosten van toepassing.

Vereist de scope credit:create en voldoende Vito op je eigen saldo — anders geeft de aanroep 402 INSUFFICIENT_OWNER_FUNDS terug.
Voorbeeldverzoek
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" }
}
Voorbeeldreactie
json
{
  "success": true,
  "data": {
    "transactionId": "txn_91b2",
    "toDiscordId": "123456789012345678",
    "amount": 250,
    "ownerBalance": 48250,
    "recipientBalance": 1250,
    "status": "completed"
  },
  "requestId": "req_def456",
  "timestamp": 1735689300000
}
09

Webhooks en handtekeningverificatie

Voeg een of meer https-callback-URL’s toe op het tabblad Instellingen. Vito levert een ondertekende POST aan elke geconfigureerde URL wanneer een bevestiging een eindstatus bereikt, met tot 5 nieuwe pogingen met exponentiële backoff.

Webhooks worden alleen geactiveerd wanneer je project ZOWEL een geconfigureerde callback-URL ALS een ondertekeningsgeheim heeft. Onthul je ondertekeningsgeheim (whsec_…) één keer op het tabblad API-sleutels.

De handtekening verifiëren

Elke levering bevat een X-Vito-Signature-header van de vorm ts=<unix>;h1=<hex>. Herbereken de HMAC over "<ts>:<rawBody>" met je ondertekeningsgeheim en vergelijk in constante tijd. Verifieer altijd tegen de ruwe verzoekbody, vóór het JSON-parsen.

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

Webhook-gebeurtenissen en payloads

Vito stuurt een van vier gebeurtenistypes. Ze delen allemaal dezelfde payload-vorm; data.status weerspiegelt de uitkomst.

confirmation.completed
De gebruiker heeft bevestigd en de Vito is afgeschreven. Voer je actie uit. Bevat transactionId.
confirmation.failed
De afschrijving kon niet worden voltooid (bijvoorbeeld onvoldoende saldo of een interne fout). Bevat failureReason.
confirmation.expired
De gebruiker heeft niet binnen 10 minuten bevestigd. Geen Vito verplaatst.
confirmation.cancelled
De gebruiker heeft het verzoek geannuleerd. Geen Vito verplaatst.
credit.completed
Een door de eigenaar gefinancierde /add-bijschrijving is afgewikkeld. Bevat de ontvanger en transactionId, en wordt direct geactiveerd — er is geen bevestigingsstap.
Voorbeeld-payload (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

Foutcodes

Fouten geven een niet-2xx-status terug met een JSON-body die een top-level error.code bevat. Veelvoorkomende gevallen:

400VITO_VALIDATION_ERROR
Een verzoekveld ontbreekt of is misvormd (bijvoorbeeld een ongeldige guildId).
401invalid key
Ontbrekende, misvormde of onbekende API-sleutel.
403OWNER_MEMBERSHIP_INACTIVE
Het lidmaatschap van de projecteigenaar is verlopen — de API is bevroren totdat hij opnieuw abonneert.
403scope / IP
De sleutel mist de vereiste scope, of het verzoek-IP staat niet op de toelatingslijst.
404not found
De gebruiker, transactie of resource bestaat niet.
402insufficient balance
De gebruiker heeft niet genoeg Vito voor de afschrijving.
402INSUFFICIENT_OWNER_FUNDS
Je eigen (eigenaars)saldo is te laag om een /add-bijschrijving te financieren.
429rate limited
Je hebt de snelheidslimiet van je project overschreden — wacht en probeer opnieuw.
Vorm van de foutreactie
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

Integratievoorbeelden

Een afschrijvingsverzoek maken:

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

Het saldo van een gebruiker controleren:

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

Testen met de demo-integratie

Een zelfstandige demo-integratie wordt geleverd om de volledige flow van begin tot eind te doorlopen zonder code te schrijven. Hij doorloopt verzoek → bevestiging → pincode → webhook → voltooiing.

  1. 1Open de map van de demo-integratie en voeg je Vito-API-sleutel, een Discord-bottoken en je test-server-(guild-)ID toe aan config.json.
  2. 2Voer npm install && npm start uit en open vervolgens http://localhost:4000.
  3. 3Zoek een gebruiker op Discord-ID, kies een item en start een afschrijving — de demo roept /deduct aan en toont de confirmUrl.
  4. 4Keur de pincode goed op vetox.io; de demo detecteert de voltooiing (via webhook indien geconfigureerd, anders via polling) en voert de voorbeeldactie uit.
Het ontvangen van de webhook vereist een openbare https-callback en een ondertekeningsgeheim; zonder deze valt de demo terug op het pollen van je transacties, dus werkt hij zonder configuratie.
14

Best practices, limieten en beveiliging

Beveiliging

  • Houd je API-sleutel en ondertekeningsgeheim alleen server-side; roteer een van beide onmiddellijk bij een lek.
  • Verifieer de webhook-handtekening altijd tegen de ruwe body en weiger leveringen ouder dan ~5 minuten (replaybescherming).
  • Voer je actie alleen uit bij confirmation.completed — nooit bij de reactie van /deduct (op dat moment is de afschrijving niet definitief).
  • Gebruik de IP-toelatingslijst en vraag alleen de scopes aan die je echt nodig hebt.

Limieten

  • Bevestigingen verlopen na 10 minuten; behandel niet-bevestigde verzoeken als verlaten.
  • Snelheidslimieten gelden per project en schalen mee met het lidmaatschapsniveau van de eigenaar.
  • amount moet een positief geheel getal zijn; metadata is beperkt tot 10 sleutels.
Idempotency-Key dedupliceert nieuwe pogingenWebhooks proberen 5× opnieuw met backoffBevestig snel 2xx, voer asynchroon uit