Aller au contenu
API HILLS v1 Accès anticipé

Guides

Webhooks

Recevez les événements HILLS Music sur votre endpoint HTTPS : abonnement, 13 types d’événements, enveloppe, signature, relances et journal.

Mis à jour le

Les webhooks préviennent votre système quand quelque chose se produit dans HILLS Music : plus besoin d’interroger l’API en boucle. HILLS envoie un POST signé à votre endpoint HTTPS pour chaque événement auquel vous êtes abonné. Les webhooks sont fournis avec l’API dans les offres Enterprise et Custom.

S’abonner

Créez un endpoint avec POST /webhooks (scope webhooks:write). Vous pouvez aussi en ajouter un dans l’application web HILLS, sous Settings → Webhooks.

curl -X POST "https://api.hillsmusic.com/v1/webhooks" \
  -H "Authorization: Bearer $HILLS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hills/webhooks","description":"Order system","events":["release.approved","delivery.store_status_changed"]}'

La réponse (201) contient l’endpoint et son secret de signature, whsec_…. Le secret n’est affiché qu’une fois. Stockez-le avec vos autres secrets.

L’URL doit être en https:// sur le port 443, à une adresse publique. Les adresses privées, de bouclage, link-local et de métadonnées cloud sont refusées avec 400 invalid_url, à l’enregistrement de l’URL comme à chaque requête. Vous pouvez avoir jusqu’à 20 endpoints (au-delà, 409 too_many).

Toutes les requêtes liées aux endpoints webhook, avec leurs champs et leurs erreurs, figurent dans la référence Webhooks. GET /webhooks/events liste les types d’événements avec la signification de chacun.

Événements

Événement Envoyé quand
release.submitted Une sortie a été soumise à vérification.
release.approved Une sortie a été approuvée et attend d’être envoyée à ses plateformes.
release.changes_requested Une sortie a été renvoyée avec les corrections à apporter.
release.live Une sortie est passée en ligne sur sa première plateforme.
delivery.store_status_changed Le statut d’une sortie sur une plateforme a changé : en cours d’envoi, envoyée, en ligne, correction requise, retrait demandé ou retirée.
takedown.completed Toutes les plateformes d’une demande de retrait approuvée ont retiré la sortie.
ddex.import.report_ready Un test DDEX est terminé (dossier /test, application web ou POST /ddex/validate) ; son rapport est prêt.
ddex.import.imported Un lot DDEX livré en production a fini d’être importé : done, partial ou failed.
statement.published Le relevé de revenus d’un mois est prêt (en marque blanche, il vous est d’abord soumis pour vérification).
payout.status_changed Une demande de versement a été approuvée, envoyée, payée, a échoué ou a été annulée.
request.status_changed HILLS Music a répondu à une demande : approuvée, modifications nécessaires, terminée ou refusée.
chat.message HILLS Music vous a écrit dans le support en direct.
webhook.test Envoyé uniquement par POST /webhooks/{id}/test. Vous ne pouvez pas vous y abonner.

L’enveloppe

Chaque événement a la même structure externe :

{
  "id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
  "type": "release.approved",
  "api_version": "v1",
  "occurred_at": "2026-10-13T09:05:00Z",
  "account_id": 12,
  "data": { "release": { "id": 812, "title": "Midnight Run" } }
}
Champ Signification
id L’UUID de l’événement. Identique à chaque tentative : dédoublonnez sur cette valeur.
type Le type d’événement.
api_version v1. Au sein d’une version, des champs peuvent seulement être ajoutés.
occurred_at Le moment de l’événement, en ISO 8601 et en UTC.
account_id Votre compte client.
data Les données de l’événement. Leur forme dépend de type.

Chaque requête porte aussi X-Hills-Signature, X-Hills-Event (le type), X-Hills-Delivery (l’identifiant de la livraison, identique à chaque tentative), X-Hills-Attempt (1, 2, 3…), Content-Type: application/json et User-Agent: HILLS-Webhooks/1.0.

Un corps fait au plus 256 Ko. Un événement plus volumineux est envoyé avec data.truncated: true et seulement ses identifiants. Lisez le reste via l’API.

L’ordre d’arrivée des événements n’est pas garanti. Fiez-vous à occurred_at, et lisez l’état actuel via l’API quand l’ordre compte.

Vérifier la signature

X-Hills-Signature se présente sous la forme t=1792141500,v1=5257a869…. Pour la vérifier :

  1. Lisez t (secondes Unix) et v1 dans l’en-tête.
  2. Refusez la requête si t s’écarte de plus de 5 minutes de votre horloge. Cela bloque les rejeux.
  3. Calculez le HMAC-SHA256 de <t>.<raw body>, avec votre secret tel quel comme clé (préfixe whsec_ compris). Encodez-le en hexadécimal.
  4. Comparez-le à v1 en temps constant.

Utilisez les octets bruts du corps. Un JSON analysé puis resérialisé ne correspondra pas.

import crypto from "node:crypto";
import express from "express";

const SECRET = process.env.HILLS_WEBHOOK_SECRET; // whsec_…
const app = express();

app.post("/hills/webhooks", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-Hills-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=", 2)));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);

  const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${req.body}`).digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(parts.v1 ?? "", "hex");
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.sendStatus(400);

  const event = JSON.parse(req.body.toString("utf8"));
  // Dedupe on event.id: a retry after a lost answer sends the same event again.
  queue.push(event); // do the work later
  res.sendStatus(200); // answer within 10 seconds
});

app.listen(3000);

Répondre vite

Toute réponse 2xx en moins de 10 secondes est un succès. Tout le reste est un échec, y compris un délai dépassé ou une 3xx (les redirections ne sont pas suivies). Répondez d’abord, puis traitez l’événement dans une file d’attente.

Nouvelles tentatives

Une livraison qui échoue est retentée, jusqu’à 8 tentatives au total. Après chaque échec, la tentative suivante a lieu 1 minute, 5 minutes, 30 minutes, 2 heures, 12 heures, 12 heures et 12 heures plus tard : environ un jour et demi en tout. Après le 8ᵉ échec, la livraison est abandonnée, mais vous pouvez toujours la relancer manuellement.

Désactivation automatique

Un endpoint qui échoue 20 tentatives d’affilée, sans aucun succès entre-temps, est désactivé. Vous recevez une notification. Les livraisons en attente à ce moment-là sont abandonnées.

GET /webhooks/{id} en indique la raison : enabled: false, disabled_at et disabled_reason. Corrigez votre endpoint, puis réactivez-le :

curl -X PATCH "https://api.hillsmusic.com/v1/webhooks/7" \
  -H "Authorization: Bearer $HILLS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

Cela remet failure_count à zéro. Relancez ensuite ce qui a été abandonné.

Le journal des livraisons

GET /webhooks/{id}/deliveries liste ce qui a été envoyé au cours des 30 derniers jours, du plus récent au plus ancien : le payload, les tentatives, le dernier statut HTTP, l’heure et l’erreur. Filtrez avec status (pending, succeeded, failed) et event. La pagination se fait avec before. Voir Pagination.

Pour renvoyer une livraison, avec le même identifiant d’événement et le même payload :

curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/deliveries/1042/retry" \
  -H "Authorization: Bearer $HILLS_API_KEY"

La relance et le test renvoient 409 endpoint_disabled tant que l’endpoint est désactivé.

Renouveler le secret

POST /webhooks/{id}/roll-secret génère un nouveau secret. La réponse le contient, une seule fois. L’ancien secret cesse immédiatement de fonctionner : préparez la transition.

  1. Faites en sorte que votre vérificateur accepte une liste de secrets et les essaie l’un après l’autre.
  2. Renouvelez le secret et ajoutez le nouveau à la liste.
  3. Une fois que les livraisons passent avec le nouveau secret, retirez l’ancien.

Renouvelez-le sans attendre si le secret a fuité.

Envoyer un événement de test

POST /webhooks/{id}/test met en file d’attente un événement webhook.test pour l’endpoint, quels que soient les événements auxquels il est abonné. Il est signé comme n’importe quel autre événement, et son résultat apparaît dans le journal des livraisons.

curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/test" \
  -H "Authorization: Bearer $HILLS_API_KEY"
const res = await fetch("https://api.hillsmusic.com/v1/webhooks/7/test", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.HILLS_API_KEY}` },
});
const { delivery } = await res.json(); // 202; check GET /webhooks/7/deliveries for the outcome

Pour tester sur votre propre machine, utilisez un tunnel qui vous fournit une URL HTTPS publique.

Prêt à vous intégrer ?

Demandez l’accès à l’API : nous vous répondons sous 1–3 business days.