Retour à la documentation
Fonctionnalités

Module npm : @flitt/api

Le client officiel de l'API publique Flitt pour TypeScript et JavaScript. Il appelle l'API à votre place, gère les erreurs, les relances et le temps réel, pour que votre code ne contienne que votre logique.

1

À quoi sert le module

@flitt/api enveloppe l'API publique : au lieu d'écrire vos requêtes HTTP, vous appelez des méthodes typées (flitt.servers.list(), flitt.votes.claim()…). Le module s'occupe de l'authentification, des délais, des relances, de la pagination et des erreurs.

  • Aucune dépendance, ESM et CommonJS, types TypeScript complets.
  • Fonctionne sur Node.js 20+, Deno, Bun et Cloudflare Workers (tout environnement avec fetch).
  • Sûr par défaut : HTTPS uniquement, clé jamais affichée, redirections jamais suivies, refus de s'exécuter dans un navigateur.
  • Résilient : délai par requête, relances avec backoff exponentiel, Retry-After respecté, réclamation de vote idempotente.
  • Temps réel : votes, joueurs, chat et statut poussés par WebSocket, avec reconnexion et reprise automatiques.
2

Installation

npm install @flitt/api
# pnpm add @flitt/api · yarn add @flitt/api · bun add @flitt/api

Premier appel, en ESM ou TypeScript :

import { Flitt } from "@flitt/api";

const flitt = new Flitt({ apiKey: process.env.FLITT_API_KEY });

const { data: servers } = await flitt.servers.list();
for (const server of servers) {
  console.log(`${server.name}: ${server.playersOnline} players (${server.status})`);
}

En CommonJS :

const { Flitt } = require("@flitt/api");
3

Identifiants

Deux identifiants existent. Passez-en un ou les deux au constructeur, ou laissez le module les lire dans les variables d'environnement :

Clé du compteJeton serveur
OptionapiKey (env FLITT_API_KEY)serverToken (env FLITT_SERVER_TOKEN)
Formatflt_…flv_…
Où le créerDashboard → Profil → APIDashboard → votre serveur → Votes → API
Donne accès àTous les endpoints, sur tous les serveurs dont vous êtes propriétaire ou membre (selon votre rôle)Votes et avis d'un seul serveur
QuotaMensuel, selon l'offre120 requêtes / minute
À utiliserVotre backend, votre bot Discord, vos outilsSur un serveur de jeu (Lua, Java, petit script)

.env

FLITT_API_KEY=flt_...
FLITT_SERVER_TOKEN=flv_...

Sur un serveur de jeu

const flitt = new Flitt({ serverToken: process.env.FLITT_SERVER_TOKEN });

const claim = await flitt.votes.claim("srv_123", { player: "Alex_RP" });
4

Quotas et limites

OffreClé du compteJeton serveur
Freemium1 000 requêtes / mois120 requêtes / minute
Premium20 000 requêtes / mois120 requêtes / minute
EntrepriseIllimité120 requêtes / minute

Un compte propriétaire d'un serveur Premium serveur (ou d'un serveur partenaire) profite du quota Premium. Un flux d'événements temps réel compte pour une requête par connexion ; les événements reçus sont gratuits.

L'état du quota après chaque réponse est disponible dans flitt.rateLimit ({ limit, remaining, reset }, null = illimité) ou via le rappel onRateLimit :

const flitt = new Flitt({
  apiKey: process.env.FLITT_API_KEY,
  onRateLimit: ({ limit, remaining, reset }) => console.log(remaining, "/", limit, "until", reset),
});

await flitt.servers.list();
console.log(flitt.rateLimit);
5

Méthodes disponibles

Toutes les méthodes renvoient une Promise et lèvent une erreur typée en cas d'échec. Chacune accepte un dernier argument d'options par requête.

Compte

flitt.usage.get()

Quota du compte : offre, limite mensuelle, consommé, restant, date de remise à zéro.

Serveurs

flitt.servers.list({ page?, limit? })

Vos serveurs (propriétaire ou membre), paginés.

flitt.servers.listAll()

Itérateur sur toutes les pages.

flitt.servers.get(serverId)

Détail d'un serveur, avec hold (maintenance, développement, fermeture) ; IP et port masqués pour le rôle VIEWER.

Joueurs

flitt.players.list(serverId, { status?, page?, limit? })

Joueurs : online, offline ou all.

flitt.players.listAll(serverId, { status? })

Itérateur sur tous les joueurs.

flitt.players.get(serverId, playerId)

Un joueur, par id Flitt ou identifiant de jeu (SteamID, licence, UUID).

Pare-feu (anti-VPN)

flitt.firewall.get(serverId) / update(serverId, changes)

Réglages : activé, blocage VPN, message de kick (OWNER ou ADMIN pour modifier).

flitt.firewall.rules.list / create / delete / deleteMany

Règles ALLOW / DENY par identifiant, IP ou pays ; jusqu'à 100 règles par appel, succès partiel possible ; deleteMany refuse un filtre vide.

flitt.firewall.blocks(serverId, { limit? })

Dernières connexions bloquées.

await flitt.firewall.update("srv_123", { enabled: true, blockVpn: true, kickMessage: "VPN not allowed" });

const result = await flitt.firewall.rules.create("srv_123", [
  { action: "DENY", kind: "IDENTIFIER", value: "76561198000000000", note: "Cheater" },
  { action: "DENY", kind: "COUNTRY", value: "XX" },
]);
for (const e of result.errors) console.warn(e.index, e.code, e.message);

Commandes en jeu

flitt.commands.chat / notify(serverId, …)

Message à tout le serveur ou à un joueur (rôle EDITOR).

flitt.commands.kick / ban / unban(serverId, externalId, …)

Sanctions (rôle ADMIN). Jamais relancées automatiquement : un ban ne doit pas partir deux fois.

flitt.commands.setGroup / setName(serverId, externalId, …)

Groupe (ULX, SAM, CAMI, ace, LuckPerms…) ou pseudo.

flitt.commands.groups(serverId)

Groupes actuels des joueurs connectés.

Les commandes sont mises en file pour l'addon du serveur. Passez waitMs (15 000 au maximum) pour attendre sa réponse :

await flitt.commands.chat("srv_123", "Restart in 5 minutes!");
const res = await flitt.commands.ban("srv_123", "76561198000000000", { reason: "RDM", durationSeconds: 86_400 }, { waitMs: 5000 });
console.log(res.result?.ok, res.result?.output);

Liaison Discord

flitt.discord.createLinkCode(discordId)

Code à taper en jeu (!flitt CODE, ou /flitt link CODE sur Minecraft).

flitt.discord.findLinks / link / unlink

Comptes Discord liés à un joueur, liaison et suppression manuelles.

flitt.discord.getModule / setModule(serverId, enabled)

Module Discord de l'addon, pour une intégration sans le bot Flitt.

Votes et avis

flitt.votes.check(serverId, identity)

CLAIMABLE, CLAIMED ou NOT_FOUND, avec nextVoteInMinutes.

flitt.votes.claim(serverId, identity, { idempotencyKey? })

Réclame le plus ancien vote non réclamé (24 h), une seule fois, tous canaux confondus.

flitt.votes.list / listAll(serverId, { since?, limit? })

Votes récents, pagination par curseur (30 jours).

flitt.votes.top(serverId, { period?, limit? })

Meilleurs votants : current_month, last_month, all_time ou YYYY-MM.

flitt.votes.summary / stats(serverId)

Compteurs, classement, note ; votes, vues et clics par jour, semaine, mois.

flitt.reviews.list / listAll(serverId, { sort?, limit? })

Avis publiés.

Temps réel

flitt.events.connect(options?)

Flux d'événements en temps réel (WebSocket).

6

Pagination et itérateurs

Les méthodes list() renvoient une page ({ data, pagination } ou un curseur pour les votes). Les méthodes listAll() enchaînent les pages pour vous, avec for await :

const page = await flitt.servers.list({ page: 1, limit: 25 });
console.log(page.data, page.pagination);

for await (const player of flitt.players.listAll("srv_123", { status: "all" })) {
  console.log(player.name, player.totalPlaySeconds);
}
7

Récompenser les votes

Pour récompenser un votant depuis votre propre code (par exemple sans l'addon Flitt) : vérifiez, réclamez le vote une seule fois, puis donnez la récompense. Fonctionne avec la clé du compte (rôle avec les permissions de votes) ou le jeton serveur.

const flitt = new Flitt({ serverToken: process.env.FLITT_SERVER_TOKEN });

const claim = await flitt.votes.claim("srv_123", { player: "Alex_RP" });
if (claim.status === "CLAIMED_NOW") {
  giveReward("Alex_RP");
  for (const bonus of claim.rewards ?? []) giveBonus("Alex_RP", bonus.id);
}

Identifiez le joueur avec un seul champ parmi player, steamId, discordId, license, uuid, ip ou voteId. claim() envoie toujours un en-tête Idempotency-Key : une relance automatique après une erreur réseau ne réclame jamais deux votes. Passez votre propre clé (par exemple ${player}:${heure}) pour que vos propres relances le soient aussi.

8

Événements en temps réel

flitt.events.connect() ouvre un flux WebSocket qui reçoit les événements de vos serveurs au moment où ils arrivent : votes, arrivées et départs, chat, statut, avis, affluence. Sans requêtes en boucle et sans URL publique : idéal pour un bot Discord ou un programme qui tourne en continu.

const stream = flitt.events.connect({ events: ["vote_created", "player_joined"] });

stream.on("ready", ({ servers }) => console.log("following", servers));
stream.on("vote_created", (e) => console.log(`${e.data?.player} voted for ${e.server.name}`));
stream.on("player_joined", (e) => console.log("join", e.data));
stream.on("reconnecting", ({ delayMs }) => console.log("reconnecting in", delayMs, "ms"));
stream.on("error", (err) => console.error(err.message));

Ou avec for await :

for await (const event of flitt.events.connect({ servers: ["srv_123"] })) {
  if (event.event === "player_chat") relayToDiscord(event);
}
OptionPar défautRôle
serverstous vos serveursServeurs à suivre (200 au maximum)
eventstous ceux autorisés par votre rôleTypes d'événements à recevoir
resumetrueAprès une reconnexion, renvoyer ce qui a été manqué (jusqu'à 1 heure)
lastEventIdsaucunPositions sauvegardées depuis stream.lastEventIds, pour reprendre après un redémarrage
WebSocketWebSocket globalConstructeur à utiliser (paquet ws sur Node.js 20)
signalaucunAbortSignal qui ferme le flux
maxReconnectDelayMs30000Attente maximale entre deux tentatives de reconnexion
bufferSize1000Événements gardés pour une boucle for await lente (les plus anciens sont jetés)

Écouteurs disponibles : ready, event et un par type (vote_created…), reconnecting, gap (événements trop anciens pour être rejoués), error et close. stream.close() l'arrête définitivement ; stream.connected et stream.lastEventIds donnent son état.

Ce que vous recevez. Avec la clé du compte : les serveurs dont vous êtes propriétaire ou membre, filtrés par votre rôle (statut, affluence, joueurs, chat, votes : voir le tableau des événements). Avec un jeton serveur : vote_created et review_created de son serveur uniquement.

Reprendre après un redémarrage de votre programme :

import { readFile, writeFile } from "node:fs/promises";

const saved = JSON.parse(await readFile("positions.json", "utf8").catch(() => "{}"));
const stream = flitt.events.connect({ lastEventIds: saved });

process.on("SIGTERM", async () => {
  await writeFile("positions.json", JSON.stringify(stream.lastEventIds));
  stream.close();
});

Environnements. Le flux utilise le WebSocket natif (Node.js 22+, Deno, Bun). Sur Node.js 20, installez ws et passez-le :

import WebSocket from "ws";

const stream = flitt.events.connect({ WebSocket });
9

Vérifier un webhook

Si vous préférez recevoir les événements sur votre propre URL, verifyWebhook() vérifie que la requête vient bien de Flitt : signature HMAC-SHA256, âge (5 minutes) et rejeu. Passez le corps brut (pas le JSON re-sérialisé) et le secret affiché à la création du webhook.

Express

import express from "express";
import { verifyWebhook, FlittWebhookError } from "@flitt/api";

const app = express();

app.post("/flitt-webhook", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    const event = await verifyWebhook({
      payload: req.body,
      headers: req.headers,
      secret: process.env.FLITT_WEBHOOK_SECRET!,
      isReplay: (nonce) => seenBefore(nonce),
    });
    if (event.event === "server_down") console.log(`${event.server.name} is down`);
    res.sendStatus(204);
  } catch (err) {
    res.sendStatus(err instanceof FlittWebhookError ? 400 : 500);
  }
});

Fetch API (Next.js, Workers, Deno, Bun)

export async function POST(request: Request) {
  const event = await verifyWebhook({
    payload: await request.text(),
    headers: request.headers,
    secret: process.env.FLITT_WEBHOOK_SECRET!,
  });
  return new Response(null, { status: 204 });
}

Chaque événement a la même enveloppe, typée WebhookEvent : { event, timestamp, server: { id, name }, data }.

10

Erreurs

Toutes les erreurs héritent de FlittError. Les erreurs de l'API héritent de FlittAPIError et portent status, code, message, details, method, path, rateLimit et retryAfterMs. Elles ne contiennent jamais votre clé.

ClasseQuand
FlittConfigErrorOption ou argument invalide, détecté avant toute requête
FlittValidationError400 : paramètre invalide
FlittAuthenticationError401 : clé ou jeton manquant, invalide ou révoqué
FlittPermissionError403 : l'offre ou le rôle ne le permet pas
FlittNotFoundError404 : introuvable, ou pas à vous
FlittConflictError409 : conflit (règle en double, serveur inactif…)
FlittRateLimitError429 : quota atteint, voir retryAfterMs et rateLimit.reset
FlittServerError5xx : erreur temporaire côté Flitt
FlittTimeoutErrorPas de réponse dans timeoutMs
FlittConnectionErrorErreur réseau (DNS, TLS, coupure…)
FlittAbortErrorAnnulé par votre AbortSignal
import { FlittNotFoundError, FlittRateLimitError } from "@flitt/api";

try {
  await flitt.servers.get("srv_123");
} catch (err) {
  if (err instanceof FlittNotFoundError) console.log("Unknown server");
  else if (err instanceof FlittRateLimitError) console.log(`Retry in ${err.retryAfterMs} ms`);
  else throw err;
}
11

Options

new Flitt({
  apiKey: "flt_...",
  serverToken: "flv_...",
  baseUrl: "https://api.flitt.me",
  timeoutMs: 15_000,
  maxRetries: 2,
  maxRetryDelayMs: 30_000,
  fetch: customFetch,
  userAgent: "my-bot/1.0",
  onRateLimit: (rl) => metrics.gauge("flitt.remaining", rl.remaining ?? Infinity),
  dangerouslyAllowBrowser: false,
});
OptionPar défautRôle
baseUrlhttps://api.flitt.meHTTPS obligatoire (HTTP accepté pour localhost)
timeoutMs15000Délai par tentative
maxRetries2Relances des requêtes idempotentes : erreurs réseau, 408, 425, 5xx, 429 courts
maxRetryDelayMs30000Attente maximale entre deux relances ; un Retry-After plus long n'est pas attendu
fetchfetch globalTests, proxy, instrumentation
userAgentflitt-api-js/<version>Côté serveur uniquement
onRateLimitaucunAppelé avec l'état du quota après chaque réponse
dangerouslyAllowBrowserfalseAutorise l'exécution dans un navigateur (déconseillé)

Chaque méthode accepte aussi un dernier argument { signal?, timeoutMs?, maxRetries? } :

const controller = new AbortController();
setTimeout(() => controller.abort(), 2000);

await flitt.servers.list({}, { signal: controller.signal, timeoutMs: 5000, maxRetries: 0 });
12

Sécurité

  • HTTPS uniquement (HTTP accepté pour localhost en développement) ; identifiants, paramètres et fragments refusés dans baseUrl.
  • Redirections jamais suivies : l'en-tête Authorization ne peut pas fuir vers un autre domaine.
  • Segments de chemin encodés, segments . et .. refusés.
  • Identifiants gardés dans des champs privés : console.log, util.inspect, JSON.stringify et les erreurs ne les affichent jamais.
  • Format des identifiants vérifié localement : un jeton serveur passé comme clé de compte (ou l'inverse) échoue avant toute requête.
  • Flux temps réel en wss:// uniquement ; l'identifiant voyage dans le premier message, jamais dans l'URL.
  • Réponses de plus de 32 Mo refusées.
13

Versions et mises à jour

Version actuelle : 1.3.0. Le module suit le versionnage sémantique : une version mineure (1.x) n'introduit jamais de changement cassant. Pour mettre à jour :

npm install @flitt/api@latest
npm ls @flitt/api
VersionNouveautés
1.3.0Événements en temps réel : flitt.events.connect(), reconnexion et reprise automatiques, erreurs typées.
1.2.xhold sur les serveurs (maintenance, développement, fermeture) et événements server_hold_started / server_hold_ended.
1.1.0Votes et avis intégrés au client Flitt (flitt.votes, flitt.reviews), jeton serveur via serverToken.
1.0.0Première version : serveurs, joueurs, pare-feu, commandes, Discord, webhooks.

Depuis 1.2 : rien à changer, flitt.events est simplement nouveau. Depuis 1.0 : le client séparé FlittVotes fonctionne toujours mais est déprécié ; passez à flitt.votes :

const votes = new FlittVotes({ token: process.env.FLITT_VOTE_TOKEN });
await votes.claim({ player: "Alex_RP" });

const flitt = new Flitt({ serverToken: process.env.FLITT_VOTE_TOKEN });
await flitt.votes.claim("srv_123", { player: "Alex_RP" });

La version installée est exportée par le module, et envoyée dans le User-Agent :

import { VERSION } from "@flitt/api";
console.log(VERSION);