Aller au contenu
Guides
Guides

Rappels

Recevoir et vérifier de manière sécurisée les livraisons de callbacks de Task.

Utilisez les callbacks pour recevoir les événements du cycle de vie des Tasks à un point de terminaison HTTPS public. Vérifiez chaque callback avant de traiter son corps JSON afin que seules les livraisons signées pour votre compte puissent modifier l’état de l’application.

Configurer une URL de callback

Ajoutez un callback_url HTTPS public lorsque vous créez une tâche. Le corps de l’événement et les états du cycle de vie dépendent du point de terminaison ; consultez la référence API de ce point de terminaison pour connaître ses charges utiles de rappel.

JSON
{
  "model": "flux-2-pro-text-to-image",
  "prompt": "A product photograph on a clean studio background",
  "callback_url": "https://your-domain.com/webhooks/runapi"
}

Créer un Callback Secret

Suivez le Guide d’authentification pour vous connecter, puis ouvrez Clés API et créez un secret de rappel pour le compte qui crée la tâche. Stockez la valeur dans un gestionnaire de secrets et rendez-la disponible uniquement pour votre récepteur de rappels. Ce n’est pas une clé API et elle ne doit jamais être envoyée dans une requête de tâche.

Le secret signe les callbacks pour ce compte. Le faire pivoter modifie la signature pour les livraisons ultérieures ; mettez donc à jour immédiatement chaque récepteur de callback et gardez les deux valeurs disponibles seulement le temps nécessaire pour traiter les livraisons en cours.

Vérifier une signature de callback

Chaque rappel est un HTTP POST avec Content-Type: application/json. RunAPI n’ajoute pas d’en-tête Authorization à cette requête. Vérifiez ces en-têtes avant de désérialiser le corps :

En-tête Signification X-Callback-Id Identifiant unique de cette tentative de livraison. X-Callback-Timestamp Horodatage Unix en secondes au moment où la livraison a été signée. X-Callback-Signature Signature HMAC-SHA-256 encodée en Base64.

Construisez la valeur signée exactement comme suit, en utilisant les octets du corps de la requête non modifiés :

TEXT
X-Callback-Id + "." + X-Callback-Timestamp + "." + raw request body

Décodez en Base64 le Callback Secret, calculez un HMAC-SHA-256 sur cette valeur, encodez le résultat en Base64 et comparez-le avec X-Callback-Signature à l’aide d’une comparaison à temps constant. Ne parsez pas et ne re-sérialisez pas le JSON avant de le vérifier.

JavaScript

JAVASCRIPT
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCallback({headers, rawBody, callbackSecret}) {
  const callbackId = headers["x-callback-id"];
  const timestamp = Number(headers["x-callback-timestamp"]);
  const signature = headers["x-callback-signature"];

  if (!callbackId || !signature || !Number.isSafeInteger(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const signedContent = `${callbackId}.${timestamp}.${rawBody}`;
  const expected = createHmac("sha256", Buffer.from(callbackSecret, "base64"))
    .update(signedContent, "utf8")
    .digest();
  const received = Buffer.from(signature, "base64");

  return expected.length === received.length && timingSafeEqual(expected, received);
}

Transmettez la chaîne brute du corps de la requête du framework à rawBody ; n’appelez pas JSON.stringify sur des données déjà analysées.

Python

PYTHON
import base64
import hashlib
import hmac
import time


def verify_callback(headers, raw_body, callback_secret):
    callback_id = headers.get("X-Callback-Id")
    timestamp = headers.get("X-Callback-Timestamp")
    signature = headers.get("X-Callback-Signature")

    if not callback_id or not timestamp or not signature:
        return False

    try:
        timestamp = int(timestamp)
        secret = base64.b64decode(callback_secret, validate=True)
        received = base64.b64decode(signature, validate=True)
    except (ValueError, TypeError):
        return False

    if abs(time.time() - timestamp) > 300:
        return False

    signed_content = f"{callback_id}.{timestamp}.{raw_body}".encode("utf-8")
    expected = hmac.new(secret, signed_content, hashlib.sha256).digest()
    return hmac.compare_digest(expected, received)

Transmettez la chaîne brute exacte du corps de la requête reçue par votre framework HTTP en tant que raw_body.

Traiter les livraisons en toute sécurité

  • Ne renvoyez une réponse 2xx qu’après que le callback a été accepté pour traitement. Après une réponse non-2xx ou un échec de transport, la livraison est tentée jusqu’à 10 fois.
  • Répondez dans les 15 secondes. Placez les traitements plus lents en file d’attente après la vérification plutôt que de bloquer la réponse HTTP.
  • Rejetez les callbacks dont les en-têtes de signature sont absents, dont la signature est invalide, ou dont l’horodatage est en dehors de la fenêtre de tolérance définie par votre récepteur.

Résoudre les problèmes de vérification

  • Discordance de signature : confirmez que le Secret de rappel appartient au même compte que la tâche, décodez-le avec Base64 et signez le corps original plutôt que le JSON parsé.
  • En-têtes de signature manquants : créez un Secret de rappel avant de vous fier aux rappels pour les changements d’état.
  • Horodatage rejeté : synchronisez l’horloge du récepteur et utilisez une tolérance appropriée à votre déploiement.

Pour les conseils de configuration et de cycle de vie, revenez au démarrage rapide de l’API Task .