Webhooks

Recevez une notification signée lorsqu’une génération de la Direct API se termine, vérifiez-la, puis lisez la génération pour en obtenir le détail.

Un webhook est un point de terminaison HTTPS qui vous appartient et que Morphic appelle lorsqu’une génération atteint un statut final. Les livraisons sont signées, retentées pendant environ une journée et journalisées. Chaque événement est une notification minimale : il indique quelle génération s’est terminée et comment, et vous lisez la génération avec votre clé pour obtenir ses assets, sa facturation et son message d’erreur.

Enregistrer un point de terminaison

POST /v1/direct/webhooks nécessite l’autorisation generations:write.

curl -s https://mcp.morphic.com/v1/direct/webhooks \
  -H "Authorization: Bearer $MORPHIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "https://hooks.example.com/morphic",
        "events": ["generation.succeeded", "generation.failed", "generation.cancelled"],
        "description": "Production order pipeline"
      }'
ChampRègles
urlHTTPS sur un hôte public (pas d’adresse IP littérale, pas d’adresse privée ou locale), jusqu’à 2048 caractères
eventsun ou plusieurs types d’événements
descriptionfacultatif, jusqu’à 200 caractères

La réponse est 201 :

{
  "webhook": {
    "id": "01929a10-0c3d-7f44-8a21-0b1c2d3e4f50",
    "url": "https://hooks.example.com/morphic",
    "events": ["generation.succeeded", "generation.failed", "generation.cancelled"],
    "description": "Production order pipeline",
    "created_at": "2026-10-25T08:00:00.000Z",
    "signing_secret": "whsec_4b1e…"
  }
}
  • signing_secret n’est affiché que dans cette réponse. Conservez-le comme vous le feriez pour la clé d’API.
  • Pour renouveler le secret, enregistrez un nouveau point de terminaison, faites passer vos appels sur son identifiant, puis supprimez l’ancien.
  • Une organisation peut détenir jusqu’à 10 points de terminaison.

Gérer les points de terminaison

RequêteAutorisationRôle
GET /v1/direct/webhooksassets:readliste les points de terminaison sous la forme { "webhooks": [ … ] }, jamais avec leurs secrets
DELETE /v1/direct/webhooks/{webhook_id}generations:writesupprime le point de terminaison ; les livraisons encore en attente pour celui-ci sont abandonnées

Associer un point de terminaison à un appel

Transmettez webhook_id dans le corps de l’appel d’outil. Chaque génération lancée par cet appel est livrée à ce point de terminaison lorsqu’elle atteint un statut final auquel le point de terminaison est abonné. Les appels sans webhook_id n’envoient aucun webhook.

Les outils qui effectuent le rendu vidéo sur les serveurs de Morphic (video_compose, les transformations video_* et burn_subtitles) n’acceptent pas encore webhook_id ; interrogez leurs générations par sondage.

Types d’événements

ÉvénementEnvoyé lorsque
generation.succeededla génération s’est terminée avec un résultat
generation.failedla génération a échoué ; le montant facturé est remboursé
generation.cancelledla génération a été annulée ; le montant facturé est remboursé
  • Chaque génération produit au plus un événement.
  • queued et in_progress ne sont pas livrés ; interrogez par sondage si vous en avez besoin.
  • Une génération est surveillée pendant 72 heures après son lancement. Une génération qui se termine plus tard n’envoie aucun événement : lisez-la plutôt.

La livraison

POST /morphic HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
User-Agent: Morphic-Webhooks/1.0
Morphic-Event-Id: 0192a7c0-5d4e-7b3a-8c2d-1e0f9a8b7c6d
Morphic-Delivery-Id: 0192a7c0-6e5f-7c4b-9d3e-2f1a0b9c8d7e
Morphic-Event-Type: generation.succeeded
Morphic-Signature: t=1792919741,v1=5f8a0c…e21b

{
  "id": "0192a7c0-5d4e-7b3a-8c2d-1e0f9a8b7c6d",
  "type": "generation.succeeded",
  "created_at": "2026-10-25T09:15:42.410Z",
  "api_version": "v1",
  "data": {
    "generation": {
      "object": "generation",
      "id": "0192a7b3-a1c4-7e02-8f9d-1a2b3c4d5e6f",
      "tool": "generate_video",
      "status": "succeeded",
      "completed_at": "2026-10-25T09:15:41.902Z",
      "error_code": null,
      "metadata": { "order_id": "4411" }
    }
  }
}

data.generation ne contient que ces champs :

ChampSignification
object, id, tool, status, completed_at, metadatacomme dans l’objet génération
error_codenull, ou le code d’une génération en failed

Un événement ne contient jamais de résultats, de liens, de facturation, de modèle ni de message d’erreur : une livraison qui arrive au mauvais endroit ou dans un journal ne révèle donc rien. À la réception, lisez la génération avec votre clé : GET /v1/direct/generations/{id}, ou la lecture groupée pour plusieurs. Au moment où un événement est envoyé, plus rien n’est attendu sur sa facturation : cette lecture est donc définitive.

En-têteSignification
Morphic-Event-Idl’identifiant de l’événement, identique à chaque nouvelle tentative ; dédupliquez sur cette valeur
Morphic-Delivery-Idcette tentative précise ; change à chaque nouvelle tentative
Morphic-Event-Typele type du corps
Morphic-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>

Un événement est généralement envoyé dans la minute environ qui suit l’arrivée de la génération à son statut final. Ce n’est pas un flux en temps réel : pour la latence la plus faible sur une génération que vous attendez, interrogez-la en long-polling.

Vérifier la signature

v1 est le HMAC-SHA256 en hexadécimal, avec pour clé le signing_secret de votre point de terminaison, de la chaîne {t}.{raw request body}.

  1. Découpez l’en-tête sur , pour obtenir t et v1.
  2. Calculez HMAC_SHA256(secret, t + "." + raw_body) sur les octets bruts reçus, avant toute analyse ou resérialisation JSON.
  3. Comparez-le à v1 en temps constant.
  4. Rejetez la livraison si t s’écarte de plus de 5 minutes de l’heure actuelle, pour empêcher les rejeux.
import crypto from 'node:crypto';
import express from 'express';

const app = express();

app.post('/morphic', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('Morphic-Signature') ?? '';
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto
    .createHmac('sha256', process.env.MORPHIC_WEBHOOK_SECRET)
    .update(`${parts.t}.${req.body}`)
    .digest('hex');

  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
  const valid =
    parts.v1?.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));

  if (!fresh || !valid) return res.sendStatus(400);

  const event = JSON.parse(req.body);
  enqueue(event); // process asynchronously, keyed by event.id
  res.sendStatus(204);
});

Un point de terminaison non vérifié est une porte ouverte : quiconque connaît son URL peut vous faire croire qu’une génération s’est terminée. Vérifiez chaque signature et rejetez tout horodatage de plus de 5 minutes.

Réponse et nouvelles tentatives

  • Répondez par n’importe quel 2xx dans les 10 secondes, puis effectuez le traitement.
  • Toute autre issue est une tentative échouée : un statut autre que 2xx, un délai dépassé, une connexion refusée, une erreur TLS.
  • Les redirections ne sont pas suivies ; un 3xx est une tentative échouée.
  • L’ordre n’est pas garanti et un événement peut arriver plusieurs fois. Rendez votre gestionnaire idempotent sur Morphic-Event-Id, ou sur data.generation.id, puisqu’une génération a au plus un événement.

Une tentative échouée est retentée après :

Nouvelle tentative1234567
Délai après la tentative précédente1 min5 min15 min1 h3 h6 h12 h

Après la 7e nouvelle tentative, environ 22 heures après la première tentative, l’événement est abandonné. La génération elle-même n’est pas affectée : lisez-la avec GET /v1/direct/generations/{id}.

Journal des livraisons

GET /v1/direct/webhooks/{webhook_id}/events (autorisation assets:read) renvoie les 20 événements les plus récents du point de terminaison, chacun avec le corps exact envoyé et l’issue de chaque tentative. Ajoutez ?generation_id=… pour obtenir les événements d’une seule génération.

{
  "webhook_events": [
    {
      "id": "0192a7c0-5d4e-…",
      "type": "generation.succeeded",
      "generation_id": "0192a7b3-…",
      "status": "delivered",
      "created_at": "2026-10-25T09:15:42.410Z",
      "payload": { "…": "the body that was sent" },
      "attempts": [
        { "id": "0192a7c0-6e5f-…", "attempted_at": "2026-10-25T09:15:42.900Z", "response_status": 204, "duration_ms": 131, "error": null, "response_body_excerpt": null }
      ]
    }
  ]
}
status de l’événementSignification
pendingpas encore livré ; d’autres tentatives peuvent suivre
deliveredvotre point de terminaison a répondu 2xx
abandonedla dernière tentative a échoué, ou le point de terminaison a été supprimé
skippedle point de terminaison n’était pas abonné à ce type d’événement

Les webhooks sont le signal le moins coûteux et le plus rapide, pas une garantie. Conservez un sondage occasionnel des générations dont vous n’avez pas eu de nouvelles, comme filet de sécurité.


Si vous rencontrez le moindre problème ou si vous avez des questions, n’hésitez pas à nous contacter. Nous serons ravis de vous aider. Écrivez-nous à support@morphic.com.

Sur cette page