Intégration API et webhooks : connecter Predite à votre propre stack
L'essentiel de Predite vit dans le tableau de bord, mais les cas d'usage les plus intéressants surviennent quand vous arrêtez de cliquer pour commencer à scripter. Peut-être voulez-vous que les signaux EV soient acheminés vers un canal Discord à l'instant même où le scanner les détecte. Peut-être faites-vous tourner un bot hors plateforme et avez-vous besoin de vos positions Polymarket en direct au format JSON chaque minute. Peut-être voulez-vous qu'un déclenchement de stop-loss vous alerte, publie sur Slack et lance un workflow n8n qui ferme automatiquement la position. C'est précisément à cela que servent l'API Predite et le système de webhooks.
Ce guide parcourt toute la surface d'intégration de bout en bout : générer une clé API, appeler les endpoints REST avec une authentification Bearer, respecter les limites de débit, s'abonner aux événements webhook, vérifier les signatures de livraison avec HMAC-SHA256, et enfin assembler le tout dans un pipeline d'alertes sur mesure. Tout ce qui suit est réservé au plan Bot ($99/mois) — les plans Starter ($29) et Pro ($59) utilisent le tableau de bord et les notifications intégrées, mais l'accès programmatique (clés API et webhooks sortants) est restreint au plan Bot.
Générer une clé API
Predite utilise des clés API à longue durée de vie pour l'API REST. Elles sont rattachées à votre compte : n'importe quelle clé peut donc lire votre portefeuille et le flux de signaux, et rien d'autre — il n'existe aucun endpoint d'écriture, donc une clé compromise ne peut ni passer d'ordres ni déplacer de fonds.
- Ouvrez Réglages → API dans le tableau de bord. Si vous n'êtes pas sur le plan Bot, vous verrez une invitation à passer à l'offre supérieure plutôt que le gestionnaire de clés.
- Cliquez sur Créer une clé API et donnez-lui un libellé descriptif, par exemple
n8n-prodoudiscord-bot. Le libellé sert uniquement à votre propre organisation lorsque vous possédez plusieurs clés. - Predite génère la clé et vous l'affiche une seule fois. Copiez-la immédiatement et stockez-la dans un gestionnaire de secrets ou dans votre environnement.
Une clé ressemble à ceci :
``
pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
``
Le format est le préfixe pdt_live_ suivi de 32 caractères hexadécimaux — soit 40 caractères au total. Côté serveur, seul un hachage SHA-256 de la clé complète est conservé, ainsi que les 16 premiers caractères (pdt_live_ + 7 caractères) comme préfixe non secret, afin que le tableau de bord puisse vous afficher pdt_live_a1b2c3… dans la liste des clés sans jamais stocker le secret en clair. C'est aussi pour cette raison que nous ne pouvons pas vous réafficher la clé complète après sa création : littéralement, nous ne l'avons pas.
Quelques remarques opérationnelles :
- •La révocation d'une clé est une suppression logique. La ligne est conservée à des fins d'audit, le statut bascule sur
revoked, et toute requête qui l'utilise se met immédiatement à renvoyer 401. Effectuez la rotation en créant une nouvelle clé, en la déployant, puis en révoquant l'ancienne. - •Le tableau de bord suit
last_used_atainsi qu'un compteur de requêtes par clé, ce qui vous permet de repérer une clé devenue silencieuse (signe que votre intégration est cassée) ou une clé plus active que prévu (signe qu'elle a fuité). - •Traitez la clé comme un mot de passe. Ne la committez jamais, ne la placez jamais dans du JavaScript côté client, ne la collez jamais dans une capture d'écran. C'est pour cela que tous les exemples ci-dessous la lisent depuis une variable d'environnement.
Authentifier les requêtes
Chaque appel à l'API REST transporte la clé dans l'en-tête Authorization standard, sous forme de jeton Bearer :
``
Authorization: Bearer pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
``
Le serveur extrait le jeton, le hache, recherche la clé active correspondante, puis compare les hachages. Si l'en-tête est absent, mal formé, pointe vers une clé révoquée, ou si la clé a expiré, vous obtenez :
``json
{ "error": "invalid_api_key" }
``
avec le statut HTTP 401. Il n'y a aucune étape de connexion distincte ni rafraîchissement de jeton — la clé est l'identifiant. Définissez-la une fois dans votre client et c'est terminé.
Les endpoints REST
L'API v1 est délibérément réduite et en lecture seule. Deux endpoints couvrent les données dont la plupart des intégrations ont réellement besoin : votre portefeuille et le flux de signaux. Les deux se trouvent sous /api/v1/ et renvoient tous deux du JSON accompagné d'un horodatage ISO generated_at, pour que vous puissiez savoir à quel point une réponse est récente.
GET /api/v1/portfolio
Renvoie vos positions en direct et un résumé du portefeuille, lus depuis le portefeuille Polymarket que vous avez connecté dans Réglages → Plateformes.
``bash
curl -s https://app.predite.io/api/v1/portfolio \
-H "Authorization: Bearer $PREDITE_API_KEY"
``
Une réponse typique :
``json
{
"wallet": "0x3a1f...8c2d",
"positions": [
{
"market": "Will the Fed cut rates in July 2026?",
"outcome": "Yes",
"shares": 420.0,
"avg_price": 0.61,
"current_price": 0.68,
"value": 285.6,
"unrealized_pnl": 29.4
}
],
"summary": {
"total_value": 4128.55,
"unrealized_pnl": 312.10,
"open_positions": 7
},
"generated_at": "2026-06-01T14:32:08.114Z"
}
``
Si vous n'avez pas encore connecté de portefeuille, vous obtiendrez { "positions": [], "summary": null, "error": "no_wallet_connected" } avec un statut 200 — vérifiez donc la présence du champ error plutôt que de supposer qu'un 200 signifie qu'il y a des données. L'endpoint lit les mêmes données de position on-chain que le tableau de bord, donc le P&L ici correspond à ce que vous voyez dans l'interface.
GET /api/v1/signals
Renvoie les meilleurs signaux EV (expected value, ou valeur attendue) du scanner de marchés — le même moteur qui alimente le flux de signaux du tableau de bord. Chaque signal compare le prix de marché actuel au prix juste estimé par l'IA de Predite et indique l'edge en points de pourcentage.
Il accepte deux paramètres de requête :
- •
limit— le nombre de signaux à renvoyer, de 1 à 100, par défaut 20. - •
min_edge— l'edge minimum en points de pourcentage, par défaut 5. Utilisez-le pour filtrer les signaux marginaux.
``bash
curl -s "https://app.predite.io/api/v1/signals?limit=10&min_edge=8" \
-H "Authorization: Bearer $PREDITE_API_KEY"
``
``json
{
"signals": [
{
"market_title": "Will Bitcoin close above $120k on 2026-06-30?",
"direction": "YES",
"edge_pp": 11.4,
"market_price": 0.42,
"ai_price": 0.534,
"created_at": "2026-06-01T14:05:00.000Z"
}
],
"count": 1,
"filters": { "min_edge": 8, "limit": 10 },
"generated_at": "2026-06-01T14:32:40.781Z"
}
``
Ici, un edge_pp de 11,4 signifie que le prix juste estimé par l'IA (0,534) se situe 11,4 points au-dessus du prix de marché (0,42) côté YES — exactement le type d'écart que recherche un trader EV. Les résultats sont triés par edge décroissant, puis par ordre de récence. Le bloc filters renvoie ce qui a été appliqué, ce qui vous permet de confirmer que vos paramètres ont bien été interprétés.
Un client Python
Voici un petit wrapper qui gère l'authentification et les deux endpoints. Il utilise requests, mais sa structure se transpose à n'importe quel client HTTP.
```python import os import requests
BASE = "https://app.predite.io/api/v1" KEY = os.environ["PREDITE_API_KEY"] SESSION = requests.Session() SESSION.headers["Authorization"] = f"Bearer {KEY}"
def get_portfolio(): r = SESSION.get(f"{BASE}/portfolio", timeout=10) r.raise_for_status() return r.json()
def get_signals(limit=20, min_edge=5.0): params = {"limit": limit, "min_edge": min_edge} r = SESSION.get(f"{BASE}/signals", params=params, timeout=10) r.raise_for_status() return r.json()
if __name__ == "__main__": for s in get_signals(limit=5, min_edge=10)["signals"]: print(f"{s['edge_pp']:+.1f}pp {s['direction']:>3} {s['market_title']}") ```
Réutiliser une même Session maintient la connexion ouverte d'un appel à l'autre, ce qui compte dès lors que vous interrogez l'API selon un horaire régulier.
Limites de débit
L'API autorise 120 requêtes par minute par clé, et ce plafond est le même sur tous les plans. C'est généreux pour la charge en lecture seule à laquelle elle est destinée : interroger le portefeuille une fois par minute en consomme moins de 1 %, laissant largement de quoi vérifier les signaux. Au-delà, l'appel renvoie 429 avec le plafond dans le corps — levez le pied plutôt que de réessayer aussitôt.
Quelques habitudes vous maintiennent confortablement sous la limite :
- •N'interrogez pas plus vite que nécessaire. Les signaux EV se rafraîchissent au rythme du cron du scanner, et non en continu : interroger
/signalstoutes les quelques secondes ne fait donc que consommer du quota en renvoyant des données identiques. Une fois par minute suffit largement ; une fois toutes les quelques minutes est souvent amplement suffisant. - •Privilégiez les webhooks pour les événements. Si vous interrogez l'API pour détecter *qu'un événement s'est produit* — un whale a bougé, un stop-loss s'est déclenché — vous utilisez le mauvais outil. Les webhooks (ci-dessous) vous les transmettent à l'instant même où ils surviennent, sans aucun coût d'interrogation.
- •Mettez en cache à l'échelle d'un cycle. Si trois parties de votre pipeline ont besoin du portefeuille, récupérez-le une fois et faites-le circuler, plutôt que d'appeler l'API trois fois.
- •Ralentissez en cas d'erreur. Si vous êtes un jour limité, prenez-le comme un signal qu'il faut ralentir, et non réessayer dans une boucle serrée.
Le modèle mental : utilisez l'API REST pour récupérer l'état à la demande, et utilisez les webhooks pour réagir aux événements. Confondre les deux est la cause la plus fréquente de quota gaspillé.
Abonnements aux webhooks
Les webhooks inversent le sens. Au lieu que ce soit vous qui demandiez à Predite « du nouveau ? », c'est Predite qui appelle *votre* URL à l'instant où quelque chose se produit. Comme la charge utile est du JSON brut envoyé via HTTPS POST, tout ce qui peut recevoir une requête HTTP fait office de destination : les webhooks entrants Discord et Slack, les plateformes d'automatisation comme n8n et Zapier, ou votre propre serveur.
Créer un abonnement
- Allez dans Réglages → Webhooks (plan Bot requis).
- Cliquez sur Ajouter un webhook et collez l'URL de votre destination. En production, elle doit être en
https://— le HTTP simple est rejeté. - Donnez-lui un libellé et sélectionnez les types d'événements qu'il doit recevoir. Un abonnement doit écouter au moins un événement.
- Enregistrez. Predite génère un secret de signature et l'affiche dans la vue détaillée de l'abonnement.
Le secret ressemble à ceci :
``
whsec_4f8c...d2a1
``
(le préfixe whsec_ suivi de 64 caractères hexadécimaux). Vous en aurez besoin pour vérifier les livraisons — stockez-le avec la destination, idéalement sous forme de variable d'environnement sur le service récepteur.
Types d'événements
Six types d'événements sont abonnables, et les six sont livrés aujourd'hui :
- •`ev_signal` — le scanner a trouvé une nouvelle opportunité d'EV au-dessus du seuil.
- •`arb_opportunity` — un écart d'arbitrage est apparu entre Polymarket et Kalshi sur des marchés équivalents.
- •`stop_loss_triggered` — l'une de vos protections a franchi son seuil et s'est déclenchée.
- •`bot_trade_executed` — l'un de vos bots a ouvert ou clôturé un trade.
- •`whale_move` — un portefeuille suivi a pris une position de 25 000 $ ou plus. Événement global : chaque abonné le reçoit, et chaque mouvement est dédupliqué pour ne jamais arriver deux fois.
- •`resolution_imminent` — un marché *que vous détenez* est entré dans les 24 heures avant sa résolution. Par utilisateur, et envoyé une seule fois par marché.
Les deux derniers étaient abonnables mais muets : leurs sources sont de simples requêtes, donc sans mémoire de ce qui a déjà été envoyé, un cron réémettrait les mêmes événements à chaque passage. Cette mémoire existe désormais — c'est ce qui les a rendus livrables.
Abonnez chaque destination uniquement aux événements qui la concernent. Votre pager d'astreinte ne veut probablement que stop_loss_triggered ; un canal de recherche pourrait vouloir whale_move et ev_signal ; un flux d'automatisation qui rééquilibre pourrait vouloir bot_trade_executed.
Charge utile et en-têtes de livraison
Chaque livraison est une requête HTTP POST avec un corps JSON structuré comme ceci :
``json
{
"event": "ev_signal",
"timestamp": "2026-06-01T14:33:12.004Z",
"data": {
"market_title": "Will the ECB hold rates in June 2026?",
"direction": "NO",
"edge_pp": 9.2,
"market_price": 0.71,
"ai_price": 0.618
}
}
``
Le champ event vous indique le type, timestamp correspond au moment où Predite l'a expédié, et data est la charge utile propre à l'événement. En plus du corps, Predite envoie ces en-têtes :
- •
X-Predite-Event— le type d'événement, pour que vous puissiez router sans analyser le corps. - •
X-Predite-Timestamp— l'heure d'expédition (identique autimestampdu corps). - •
X-Predite-Signature— la signature HMAC-SHA256, au formatsha256=<hex>. - •
User-Agent: Predite-Webhooks/1.0.
Votre endpoint doit répondre rapidement avec n'importe quel statut 2xx. La livraison expire au bout de 5 secondes : accusez donc réception rapidement et effectuez les traitements lourds de façon asynchrone — renvoyez 200, puis traitez.
Comportement de fiabilité
Côté Predite, la livraison n'est pas du « tire-et-oublie ». Les livraisons échouées (réponse non-2xx, expiration ou erreur réseau) sont journalisées, et les échecs transitoires de type 5xx/réseau sont réessayés avec un backoff exponentiel. Si un abonnement cumule 10 échecs consécutifs, Predite le désactive automatiquement (statut disabled_by_failures) afin de cesser de marteler un endpoint mort. Vous verrez le motif de l'échec dans Réglages → Webhooks, et réactiver l'abonnement réinitialise le compteur d'échecs. Chaque abonnement suit également le dernier succès, le dernier échec et le total des livraisons, ce qui vous permet d'auditer sa santé d'un coup d'œil.
Vérifier les signatures de webhook
Comme votre URL de webhook n'est qu'un simple endpoint HTTP, quiconque la découvre pourrait y envoyer de faux événements en POST. L'en-tête de signature est le moyen de prouver qu'une livraison provient bien de Predite et n'a pas été altérée en transit.
Le mécanisme est simple : Predite calcule HMAC-SHA256(raw_request_body, your_secret) et envoie le condensé hexadécimal dans X-Predite-Signature sous la forme sha256=<digest>. De votre côté, vous recalculez le même HMAC sur le corps brut, non analysé, puis vous comparez. La partie « corps brut » est importante — si votre framework analyse le JSON et que vous le re-sérialisez, les espaces et l'ordre des clés peuvent différer, et la signature ne correspondra pas. Capturez les octets avant l'analyse.
Une comparaison à temps constant en Python :
```python import hashlib import hmac
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool: if not signature_header or not signature_header.startswith("sha256="): return False sent = signature_header.split("=", 1)[1] expected = hmac.new( secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(sent, expected) ```
Un récepteur Flask minimal qui rejette tout ce qui n'est pas signé :
```python import os from flask import Flask, request, abort
app = Flask(__name__) SECRET = os.environ["PREDITE_WEBHOOK_SECRET"]
@app.post("/predite-webhook") def receiver(): raw = request.get_data() # raw bytes, before JSON parsing if not verify(raw, request.headers.get("X-Predite-Signature", ""), SECRET): abort(401) payload = request.get_json() event = payload["event"] # route on event, then return fast handle(event, payload["data"]) return "", 200 ```
Utilisez toujours une comparaison à temps constant (hmac.compare_digest, et non ==) afin de ne pas divulguer les octets de la signature par effet de timing. Et rejetez la requête avant tout traitement réel si la vérification échoue — cette unique vérification fait toute la différence entre un pipeline de confiance et une porte ouverte.
Construire un pipeline d'alertes sur mesure
Attachons tout cela avec un exemple concret : vous voulez que chaque signal EV à forte conviction et chaque déclenchement de stop-loss atterrissent dans Slack, et vous voulez en outre que les stop-loss déclenchent un workflow n8n qui enregistre l'événement et (éventuellement) ferme la position via votre propre intégration de courtier.
- Créez deux abonnements webhook dans Réglages → Webhooks. L'un pointe vers l'URL de votre webhook entrant Slack et s'abonne à
ev_signal. L'autre pointe vers votre nœud webhook n8n et s'abonne àstop_loss_triggered. Chacun reçoit son propre secret. - Pour Slack, l'approche la plus propre est un petit relais (une fonction serverless fait très bien l'affaire) : il vérifie la signature Predite, met en forme
dataen un message Slack, et le publie vers l'URL de Slack. Le relais vous permet de filtrer — par exemple, ne transmettre que les signaux oùdata.edge_pp >= 10— afin de ne pas saturer le canal. Les marchés au faible edge sont écartés ; seuls les plus forts alertent l'équipe. - Pour n8n, placez d'abord un nœud de déclenchement Webhook, puis un nœud Function qui exécute la même vérification HMAC sur le corps brut avant que quoi que ce soit en aval ne s'exécute. Après vérification, branchez selon
datapour journaliser l'événement et appeler votre courtier. Zapier suit le même schéma avec une étape Code qui effectue la vérification. - Ajoutez l'API REST pour le contexte. Lorsqu'un événement
stop_loss_triggeredarrive, votre flux n8n peut immédiatement appelerGET /api/v1/portfolioavec votre clé API pour récupérer l'ensemble complet des positions actuelles, de sorte que l'alerte soit enrichie du P&L en direct plutôt que des seules données du déclenchement. C'est le schéma de lecture à la demande qui fonctionne aux côtés du schéma push. - Rendez l'ensemble idempotent. Les réseaux réessaient ; une même livraison peut arriver deux fois. Indexez vos actions en aval sur quelque chose de stable (l'événement et son timestamp, ou un identifiant à l'intérieur de
data) afin qu'une livraison en double n'agisse pas deux fois. - Surveillez le surveillant. Vérifiez régulièrement Réglages → Webhooks pour repérer les
disabled_by_failures, et faites en sorte que votre relais journalise chaque signature rejetée — un pic soudain signifie soit que votre secret a été renouvelé, soit que quelqu'un sonde votre endpoint.
Une fois cela en place, vous disposez d'une boucle fermée : le scanner et le moteur de risque de Predite poussent les événements à l'instant où ils se déclenchent, vos relais les vérifient et les filtrent, et vos appels REST complètent l'état en direct à la demande — le tout sans la moindre boucle d'interrogation qui consomme du quota.
Si vous êtes sur le plan Bot, le moyen le plus rapide de ressentir le déclic est de vous rendre dans Réglages → Webhooks, de pointer un abonnement de test vers un canal Discord jetable, de l'abonner à ev_signal, et de regarder le premier vrai signal arriver sous forme de POST JSON — branchez ensuite l'extrait de vérification ci-dessus et vous n'êtes plus qu'à un relais de la production.