Webhooks

Reciba un aviso firmado cuando termine una generación de la Direct API, verifíquelo y lea la generación para obtener sus detalles.

Un webhook es un endpoint HTTPS suyo al que Morphic llama cuando una generación alcanza un estado final. Las entregas van firmadas, se reintentan durante aproximadamente un día y quedan registradas. Cada evento es un aviso mínimo: indica qué generación terminó y cómo, y usted lee la generación con su clave para obtener sus recursos, su facturación y su mensaje de error.

Registrar un endpoint

POST /v1/direct/webhooks necesita el permiso 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"
      }'
CampoReglas
urlHTTPS en un host público (sin direcciones IP literales ni direcciones privadas o locales), hasta 2,048 caracteres
eventsuno o más tipos de evento
descriptionopcional, hasta 200 caracteres

La respuesta es 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 solo se muestra en esta respuesta. Guárdelo como guardaría la clave de API.
  • Para rotar el secreto, registre un endpoint nuevo, cambie sus llamadas a su id y luego elimine el antiguo.
  • Una organización puede tener hasta 10 endpoints.

Gestionar endpoints

SolicitudPermisoQué hace
GET /v1/direct/webhooksassets:readenumera los endpoints como { "webhooks": [ … ] }, nunca con sus secretos
DELETE /v1/direct/webhooks/{webhook_id}generations:writeelimina el endpoint; las entregas aún pendientes hacia él se abandonan

Asociar un endpoint a una llamada

Pase webhook_id en el cuerpo de la llamada a la herramienta. Cada generación que inicia esa llamada se entrega a ese endpoint cuando alcanza un estado final al que el endpoint está suscrito. Las llamadas sin webhook_id no envían webhooks.

Las herramientas que renderizan video en los servidores de Morphic (video_compose, las transformaciones video_* y burn_subtitles) todavía no aceptan webhook_id; sondee sus generaciones.

Tipos de evento

EventoSe envía cuando
generation.succeededla generación terminó con salida
generation.failedla generación falló; su cobro se reembolsa
generation.cancelledla generación se canceló; su cobro se reembolsa
  • Cada generación produce como máximo un evento.
  • queued e in_progress no se entregan; si los necesita, obténgalos mediante sondeo.
  • Una generación se vigila durante 72 horas desde su inicio. Una que termine más tarde no envía ningún evento, así que léala directamente.

La entrega

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 solo lleva estos campos:

CampoSignificado
object, id, tool, status, completed_at, metadatacomo en el objeto de generación
error_codenull, o el código de una generación failed

Un evento nunca lleva salidas, enlaces, facturación, el modelo ni el mensaje de error, de modo que una entrega que acabe en el lugar equivocado o en un registro no revela nada. Al recibirla, lea la generación con su clave: GET /v1/direct/generations/{id}, o la lectura por lotes para varias. Cuando se envía un evento ya no queda nada pendiente en su facturación, así que esa lectura es definitiva.

EncabezadoSignificado
Morphic-Event-Idel id del evento, idéntico en cada reintento; deduplique por él
Morphic-Delivery-Ideste intento concreto; cambia en cada reintento
Morphic-Event-Typeel type del cuerpo
Morphic-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>

Un evento suele enviarse en torno a un minuto después de que la generación alcance su estado final. No es un flujo en tiempo real: para la menor latencia en una generación concreta que esté esperando, use un sondeo largo.

Verificar la firma

v1 es el HMAC-SHA256 en hexadecimal, con el signing_secret de su endpoint como clave, de la cadena {t}.{raw request body}.

  1. Divida el encabezado por , en t y v1.
  2. Calcule HMAC_SHA256(secret, t + "." + raw_body) sobre los bytes sin procesar que recibió, antes de cualquier análisis o reserialización del JSON.
  3. Compárelo con v1 en tiempo constante.
  4. Rechace la entrega si t se aleja más de 5 minutos de la hora actual, para impedir repeticiones.
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 endpoint sin verificar es una puerta abierta: cualquiera que conozca su URL puede decirle que una generación ha terminado. Verifique cada firma y rechace cualquier marca de tiempo con más de 5 minutos de antigüedad.

Respuestas y reintentos

  • Responda con cualquier 2xx en un plazo de 10 segundos y después haga el trabajo.
  • Cualquier otra cosa es un intento fallido: un estado distinto de 2xx, un tiempo de espera agotado, una conexión rechazada, un error de TLS.
  • No se siguen las redirecciones; un 3xx es un intento fallido.
  • El orden no está garantizado y un evento puede llegar más de una vez. Haga que su controlador sea idempotente respecto a Morphic-Event-Id, o a data.generation.id, ya que una generación tiene como máximo un evento.

Un intento fallido se reintenta tras:

Reintento1234567
Espera desde el intento anterior1 min5 min15 min1 h3 h6 h12 h

Tras el 7.º reintento, unas 22 horas después del primer intento, el evento se abandona. La generación en sí no se ve afectada: léala con GET /v1/direct/generations/{id}.

Registro de entregas

GET /v1/direct/webhooks/{webhook_id}/events (permiso assets:read) devuelve los 20 eventos más recientes del endpoint, cada uno con el cuerpo exacto enviado y el resultado de cada intento. Añada ?generation_id=… para ver los eventos de una sola generación.

{
  "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 del eventoSignificado
pendingaún no entregado; puede haber más intentos
deliveredsu endpoint respondió 2xx
abandonedel último reintento falló, o se eliminó el endpoint
skippedel endpoint no estaba suscrito a ese tipo de evento

Los webhooks son la señal más barata y más rápida, no una garantía. Mantenga un sondeo ocasional de las generaciones de las que no haya tenido noticias como red de seguridad.


Si tiene algún problema o alguna pregunta, no dude en ponerse en contacto con nosotros. Estaremos encantados de ayudarle. Escríbanos a support@morphic.com.

En esta página