vivraDocumentationV1Comprendre le vote

Annexe B · API

Contrat complet de l'API du serveur Vivra : routes, entrées, sorties et erreurs. Il complète la liste de routes de la page Analyse des projets.

Généralités#

  • Base : https://api.<domaine-vivra>/v1. JSON partout, en UTF-8.
  • Montants on-chain renvoyés en chaîne de caractères (unités brutes) ; montants en USD en nombre à 2 décimales ; dates en ISO 8601 UTC.
  • Pagination par curseur : ?limit=50&cursor=<opaque>, réponse { "items": [...], "next_cursor": "..." | null }. limit max 100.
  • Auth des votants : cookie vivra_session (HttpOnly, Secure, SameSite=Lax), valeur = id de session. Pas de jeton dans le stockage du navigateur.
  • Auth du back-office : signature d'un message par un wallet de team_wallets (SIWE, EIP-4361), puis cookie vivra_admin de 12 h.
  • CORS : seulement le domaine de la dapp et celui du back-office.

Format d'erreur#

json
{ "error": { "code": "vote_closed", "message": "Voting for cycle 4 closed at 2026-11-12T18:00:00Z." } }
HTTPcodeQuand
400invalid_inputchamp manquant ou mal formé (détail dans message)
401unauthenticatedpas de session valide
403forbiddencompte de l'équipe, compte bloqué, wallet hors liste blanche
403account_too_newcompte plus récent que min_account_age_hours
404not_foundressource inconnue
409vote_closed / vote_not_openhors fenêtre de vote
422disposable_emaildomaine d'email jetable
422invalid_codecode faux ou expiré
429rate_limitedtrop de requêtes ; en-tête Retry-After
500internalerreur serveur, avec un request_id

Limites de débit#

RouteLimite
POST /auth/email3 par email par heure, 10 par IP par heure
POST /auth/verify5 essais par code, puis le code est invalidé
POST /votes, POST /supports20 par session par minute
Routes publiques en lecture120 par IP par minute

Routes publiques (sans session)#

GET /stats#

json
{ "takeovers": 4, "revived": 2, "verdicts": 3,
  "vivra_burned": "1250000000000000000000000", "cto_fees_received_usd": 4210.55 }

GET /tokens/:address#

Seulement pour un token publié (candidat d'un cycle publié ou token repris) ; 404 sinon. Renvoie l'en-tête du token, le dernier instantané, les vérifications de contrat, les socials, le dernier score de reprise avec son détail, et l'historique quotidien sur 90 jours (history: [{ "date", "price_usd", "volume_usd", "holders" }]).

GET /cycles/current#

json
{ "id": 4, "number": 4, "status": "voting",
  "vote_opens_at": "2026-11-10T18:00:00Z", "vote_closes_at": "2026-11-12T18:00:00Z",
  "voters": 3812,
  "candidates": [ {
    "id": 17, "position": 1, "token": "0x…", "ticker": "GRANDPA",
    "tagline": "…", "thesis_short": "…", "votes": 1290, "share_pct": 33.8,
    "supports": 412, "recovery_score": 68 } ],
  "my_vote": 17 }

my_vote vaut null sans session ou sans vote.

GET /candidates/:id#

La fiche complète : tous les blocs de « Contenu d'une fiche candidat », plus recovery_score (note figée à la publication et son détail) et recovery_score_live.

GET /cycles et GET /cycles/:id#

Historique des cycles, avec le gagnant et les résultats finaux.

GET /operations/:id#

json
{ "id": 4, "token": "0x…", "ticker": "GRANDPA", "status": "running", "j0": "…",
  "steps": [ { "key": "designer.directions", "agent": "designer", "due_at": "…", "status": "done" } ],
  "spent_usd": 38.12,
  "buyback": { "envelope_wei": "…", "cap_wei": "…", "slices_done": 9, "slices_total": 23, "burned": "…" },
  "links": { "site": "https://grandpa.<domaine-vivra>", "x": "https://x.com/…", "telegram": "https://t.me/…" },
  "cto_request": { "status": "submitted" },
  "verdict": null }

GET /operations/:id/log#

Pagination par curseur. Chaque ligne : { "id", "at", "agent", "kind", "summary", "cost_usd" }. Jamais payload.

GET /ledger#

Paramètres : type, from, to, limit, cursor. Chaque ligne : type, jeton, montant brut et en USD, wallets source et destination, hash, date.

GET /wallets#

Les adresses publiques de tous les wallets de la table wallets, avec leur solde courant.

Connexion#

POST /auth/email#

Entrée : { "email": "a@b.com", "captcha_token": "…" }. Normalise l'email (minuscules, sans espaces), refuse les domaines jetables, crée ou retrouve l'utilisateur, envoie un email contenant un code à 6 chiffres et un lien magique (/auth/magic?token=…). Réponse 204, toujours la même, que l'email existe ou non.

POST /auth/verify#

Entrée : { "email": "a@b.com", "code": "482913" } ou { "magic_token": "…" }. Crée la session (30 jours) et pose le cookie. Réponse { "user": { "id": 812, "created_at": "…", "can_vote_at": "…" } }.

POST /auth/logout#

Révoque la session courante. 204.

GET /me#

{ "id", "created_at", "can_vote_at", "votes": [{ "cycle_id", "candidate_id" }], "supports": [candidate_id] }.

Vote et soutien (session requise)#

POST /votes#

Entrée : { "candidate_id": 17 }. Crée ou remplace le vote de l'utilisateur pour le cycle de ce candidat. Erreurs : vote_not_open, vote_closed, account_too_new, forbidden. Réponse : le même objet que GET /cycles/current, à jour.

POST /supports et DELETE /supports/:candidate_id#

Entrée : { "candidate_id": 17 }. Idempotent. Réponse { "supports": 413 }.

Temps réel (WebSocket)#

Connexion : wss://api.<domaine-vivra>/v1/ws. Le client envoie { "subscribe": "cycle:4" } ou { "subscribe": "operation:4" }. Messages reçus :

typeCanalContenu
vote.tallycycle:*{ candidates: [{ id, votes, share_pct }], voters }, au plus 1 par seconde
step.updatedoperation:*{ key, status, at }
log.appendedoperation:*une ligne du journal public
slice.executedoperation:*{ index, amount_in_wei, amount_burned, tx_hash, at }
tradeoperation:*{ price_usd, amount_usd, side, at } pour le chart live
verdictoperation:*{ verdict, at }

Ping toutes les 25 s, reconnexion automatique côté client avec reprise depuis le dernier id reçu ({ "subscribe": "operation:4", "since": 1532 }).

Back-office (préfixe /admin, session admin requise)#

Méthode et routeRôle
POST /admin/auth/nonce, POST /admin/auth/verifyconnexion SIWE d'un wallet de l'équipe
POST /admin/projectssoumettre une adresse (et des liens optionnels) ; lance l'analyse du Scanner
GET /admin/projects?status=analyzedprojets analysés, avec cible, sécurité et score de reprise
POST /admin/projects/:address/review{ "decision": "validate" | "reject" | "rerun", "reason": "…" }
POST /admin/cyclescréer un cycle (numéro, horaires, délai minimal du compte)
PUT /admin/cycles/:id/candidatesfixer les 5 candidats et leurs textes ; refusé si le cycle est publié
POST /admin/candidates/:id/prereportlancer le pré-rapport du Scanner
POST /admin/cycles/:id/publishpublier les 5 fiches d'un coup ; fige les scores
GET /admin/validations?status=pendingfile de validation
POST /admin/validations/:id{ "decision": "approve" | "reject", "reason": "…" }
POST /admin/operations/:id/pause, /resume, /stoppilotage et arrêt d'urgence d'une opération
POST /admin/buyback/stoparrêt d'urgence de toutes les tranches
PUT /admin/cto-requests/:idmettre à jour le statut de la demande Pons
GET /admin/settings, PUT /admin/settings/:keyseuils et poids, avec audit
GET /admin/auditjournal des actions de l'équipe

Chaque appel /admin en écriture ajoute une ligne à admin_audit.

↑ ↓ pour parcourir · Entrée pour ouvrirVIVRA DOCS