Webhooks
Receba um aviso assinado quando uma geração da Direct API terminar, verifique-o e leia a geração para obter os detalhes.
Um webhook é um endpoint HTTPS seu que o Morphic chama quando uma geração chega a um status final. As entregas são assinadas, repetidas por cerca de um dia e registradas. Cada evento é um aviso enxuto: ele diz qual geração terminou e como, e você lê a geração com a sua chave para obter os recursos, a cobrança e a mensagem de erro.
Registrar um endpoint
POST /v1/direct/webhooks exige o escopo 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"
}'| Campo | Regras |
|---|---|
url | HTTPS em um host público (sem IPs literais, sem endereços privados ou locais), até 2.048 caracteres |
events | um ou mais tipos de evento |
description | opcional, até 200 caracteres |
A resposta é 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_secretaparece apenas nesta resposta. Guarde-o como você guarda a chave de API.- Para rotacionar o segredo, registre um novo endpoint, passe a usar o ID dele nas suas chamadas e depois exclua o antigo.
- Uma organização pode ter até 10 endpoints.
Gerenciar endpoints
| Solicitação | Escopo | O que faz |
|---|---|---|
GET /v1/direct/webhooks | assets:read | lista os endpoints como { "webhooks": [ … ] }, nunca com seus segredos |
DELETE /v1/direct/webhooks/{webhook_id} | generations:write | remove o endpoint; as entregas ainda pendentes para ele são abandonadas |
Associar um endpoint a uma chamada
Passe webhook_id no corpo da chamada da ferramenta. Cada geração que essa chamada iniciar é entregue a esse endpoint quando chegar a um status final ao qual o endpoint se inscreveu. Chamadas sem webhook_id não enviam webhooks.
As ferramentas que renderizam vídeo nos servidores da Morphic (video_compose, as transformações video_* e burn_subtitles) ainda não aceitam webhook_id; consulte as gerações delas por polling.
Tipos de evento
| Evento | Enviado quando |
|---|---|
generation.succeeded | a geração terminou com um resultado |
generation.failed | a geração falhou; a cobrança é reembolsada |
generation.cancelled | a geração foi cancelada; a cobrança é reembolsada |
- Cada geração produz no máximo um evento.
queuedein_progressnão são entregues; consulte por polling se precisar deles.- Uma geração é acompanhada por 72 horas após o início. Uma que termine depois disso não envia evento, então leia-a diretamente.
A 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 traz apenas estes campos:
| Campo | Significado |
|---|---|
object, id, tool, status, completed_at, metadata | como no objeto de geração |
error_code | null, ou o código de uma geração failed |
Um evento nunca traz resultados, links, cobrança, o modelo ou a mensagem de erro, então uma entrega que chegue ao lugar errado ou acabe em um log não revela nada. Ao recebê-la, leia a geração com a sua chave: GET /v1/direct/generations/{id}, ou a leitura em lote para várias. Quando um evento é enviado, não há mais nada pendente na cobrança dele, então essa leitura é final.
| Cabeçalho | Significado |
|---|---|
Morphic-Event-Id | o ID do evento, idêntico em todas as novas tentativas; elimine duplicatas por ele |
Morphic-Delivery-Id | esta tentativa específica; muda a cada nova tentativa |
Morphic-Event-Type | o type do corpo |
Morphic-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256> |
Um evento costuma ser enviado em cerca de um minuto depois que a geração chega ao status final. Não é um stream em tempo real: para a menor latência em uma geração que você está aguardando, faça long-poll dela.
Verificar a assinatura
v1 é o HMAC-SHA256 em hexadecimal, com o signing_secret do seu endpoint como chave, da string {t}.{raw request body}.
- Divida o cabeçalho em
,para obtertev1. - Calcule
HMAC_SHA256(secret, t + "." + raw_body)sobre os bytes brutos que você recebeu, antes de qualquer parsing ou reserialização de JSON. - Compare-o com
v1em tempo constante. - Rejeite a entrega se
testiver a mais de 5 minutos do horário atual, para impedir reenvios maliciosos.
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);
});Um endpoint sem verificação é uma porta aberta: qualquer pessoa que descubra a URL dele pode dizer que uma geração terminou. Verifique todas as assinaturas e rejeite carimbos de tempo com mais de 5 minutos.
Resposta e novas tentativas
- Responda com qualquer
2xxem até 10 segundos e só depois faça o trabalho. - Qualquer outra coisa é uma tentativa com falha: um status diferente de
2xx, um timeout, uma conexão recusada, um erro de TLS. - Redirecionamentos não são seguidos; um
3xxé uma tentativa com falha. - A ordem não é garantida e um evento pode chegar mais de uma vez. Torne o seu handler idempotente em relação a
Morphic-Event-Id, ou adata.generation.id, já que uma geração tem no máximo um evento.
Uma tentativa com falha é repetida depois de:
| Nova tentativa | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| Espera após a tentativa anterior | 1 min | 5 min | 15 min | 1 h | 3 h | 6 h | 12 h |
Depois da 7ª nova tentativa, cerca de 22 horas após a primeira, o evento é abandonado. A geração em si não é afetada: leia-a com GET /v1/direct/generations/{id}.
Registro de entregas
GET /v1/direct/webhooks/{webhook_id}/events (escopo assets:read) retorna os 20 eventos mais recentes do endpoint, cada um com o corpo exato enviado e o resultado de cada tentativa. Adicione ?generation_id=… para ver os eventos de uma geração.
{
"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 do evento | Significado |
|---|---|
pending | ainda não entregue; mais tentativas podem ocorrer |
delivered | o seu endpoint respondeu 2xx |
abandoned | a última tentativa falhou, ou o endpoint foi removido |
skipped | o endpoint não se inscreveu nesse tipo de evento |
Webhooks são o sinal mais barato e mais rápido, não uma garantia. Mantenha uma consulta ocasional das gerações das quais você não teve notícia como rede de segurança.
Se você tiver algum problema ou alguma dúvida, entre em contato. Teremos prazer em ajudar. Escreva para nós em support@morphic.com.