Documentation développeur

Vos votes et votre billetterie, dans votre backend.

Une API REST par clé d’organisation — créez une transaction de vote ou de billet, suivez son statut, éprouvez votre intégration en bac à sable. L’API héritée reste servie telle quelle pour les intégrations déjà construites contre l’ancienne plateforme.

Vue d’ensemble

Le SDK et le plugin affichent et encaissent depuis le navigateur de vos visiteurs. Si vous préférez tout maîtriser — votre propre interface, un canal de vente parallèle, un rapprochement comptable automatisé — appelez directement notre API, par clé d’organisation.

Quatre gestes, et quatre seulement : lire vos éditions et ce qu’elles contiennent, créer une transaction de vote ou d’achat de billet, accorder des votes avec un code de billet déjà payé, suivre le statut d’un paiement. Rien ne s’y crée ni ne s’y modifie côté configuration — pas d’édition, pas de nominé, pas de retrait : une clé qui fuite ne peut qu’engager des paiements que vous encaissez vous-même.

Adresse de base
https://api.zekin.me/api/v1/public

Format des réponses

Le même partout dans cette API : un succès rend { success: true, message, data, meta? }, un refus { success: false, error: { code, message, details? } }. Les messages suivent l’en-tête X-Locale: fr|en (français par défaut).

Cloisonnement

Une clé ne voit que son organisation. Ce qui ne lui appartient pas est traité comme absent (404), jamais comme interdit — répondre « interdit » apprendrait déjà qu’une donnée existe ailleurs. Cela vaut en lecture comme en écriture : voter pour le nominé d’un autre organisateur ou consulter sa transaction rend 404, même avec une clé parfaitement valide.

Cette API, le SDK et le plugin lisent les mêmes données et respectent les mêmes règles — prix, paliers, clôture d’une phase. Rien ne diffère entre eux, sinon la façon d’y accéder.

Authentification

Votre clé se génère et se régénère depuis votre espace Zekin : Organisations → menu de la ligne → « Clé API ». Elle se transmet dans l’en-tête X-API-Key de chaque appel — jamais dans l’adresse, jamais dans un fichier JavaScript servi à vos visiteurs.

GET/public/meVérifier votre clé
Réponse
{
  "success": true,
  "message": "Clé API valide.",
  "data": {
    "organizationId": "00615193-0dbf-4537-872b-d59a424f26ba",
    "organizationName": "Votre organisation"
  }
}
cURL
curl https://api.zekin.me/api/v1/public/me \
  -H "X-API-Key: zk_votre_cle"
Appelez cette route en premier lorsqu’une intégration ne fonctionne pas : une clé fausse se manifeste sinon au milieu d’un appel métier, où l’erreur se confond avec un problème de données.
Authorization: ApiKey <clé> est aussi accepté, pour les clients HTTP qui l’imposent. Le schéma Bearer n’est jamais accepté : il désigne un jeton de session ailleurs dans cette API, et les deux doivent rester distincts dans nos journaux.

Lire les éditions

Vos éditions, avec leurs catégories, phases, nominés et types de billets. Les mêmes données que la vitrine, jamais un instantané à part.

GET/public/editionsLister vos éditions
ChampNote
includeSéparés par des virgules : categories, phases, nominees, tickets ; ou vote, ticket, all. Défaut ici : none.
eventId / eventSlugNe garder que les éditions de cet évènement
statusdraft, active, completed, inactive
page, limitDéfaut 1 / 50 — limit plafonné à 200
Requête
GET /public/editions?status=active
X-API-Key: zk_votre_cle
Réponse
{
  "success": true,
  "message": "Éditions récupérées avec succès.",
  "data": [
    {
      "id": "c14f4e90-…",
      "slug": "tmpapi-festival/edition-2026",
      "name": "TMPAPI Festival - 2026",
      "status": "active",
      "country": "BJ",
      "currency": "XOF",
      "votePrice": 100,
      "startDate": "2026-08-19T23:05:08.477Z",
      "endDate": "2026-09-19T23:05:08.477Z",
      "coverUrl": null,
      "features": { "vote": true, "ticket": true, "ticketVote": true, "candidature": false, "donation": false },
      "event": { "id": "a72bb985-…", "name": "TMPAPI Festival", "slug": "tmpapi-festival" }
    }
  ],
  "meta": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
}

Toutes les adresses d’images sont absolues, domaine compris — couverture, bannière, photo d’un nominé, image d’un billet. Vous n’avez aucune page depuis laquelle résoudre une adresse relative.

GET/public/editions/{id|slug|évènement/edition-n}Une édition, avec son contenu
ChampNote
includeMêmes valeurs que ci-dessus. Défaut ici : vote
categoryIdNe garder que les nominés de cette catégorie
phaseIdLes nominés de cette phase plutôt que la phase en cours
groupByCategorytrue pour regrouper les nominés sous leur catégorie
Requête
GET /public/editions/tmpapi-festival/edition-2026?include=vote
X-API-Key: zk_votre_cle
Réponse
{
  "success": true,
  "data": {
    "id": "c14f4e90-…",
    "slug": "tmpapi-festival/edition-2026",
    "vote": {
      "phase": { "id": "654c7e8b-…", "name": "Phase unique", "order": 1 },
      "categories": [{ "id": "27494f30-…", "name": "TMPAPI Chant", "slug": "…/chant" }],
      "nominees": {
        "items": [
          {
            "id": "7d1e5322-…",
            "pseudo": "TMPAPI Alpha",
            "keyword": "tmpapi-alpha",
            "category": { "id": "27494f30-…", "name": "TMPAPI Chant" },
            "voteCount": 5
          }
        ],
        "pagination": { "page": 1, "limit": 50, "total": 3, "totalPages": 1 }
      }
    }
  }
}

Trois formes d’adresse acceptées, essayées dans l’ordre : l’identifiant, le slug de l’édition (deux segments, tmpapi-festival/edition-2026), ou le slug de l’évènement suivi de edition-{n}. Écrire l’adresse de mémoire fonctionne dans les deux cas.

voteCount suit le réglage « Afficher le résultat des votes » de l’édition : null = non communiqué, 0 = aucun vote — jamais confondus. Une catégorie ou une phase qui n’appartient pas à l’édition est refusée (400) plutôt que de rendre une liste vide qu’on lirait à tort comme « aucun nominé ».

Créer un paiement

Deux façons de créer une transaction — vote ou billet. Le montant n’est jamais soumis : il est calculé côté serveur depuis le prix de vote réel de l’édition et ses paliers.

POST/public/votesCréer une transaction de vote
ChampNote
nomineerequisLe mot-clé du nominé — pas son identifiant
voteCountrequisEntier ≥ 1
phoneNumberrequisNuméro du payeur
paymentMethodrequisVoir le catalogue plus bas
emailExigé pour carte ou PayPal
editionIdentifiant ou slug, pour départager un mot-clé porté par plusieurs éditions
country2 lettres, transmis à la passerelle (défaut BJ)
channelwebsite (défaut), mobile, whatsapp, autre
referralCodeCode de parrainage — absent ou inconnu, il est ignoré
Requête
POST /public/votes
X-API-Key: zk_votre_cle
Content-Type: application/json

{
  "nominee": "tmpapi-alpha",
  "voteCount": 5,
  "phoneNumber": "+22997000001",
  "paymentMethod": "mtn",
  "email": "[email protected]"
}
Réponse
{
  "success": true,
  "message": "Transaction de vote créée avec succès.",
  "data": {
    "flow": "vote",
    "transactionId": "eeac7363-…",
    "reference": "VOTE-20260820-18F719",
    "status": "pending",
    "amount": 500,
    "currency": "XOF",
    "voteCount": 5,
    "nominee": { "id": "7d1e5322-…", "keyword": "tmpapi-alpha", "pseudo": "TMPAPI Alpha" },
    "edition": { "id": "c14f4e90-…", "slug": "tmpapi-festival/edition-2026" },
    "redirectUrl": null,
    "gatewayMessage": "Veuillez consulter votre téléphone pour valider le paiement…"
  }
}

redirectUrl — à ouvrir quand le moyen de paiement passe par une page (carte, Wave, PayPal), null sur un paiement mobile. gatewayMessage — l’instruction exacte de l’opérateur, à afficher telle quelle au payeur. Le paiement n’est pas terminé à ce stade : il faut suivre son statut.

POST/public/ticketsAcheter un ou plusieurs billets
ChampNote
editionrequisIdentifiant ou slug
itemsrequisTableau de { ticket, quantity } — un ticket par type
phoneNumberrequisNuméro du payeur
paymentMethodrequisVoir le catalogue plus bas
emailExigé pour carte ou PayPal
country2 lettres (défaut BJ)
promoCodeCode de réduction, s'il y en a un
Requête
POST /public/tickets
X-API-Key: zk_votre_cle
Content-Type: application/json

{
  "edition": "c14f4e90-47d2-4f46-9a16-f3e6b96814da",
  "items": [
    { "ticket": "2fd99ec5-7df0-437d-b442-f1fcccc484e2", "quantity": 2 },
    { "ticket": "18580ad5-2857-4319-b8e9-76cfef9462de", "quantity": 1 }
  ],
  "phoneNumber": "+22997000002",
  "email": "[email protected]",
  "paymentMethod": "moov"
}
Réponse
{
  "success": true,
  "message": "Transaction de billet créée avec succès.",
  "data": {
    "flow": "ticket",
    "transactionId": "2071e56c-…",
    "reference": "TCK-20260820-D5477D",
    "status": "pending",
    "amount": 3000,
    "currency": "XOF",
    "quantity": 3,
    "items": [
      { "id": "86e3913c-…", "ticketType": "TMPAPI Standard", "amount": 500, "status": "pending" },
      { "id": "c7d9b2a8-…", "ticketType": "TMPAPI Standard", "amount": 500, "status": "pending" },
      { "id": "39c0b036-…", "ticketType": "TMPAPI VIP", "amount": 2000, "status": "pending" }
    ],
    "redirectUrl": null,
    "gatewayMessage": null
  }
}

Une ligne par billet, un seul paiement. Un panier de plusieurs unités crée autant de lignes — chacune son propre code à scanner à l’entrée — mais un seul transactionId, celui qu’il faut présenter à la vérification.

Moyens de paiement acceptés

Ils dépendent du réglage de passerelle de votre espace — opérateurs du pays du payeur plus carte/Wave/PayPal, ou le catalogue direct de la passerelle active. Le catalogue exact se lit toujours sur GET /api/v1/payment-simulation/methods.

Voter avec un billet

Le billet a déjà été payé : aucun paiement ici, son code est présenté une fois et accorde le nombre de votes que son type déclare.

POST/public/votes/by-ticket-codeAccorder des votes sans paiement
ChampNote
nomineerequisLe mot-clé du nominé, résolu comme sur /public/votes
coderequisLe code du billet, à usage unique
editionPour lever une ambiguïté de mot-clé
Requête
POST /public/votes/by-ticket-code
X-API-Key: zk_votre_cle
Content-Type: application/json

{ "nominee": "tmpapi-alpha", "code": "N7CW2UVJEZ" }
Réponse
{
  "success": true,
  "message": "Votes accordés avec succès grâce à votre ticket.",
  "data": {
    "flow": "vote",
    "transactionId": "bcd08eae-…",
    "reference": "VOTE-20260820-1F1BBD",
    "status": "approved",
    "voteCount": 3,
    "nominee": { "id": "7d1e5322-…", "keyword": "tmpapi-alpha", "pseudo": "TMPAPI Alpha" },
    "edition": { "id": "c14f4e90-…", "slug": "tmpapi-festival/edition-2026" },
    "amount": 0,
    "currency": "XOF"
  }
}

Se règle immédiatement (status: "approved") : rien à vérifier ensuite. Une seconde présentation du même code est refusée — la double dépense est fermée côté base de données, deux appels simultanés portant le même code n’en font aboutir qu’un.

Bac à sable

Éprouvez votre intégration sans jamais encaisser. Le mode se donne dans le corps de chaque appel, jamais dans un réglage : la même clé fait les deux, appel par appel, sans jamais rien couper.

Voter en bac à sable
{
  "mode": "sandbox",
  "nominee": "tmpapi-alpha",
  "voteCount": 5,
  "phoneNumber": "+22997000000",
  "paymentMethod": "mobile_money"
}

Sans ce champ, un appel encaisse pour de vrai — production est le défaut. Une simulation porte une référence SBX-…, vit dans sa propre table, et n’a aucun effet réel — aucun vote compté, aucun solde modifié, aucun billet capable d’ouvrir une porte.

Ce qui change entre les deux modes

ProductionBac à sable
RéférencesVOTE-…, TCK-…SBX-VOTE-…, SBX-TCK-…
redirectUrlpage de paiement, ou nulltoujours null
Bloc sandboxabsentprésent
Adresse de contrôle du billetservietoujours null
Total de votes du nominéservitoujours null

Toute seule, une simulation aboutit après trois vérifications — deux passages restent « en attente », le troisième tranche au sort (85 % d’approbation), exactement comme un vrai paiement dont l’issue arrive d’ailleurs. Vous pouvez aussi la fixer d’avance :

Choisir l'issue à la création
{ "mode": "sandbox", "desiredStatus": "approved", … }
ValeurCe que vous obtenez
approvedApprouvée d'emblée, avec sa référence d'opérateur et, pour un billet, son code.
canceledRefusée d'emblée, sans référence d'opérateur ni code de billet.
pendingEn attente pour toujours — le seul moyen d'éprouver un payeur qui ne valide jamais.
(absent)Comportement normal : deux vérifications en attente, la troisième tranche au sort.
POST/public/sandbox/transactions/{référence}/settleTrancher une simulation à la demande
ChampNote
statusrequisapproved ou canceled — jamais pending, c'est l'état de départ
Requête
POST /public/sandbox/transactions/SBX-VOTE-20260820-18F719/settle
X-API-Key: zk_votre_cle
Content-Type: application/json

{ "status": "approved" }
Réponse
// La réponse de GET /public/transactions/{référence}, avec le nouveau statut.

À employer avant la troisième vérification automatique : un paiement déjà tranché ne se rejoue pas. Cette route ne connaît que le bac à sable — une référence de production y est simplement introuvable.

Le bac à sable valide le contrat de l’API — formes, codes d’erreur, enchaînements. Il ne couvre que le vote et le billet, et n’exerce pas nos écritures internes (journal des soldes, commissions réellement portées) : c’est ce qu’un bac à sable doit faire, laisser développer contre l’API sans y encaisser un centime.

Suivi des paiements

Un paiement n’est jamais terminé au moment où il est créé : suivez-le jusqu’à ce que son statut cesse de valoir pending.

GET/public/transactions/{référence}Statut d'un paiement — fait avancer la vérification
Requête
GET /public/transactions/VOTE-20260820-18F719
X-API-Key: zk_votre_cle
Réponse
{
  "flow": "vote",
  "transactionId": "eeac7363-…",
  "reference": "VOTE-20260820-18F719",
  "externalReference": "SIM-0B0F710373E6",
  "status": "approved",
  "amount": 500,
  "currency": "XOF",
  "paymentMethod": "mtn",
  "edition": { "id": "c14f4e90-…", "slug": "tmpapi-festival/edition-2026" },
  "nominee": { "id": "7d1e5322-…", "pseudo": "TMPAPI Alpha", "keyword": "tmpapi-alpha" },
  "voteCount": 5,
  "nomineeVoteCount": 5,
  "createdAt": "2026-08-20T23:08:31.965Z",
  "settledAt": "2026-08-20T23:08:41.768Z"
}

Accepte n’importe laquelle des références que la création a rendues — transactionId, reference, ou celle de la passerelle. Cet appel interroge la passerelle et fait avancer le paiement — c’est lui qu’on rappelle en boucle après une création. Vote, billet, candidature et don s’y retrouvent tous ; candidature et don ne se créent pas par cette API mais restent suivables si vous les avez encaissés autrement.

GET/public/transactionsLister — pour un rapprochement comptable, sans rien faire avancer
ChampNote
flowvote, ticket, candidature, donation — défaut : les quatre
statuspending, approved, canceled — défaut : tous
editionIdentifiant ou slug
nomineeLe mot-clé — restreint au parcours vote
from, toDates ISO, bornes incluses
modesandbox pour lister vos simulations — absent : les vraies transactions
page, limitDéfaut 1 / 200 — limit plafonné à 200
Requête
GET /public/transactions?flow=vote&status=approved
X-API-Key: zk_votre_cle
Réponse
{
  "success": true,
  "data": [
    {
      "flow": "vote",
      "reference": "VOTE-20260824-18F719",
      "status": "approved",
      "amount": 500,
      "currency": "XOF",
      "paymentMethod": "mtn",
      "payer": { "phoneNumber": "+22990000000", "email": null },
      "edition": { "id": "c14f4e90-…", "slug": "tmpapi-festival/edition-2026" },
      "organizationAmount": 300,
      "voteCount": 5,
      "nominee": { "pseudo": "TMPAPI Alpha", "keyword": "tmpapi-alpha" }
    }
  ],
  "meta": { "page": 1, "limit": 200, "total": 4, "totalPages": 1 }
}

200 par page ici, contre 50 ailleurs dans cette API : on ne parcourt pas des transactions pour les lire une à une. Ce que la plateforme prélève (systemAmount, whiteLabelAmount) n’est servi qu’à une clé de super administrateur.

Statuts

ValeurTerminalSignification
pendingnonEn attente de confirmation par la passerelle de paiement.
approvedouiPayé et son effet appliqué — votes crédités, billet valide.
canceledouiRefusé ou abandonné par le payeur, aucun effet.

Codes d’erreur

Le catalogue complet, groupé par sujet — retrouvez-le pendant que vous débogez plutôt que de le mémoriser à l’avance.

Authentification

CodeHTTPCause
PUBLIC_API_MISSING_KEY401En-tête X-API-Key absent
PUBLIC_API_INVALID_KEY401Clé inconnue, ou organisation hors service

Lecture des éditions

CodeHTTPCause
PUBLIC_API_EDITION_NOT_FOUND404Identifiant/slug inconnu, ou d'une autre organisation
PUBLIC_API_CATEGORY_NOT_FOUND400categoryId hors de cette édition
PUBLIC_API_PHASE_NOT_FOUND400phaseId hors de cette édition

Créer un vote

CodeHTTPCause
PUBLIC_API_NOMINEE_NOT_FOUND404Mot-clé inconnu, ou d'une autre organisation
PUBLIC_API_NOMINEE_AMBIGUOUS409Mot-clé porté par plusieurs éditions — précisez edition
PAYMENT_VOTE_NOT_ACTIVE400Le vote n'est pas activé sur l'édition
PAYMENT_VOTE_NOT_STARTED400Les votes n'ont pas encore commencé
PAYMENT_EDITION_NOT_RUNNING400L'édition n'est pas « en cours »
PAYMENT_NOMINEE_INACTIVE400Le nominé est désactivé
PAYMENT_NOMINEE_PHASE_CLOSED400Le nominé appartient à une phase déjà close
PAYMENT_EMAIL_REQUIRED400Carte ou PayPal sans adresse e-mail
PAYMENT_GATEWAY_NOT_CONFIGURED400Aucune passerelle de paiement configurée

Acheter un billet

CodeHTTPCause
PAYMENT_TICKET_NOT_ACTIVE400La billetterie n'est pas activée sur l'édition
PAYMENT_UNKNOWN_TICKET400Type de billet inconnu de cette édition
PAYMENT_PROMO_CODE_EXPIRED400Code de réduction expiré ou inconnu

Voter avec un billet

CodeHTTPCause
PAYMENT_TICKET_VOTE_NOT_ACTIVE400Le vote par ticket n'est pas activé sur l'édition
PAYMENT_TICKET_CODE_NOT_FOUND400Code inconnu pour cette édition
PAYMENT_TICKET_CODE_NOT_APPROVED400Le billet n'est pas payé
PAYMENT_TICKET_CODE_ALREADY_REDEEMED400Ce code a déjà servi à voter
PAYMENT_TICKET_NO_VOTES_GRANTED400Ce type de billet n'accorde aucun vote

Bac à sable

CodeHTTPCause
PUBLIC_API_DESIRED_STATUS_SANDBOX_ONLY400desiredStatus soumis en production
PUBLIC_API_DESIRED_STATUS_NOT_APPLICABLE400desiredStatus sur /votes/by-ticket-code — rien à décider
PUBLIC_API_SANDBOX_ALREADY_SETTLED409Cette simulation a déjà été tranchée

Validation générale

CodeHTTPCause
VALIDATION_ERROR422Un champ mal orthographié — include, mode, flow, desiredStatus, dates
Une clé valide qui ne voit pas ce que vous attendez pointe presque toujours vers le cloisonnement : ce qui appartient à une autre organisation est introuvable, jamais interdit.

API héritée

Réservée aux intégrations déjà construites contre l’ancienne plateforme Zekin. Elle reproduit son contrat exact, à l’adresse qu’elle servait déjà — aucune ligne à changer côté appelant. Pour une intégration nouvelle, préférez l’API publique moderne : plus complète, avec son propre bac à sable.

Les deux API sont servies par le même serveur et lisent les mêmes données — elles ne diffèrent que par le contrat qu’elles respectent.

Adresse de base
https://api.zekin.me/api/request
Aucun mode bac à sable, aucune création de contenu (évènement, catégorie, candidat, retrait) : une clé qui fuite ne peut qu’engager des paiements que votre organisation encaisse elle-même.

Les huit routes, en un coup d’œil

MéthodeRouteAuthÀ quoi elle sert
GET/current/eventsouiÉvènements groupés par statut
GET, POST/event/:keywordouiDétail d'une édition — catégories, nominés, tarifs
POST/create/vote/:nomineeKeywordnonCréer une transaction de vote
POST/check/transactionouiStatut d'une transaction — fait avancer le paiement
POST/check/transactionsouiRecherche de transactions — sans effet
POST/authenticate/:eventIdouiConnexion par mot secret (portail nominé)
GET/laureate/:keyouiProfil d'un nominé
POST/laureate/history/:keyouiHistorique des votes d'un nominé

Authentification

La même clé que pour l’API publique moderne, transmise cette fois dans le schéma Bearer — celui que l’ancienne plateforme imposait, repris à l’identique.

En-tête
Authorization: Bearer <clé>
Toutes les routes exigent cette clé, sauf une : POST /create/vote/:nomineeKeyword est publique — c’est le geste que fait le navigateur du votant lui-même, pas votre intégration.

Format des réponses — différent de l’API moderne

Le contrat exact de l’ancienne plateforme, y compris ce qu’il a de particulier — jamais l’enveloppe { success, ... } du reste de ce projet.

Succès
{ "type": "success", "message": "...", "data": ... }
Refus — code HTTP réel, message littéral
{ "type": "error", "message": "Invalid laureate" }

Les messages sont des littéraux exacts de l’ancienne plateforme (Invalid laureate, Invalid event, Invalid secret code, Invalid API key, Get idTransaction or phone valid, Le mot clé est incorrect., Keyword conflict (Please use ID)) — jamais reformulés, certains étant cités mot pour mot par du code d’intégration existant.

Évènements & éditions

GET/current/eventsÉvènements groupés par statut
Requête
GET /current/events
Authorization: Bearer <clé>
Réponse
{
  "type": "success",
  "message": "Events retrieved",
  "data": {
    "Evènements en cours": [
      { "id": "a72bb985-…", "keyword": "tmpapi-festival-2026", "name": "TMPAPI Festival", "coverUrl": "https://…" }
    ],
    "Evènements programés": []
  }
}

Deux groupes seulement, aux intitulés recopiés au caractère près de l’ancienne plateforme, faute d’orthographe comprise. Un évènement terminé sort de la liste ; sa fiche reste atteignable par /event/:keyword.

Seule route bornée par votre clé, plutôt que par le domaine appelé : vous n’y voyez que les évènements de votre propre organisation — sauf si son propriétaire porte le rôle super administrateur, auquel cas c’est le catalogue complet du domaine qui est rendu.

Détail d’un évènement

GET/event/:keywordDétail d'une édition — catégories, nominés, tarifs
ChampNote
keywordrequisDans l'adresse. Identifiant d'édition, identifiant d'évènement, slug, ou mot-clé hérité — dans cet ordre
scopeDans le corps, POST uniquement — "active" masque catégories et nominés désactivés, absent ou toute autre valeur rend tout
Requête
POST /event/tmpapi-festival-2026
Authorization: Bearer <clé>
Content-Type: application/json

{ "scope": "active" }
Réponse
{
  "type": "success",
  "message": "Event retrieved",
  "data": {
    "id": "c14f4e90-…",
    "categories": [{ "id": "27494f30-…", "name": "TMPAPI Chant" }],
    "nominees": [{ "id": "7d1e5322-…", "pseudo": "TMPAPI Alpha", "score": 42.1 }],
    "packs": [{ "name": "Pack 20", "number": 20, "numberMax": 50, "amount": 75, "include": true, "descript": "…" }],
    "countryFlag": null
  }
}

GET n’a pas de corps — un simple GET /event/tmpapi-festival-2026 suffit, et rend tout (catégories et nominés désactivés compris). Un « event » hérité est une édition, pas un évènement. Le score d’un nominé (en pourcentage) se calcule par catégorie, pas sur l’édition entière — délibérément différent de la vitrine moderne. countryFlag vaut toujours null.

Créer un vote

POST/create/vote/:nomineeKeywordCréer une transaction de vote — route publique
ChampNote
phonerequisNuméro du payeur
processrequisMoyen de paiement (mtn, wave, card, paypal…), transmis tel quel
numberNombre de votes. Vide ou 0 → 1
emailFacultatif
canalFacultatif — website par défaut
bp, userId, fname, lnameAcceptés et ignorés (exigés par l'ancienne passerelle)
Requête
POST /create/vote/tmpapi-alpha
Content-Type: application/json
(aucune clé requise — route publique)

{
  "phone": "+22997000001",
  "process": "mtn",
  "number": 5
}
Réponse
{
  "type": "success",
  "message": "Vote transaction created",
  "data": {
    "refInterne": "VOTE-20260820-18F719",
    "gatewayMessage": "Veuillez consulter votre téléphone pour valider le paiement…"
  }
}

La réponse porte gatewayMessage — l’instruction exacte à afficher au payeur (« consultez votre téléphone… ») — et refInterne, à repasser à /check/transaction pour suivre le paiement.

Suivi des paiements

POST/check/transactionStatut d'une transaction — fait avancer le paiement
ChampNote
idTransactionrequisLe seul filtre accepté ici — pas de recherche par téléphone sur cette route
Requête
POST /check/transaction?idTransaction=VOTE-20260820-18F719
Authorization: Bearer <clé>
(corps vide)
Réponse
{ "type": "success", "message": "Transaction status", "data": { "status": 16, "amount": 500 } }

Le paramètre part en chaîne de requête, pas dans le corps JSON — une particularité de l’ancienne plateforme, reprise à l’identique (le corps est relu en repli si vous préférez l’y mettre). C’est la route qu’une page de statut rappelle en boucle jusqu’à ce que le paiement soit tranché. Sans filtre : Get idTransaction or phone valid (400).

POST/check/transactionsRecherche de transactions — sans effet de bord
ChampNote
idTransactionidTransaction ou phone, l'un des deux au moins
phoneidTransaction ou phone, l'un des deux au moins
startDateFacultatif — date ISO, borne basse
endDateFacultatif — date ISO, borne haute
Requête
POST /check/transactions?phone=22997000001&startDate=2026-01-01&endDate=2026-12-31
Authorization: Bearer <clé>
(corps vide)
Réponse
{ "type": "success", "message": "Transactions found", "data": [{ "status": 16, "amount": 500 }] }

Mêmes champs acceptés dans le corps JSON, en repli. Rend un tableau, sans jamais faire avancer aucun paiement — lister plusieurs transactions ne doit jamais trancher leur sort.

Portail nominé

POST/authenticate/:eventIdConnexion par mot secret
ChampNote
eventIdrequisDans l'adresse. Identifiant d'édition, ou mot-clé d'évènement/édition
passcoderequisDans le corps. Comparé sans tenir compte de la casse — secretWord est aussi accepté, comme nom de champ alternatif
Requête
POST /authenticate/tmpapi-festival-2026
Authorization: Bearer <clé>
Content-Type: application/json

{ "passcode": "fde23QA" }
Réponse
{ "type": "success", "message": "Authenticated", "data": { "id": "7d1e5322-…", "pseudo": "TMPAPI Alpha" } }

Un refus est muet sur sa cause : édition inconnue ou mot secret faux rendent le même message. Tentatives limitées, partagées avec le portail nominé de la plateforme.

GET/laureate/:keyProfil d'un nominé
ChampNote
keyrequisMot-clé, ou identifiant hérité (l'_id Mongo, conservé tel quel)
Requête
GET /laureate/tmpapi-alpha
Authorization: Bearer <clé>
Réponse
{ "type": "success", "message": "Laureate retrieved", "data": { "id": "7d1e5322-…", "pseudo": "TMPAPI Alpha", "score": 42.1 } }

Un mot-clé porté par plusieurs nominés de la même édition (doublons hérités) rend Keyword conflict (Please use ID) plutôt qu’un choix arbitraire.

POST/laureate/history/:keyHistorique des votes reçus
ChampNote
keyrequisDans l'adresse. Mot-clé, ou identifiant hérité
startFacultatif — date ISO, borne basse
endFacultatif — date ISO, borne haute
scopeAccepté et sans effet (l'historique servi ici n'est déjà que celui de ce nominé)
Requête
POST /laureate/history/tmpapi-alpha
Authorization: Bearer <clé>
Content-Type: application/json

{ "start": "2026-01-01", "end": "2026-12-31" }
Réponse
{
  "type": "success",
  "message": "History retrieved",
  "data": [
    { "phone": "+22994••••30", "amount": 100, "status": 16, "date": "2026-08-20T…" }
  ]
}

Votes validés et annulés seulement, jamais ceux en attente. Le numéro du votant est masqué (mêmes six chiffres visibles que le portail nominé).

StatutSignification
16Validé — voix comptée.
7Annulé — paiement refusé ou abandonné.
2En attente — jamais servi ici, un paiement non abouti n'est pas un fait.
Images — hors de /api/request, même contrat
GET https://api.zekin.me/public/{clé}

Sert les mêmes fichiers que /uploads/{clé}, sous un second préfixe — ce qui évite de faire apparaître le domaine de l’API dans votre propre HTML.

/stats/event/{id} n’est pas implémentée — inutilisable même sur l’ancienne plateforme, ne la cherchez pas.

Limites

Ce que cette API ne fait pas :

  • Aucune création de contenu — pas d’évènement, de catégorie, de candidat, de prix, de palier, de retrait.
  • Pas de mode bac à sable, contrairement à l’API publique moderne.
  • Pas de candidature ni de don — seulement le vote et, via /check/transaction(s), ce qui a déjà été encaissé.
Rien n’oblige une intégration existante à migrer. Pour une intégration nouvelle, préférez l’API publique moderne : authentification cohérente (X-API-Key), enveloppe homogène, bac à sable, et lecture des transactions.

Index des routes

Les neuf routes de l’API publique, en un coup d’œil.

MéthodeRouteÀ quoi elle sert
GET/public/meVérifier que la clé est valide
GET/public/editionsLister les éditions de l'organisation
GET/public/editions/{id|slug}Une édition, avec ses catégories, phases et nominés
POST/public/votesCréer une transaction de vote
POST/public/ticketsAcheter un ou plusieurs billets
POST/public/votes/by-ticket-codeAccorder des votes avec un billet déjà payé
GET/public/transactionsLister vos transactions — les 4 parcours réunis
GET/public/transactions/{référence}Suivre un paiement — fait avancer le statut
POST/public/sandbox/transactions/{référence}/settleBac à sable : trancher une simulation à la demande

Limites connues

Une bonne documentation dit aussi ce qu’elle ne fait pas :

  • Aucune limitation de débit propre à cette API pour l’instant.
  • Candidature et don ne peuvent pas être créés par cette API — ils sont lisibles et leur paiement suivable s’ils ont été encaissés autrement.
  • Aucun total agrégé sur la liste des transactions : elle rend les lignes, pas leur somme par statut ou par devise.
  • Le bac à sable ne couvre que le vote et le billet, les deux seuls parcours que cette API sait créer.
  • Rien ne distingue une clé « de test » d’une clé de production — le mode se donne appel par appel, pas par la clé.
  • Invitations, retraits et créances ne sont pas exposés par cette API.

Une question sur l’intégration ?

Notre équipe technique vous répond directement.

Contacter le support