Webhook

Dapatkan pemberitahuan bertanda tangan saat generasi Direct API selesai, verifikasi, lalu baca generasinya untuk detailnya.

Webhook adalah endpoint HTTPS milik Anda yang dipanggil Morphic ketika sebuah generasi mencapai status akhir. Pengiriman ditandatangani, diulang selama sekitar satu hari, dan dicatat. Setiap event adalah pemberitahuan ringkas: event menyebutkan generasi mana yang selesai dan bagaimana hasilnya, lalu Anda membaca generasi itu dengan kunci Anda untuk mendapatkan aset, tagihan, dan pesan error-nya.

Mendaftarkan endpoint

POST /v1/direct/webhooks membutuhkan cakupan 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"
      }'
FieldAturan
urlHTTPS pada host publik (bukan alamat IP langsung, bukan alamat privat atau lokal), hingga 2.048 karakter
eventssatu atau beberapa jenis event
descriptionopsional, hingga 200 karakter

Responsnya adalah 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 hanya ditampilkan dalam respons ini. Simpan seperti Anda menyimpan kunci API.
  • Untuk merotasi rahasia, daftarkan endpoint baru, alihkan panggilan Anda ke id-nya, lalu hapus endpoint lama.
  • Sebuah organisasi dapat memiliki hingga 10 endpoint.

Mengelola endpoint

PermintaanCakupanFungsi
GET /v1/direct/webhooksassets:readmenampilkan daftar endpoint sebagai { "webhooks": [ … ] }, tidak pernah beserta rahasianya
DELETE /v1/direct/webhooks/{webhook_id}generations:writemenghapus endpoint; pengiriman yang masih tertunda ke endpoint itu dibatalkan

Menautkan endpoint ke panggilan

Teruskan webhook_id di body panggilan alat. Setiap generasi yang dimulai oleh panggilan itu dikirim ke endpoint tersebut ketika mencapai status akhir yang dilanggani endpoint. Panggilan tanpa webhook_id tidak mengirim webhook.

Alat yang merender video di server Morphic (video_compose, transformasi video_*, dan burn_subtitles) belum menerima webhook_id; lakukan polling pada generasinya.

Jenis event

EventDikirim saat
generation.succeededgenerasi selesai dengan keluaran
generation.failedgenerasi gagal; tagihannya dikembalikan
generation.cancelledgenerasi dibatalkan; tagihannya dikembalikan
  • Setiap generasi menghasilkan paling banyak satu event.
  • queued dan in_progress tidak dikirim; lakukan polling jika Anda membutuhkannya.
  • Sebuah generasi dipantau selama 72 jam setelah dimulai. Generasi yang berakhir lebih lambat tidak mengirim event, jadi bacalah secara langsung.

Pengiriman

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 hanya membawa field berikut:

FieldArti
object, id, tool, status, completed_at, metadataseperti pada objek generasi
error_codenull, atau kode dari generasi yang failed

Event tidak pernah membawa keluaran, link, tagihan, model, atau pesan error, sehingga pengiriman yang salah alamat atau tercatat di log tidak membocorkan apa pun. Saat menerimanya, baca generasi itu dengan kunci Anda: GET /v1/direct/generations/{id}, atau pembacaan batch untuk beberapa generasi. Saat event dikirim, tidak ada lagi perubahan tagihan yang tertunda, jadi pembacaan itu sudah final.

HeaderArti
Morphic-Event-Idid event, sama di setiap pengulangan; lakukan deduplikasi berdasarkan ini
Morphic-Delivery-Idpercobaan yang satu ini; berbeda di setiap pengulangan
Morphic-Event-Typetype dari body
Morphic-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>

Event biasanya dikirim dalam waktu sekitar satu menit setelah generasi mencapai status akhirnya. Ini bukan stream waktu nyata: untuk latensi terendah pada satu generasi yang sedang Anda tunggu, lakukan long-poll.

Memverifikasi tanda tangan

v1 adalah HMAC-SHA256 dalam heksadesimal, dengan signing_secret endpoint Anda sebagai kuncinya, atas string {t}.{raw request body}.

  1. Pisahkan header pada , menjadi t dan v1.
  2. Hitung HMAC_SHA256(secret, t + "." + raw_body) atas byte mentah yang Anda terima, sebelum parsing JSON atau serialisasi ulang apa pun.
  3. Bandingkan hasilnya dengan v1 dalam waktu konstan.
  4. Tolak pengiriman jika t berselisih lebih dari 5 menit dari sekarang, untuk mencegah pemutaran ulang.
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);
});

Endpoint yang tidak diverifikasi adalah pintu terbuka: siapa pun yang mengetahui URL-nya dapat memberi tahu Anda bahwa sebuah generasi telah selesai. Verifikasi setiap tanda tangan dan tolak stempel waktu yang lebih dari 5 menit.

Menjawab dan pengulangan

  • Jawab dengan 2xx apa pun dalam 10 detik, lalu kerjakan prosesnya.
  • Selain itu dianggap percobaan gagal: status non-2xx, timeout, koneksi ditolak, error TLS.
  • Pengalihan tidak diikuti; 3xx dianggap percobaan gagal.
  • Urutan tidak dijamin dan sebuah event dapat tiba lebih dari sekali. Buat handler Anda idempoten berdasarkan Morphic-Event-Id, atau berdasarkan data.generation.id, karena satu generasi memiliki paling banyak satu event.

Percobaan yang gagal diulang setelah:

Pengulangan1234567
Jeda setelah percobaan sebelumnya1 menit5 menit15 menit1 jam3 jam6 jam12 jam

Setelah pengulangan ke-7, sekitar 22 jam setelah percobaan pertama, event dibatalkan. Generasinya sendiri tidak terpengaruh: baca dengan GET /v1/direct/generations/{id}.

Log pengiriman

GET /v1/direct/webhooks/{webhook_id}/events (cakupan assets:read) mengembalikan 20 event terbaru untuk endpoint tersebut, masing-masing dengan body persis yang dikirim dan hasil setiap percobaan. Tambahkan ?generation_id=… untuk event dari satu generasi.

{
  "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 eventArti
pendingbelum terkirim; percobaan berikutnya mungkin masih menyusul
deliveredendpoint Anda menjawab 2xx
abandonedpengulangan terakhir gagal, atau endpoint telah dihapus
skippedendpoint tidak melanggani jenis event tersebut

Webhook adalah sinyal yang lebih hemat dan lebih cepat, bukan jaminan. Tetap lakukan polling sesekali pada generasi yang belum Anda dengar kabarnya sebagai jaring pengaman.


Jika Anda mengalami kendala atau memiliki pertanyaan, jangan ragu untuk menghubungi kami. Kami dengan senang hati akan membantu. Tulis kepada kami di support@morphic.com.

Di halaman ini