Webhooks et alertes de prix
Recevez des callbacks HTTP signés lorsqu'un prix franchit votre seuil, et vérifiez leur authenticité avec HMAC-SHA256 avant de les traiter côté serveur.
Fonctionnement des alertes et des webhooks
MetalAPI envoie des webhooks lorsqu'une alerte de prix se déclenche. Une alerte surveille un symbole dans la devise de votre choix. Elle se déclenche lorsque le prix passe au-dessus ou en dessous d'un seuil, ou varie à la hausse ou à la baisse d'un certain pourcentage depuis la clôture précédente.
Les alertes se déclenchent au franchissement : vous êtes notifié une seule fois lorsque la condition devient vraie, et l'alerte se réarme une fois que la condition est redevenue fausse. Un délai de carence (60 minutes par défaut, configurable de 5 minutes à 7 jours) évite une rafale de notifications lorsqu'un prix oscille autour du seuil.
Les alertes vous préviennent par e-mail à partir du plan Lite, ou par webhook (un POST HTTPS vers votre serveur) à partir du plan Advanced.
Configurer un webhook
- Ouvrez Alerts dans le tableau de bord et créez une alerte.
- Choisissez le symbole, la devise, la condition et le seuil.
- Choisissez la livraison par webhook et saisissez l'URL de votre endpoint.
- Copiez votre secret de signature de webhook (il commence par
whsec_) depuis le tableau de bord dans la configuration de votre serveur.
Les URL de webhook doivent utiliser https:// sur un hôte public. Les URL qui contiennent des identifiants ou qui pointent vers des adresses IP privées ou internes sont refusées, et les redirections ne sont pas suivies.
Le payload
Chaque livraison est un POST JSON avec les en-têtes et le corps suivants :
POST /webhooks/metalapi HTTP/1.1
Content-Type: application/json
User-Agent: MetalAPI-Webhooks/1.0
X-MetalAPI-Timestamp: 1792765800
X-MetalAPI-Signature: t=1792765800,v1=3b9f0c4e…a71d
{
"event": "alert.triggered",
"id": 18342,
"created": 1792765800,
"alert": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"symbol": "XAU",
"base": "USD",
"condition": "above",
"threshold": 4000,
"note": "Gold above 4k"
},
"price": 4001.25,
"unit": "troy_oz"
}condition vaut above, below, change_pct_up ou change_pct_down. price est le prix qui a déclenché l'alerte, en base par unit. id est unique pour chaque événement : vous pouvez donc l'utiliser pour ignorer les livraisons en double.
Vérifier la signature
Vérifiez toujours qu'une requête provient bien de MetalAPI avant d'agir. L'en-tête X-MetalAPI-Signature a la forme t=<unix timestamp>,v1=<signature>. La signature est le HMAC-SHA256, encodé en hexadécimal, de la chaîne <timestamp>.<raw request body>, calculé avec votre secret de signature comme clé.
- Calculez le HMAC sur le corps brut, avant tout parsing JSON.
- Comparez les signatures en temps constant.
- Rejetez les horodatages de plus de cinq minutes pour bloquer les requêtes rejouées.
Avec Node.js et Express :
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.METALAPI_WEBHOOK_SECRET;
app.post("/webhooks/metalapi", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("X-MetalAPI-Signature") ?? "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const body = req.body.toString("utf8");
const expected = crypto.createHmac("sha256", SECRET).update(`${parts.t}.${body}`).digest("hex");
const valid =
typeof parts.v1 === "string" &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
if (!valid || !fresh) return res.status(400).send("invalid signature");
const event = JSON.parse(body);
console.log(event.alert.symbol, event.alert.condition, event.alert.threshold, event.price);
res.sendStatus(200);
});Vérification en PHP
La même vérification en PHP natif :
<?php
$secret = getenv('METALAPI_WEBHOOK_SECRET');
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_METALAPI_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);
$timestamp = (int) ($parts['t'] ?? 0);
$expected = hash_hmac('sha256', $timestamp . '.' . $payload, $secret);
if (!hash_equals($expected, $parts['v1'] ?? '') || abs(time() - $timestamp) > 300) {
http_response_code(400);
exit('invalid signature');
}
$event = json_decode($payload, true);
// Traitez $event['alert'] et $event['price'] ici.
http_response_code(200);Réponse et nouvelles tentatives
Renvoyez un statut 2xx dans un délai de 8 secondes. Effectuez les traitements longs après avoir répondu, par exemple dans une file d'attente, car les dépassements de délai et les réponses autres que 2xx comptent comme des livraisons échouées.
Les livraisons échouées sont relancées avec des délais croissants, jusqu'à trois tentatives au total dans l'heure qui suit le déclenchement de l'alerte. Une relance peut arriver après une livraison qui a expiré de notre côté mais réussi du vôtre : conservez donc l'id de l'événement et rendez votre traitement idempotent.
Guides associés
Prêt à commencer ?
Créez un compte gratuit pour obtenir votre clé API, ou explorez tous les endpoints dans la référence de l'API.