Platform

Callback (webhook)

Daripada melakukan polling, tambahkan callback_url ke permintaan gambar atau video. Saat selesai atau gagal, Zurelay mengirim hasilnya ke URL tersebut lewat POST, dengan tanda tangan agar Anda bisa memastikan kiriman itu berasal dari kami.

Minta callback

Tambahkan callback_url ke salah satu permintaan berikut. URL harus menggunakan https dan bisa diakses lewat internet publik.

EndpointYang terjadi
POST /v1/videosMemulai pembuatan video seperti biasa. Saat proses berakhir, callback mengirim video yang sudah jadi (atau alasan kegagalannya). Tidak perlu polling.
POST /v1/images/generationsLangsung merespons dengan 202 dan id permintaan tanpa menunggu gambar selesai, membuatnya di latar belakang, lalu mengirim callback.
POST /v1/images/editsSama untuk pengeditan: gunakan field JSON, atau field formulir dalam permintaan multipart.
curl https://api.zurelay.com/v1/videos \
-H "Authorization: Bearer $ZURELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"prompt": "A fox running through fresh snow at dawn",
"seconds": 5,
"callback_url": "https://example.com/hooks/zurelay"
}'

Permintaan gambar dengan callback

Tanpa callback_url, permintaan gambar menunggu lalu mengembalikan gambar, seperti API OpenAI. Dengan parameter tersebut, permintaan langsung diterima dan mengembalikan data tugas. Biaya hanya dikenakan jika gambar berhasil dibuat:

Respons
HTTP/1.1 202 Accepted
x-request-id: req_6fJd0kQ3pW9xT2mB7cZ1hR4u
{
"id": "req_6fJd0kQ3pW9xT2mB7cZ1hR4u",
"object": "image.generation",
"model": "nano-banana-pro",
"status": "in_progress",
"created_at": 1791000000,
"completed_at": null,
"data": [],
"url_expires_at": null,
"cost": null,
"error": null,
"callback_url": "https://example.com/hooks/zurelay"
}
GET/v1/images/{id}

Anda juga bisa melakukan polling: GET /v1/images/{id} mengembalikan objek yang sama, dengan tautan baru di data setelah status bernilai completed. Callback selalu berisi tautan, bukan base64, apa pun nilai response_format.

Yang kami kirim

Satu POST per peristiwa, dengan body JSON: type, timestamp dan data, yaitu objek tugas persis seperti yang dikembalikan API (objek video, atau permintaan gambar di atas), dengan tautan unduhan yang berlaku selama 24 jam.

PeristiwaKapan
video.completedVideo sudah siap: unduh melalui data.url.
video.failedVideo gagal dibuat: data.error menjelaskan alasannya. Anda tidak dikenai biaya.
image.completedGambar sudah siap: data.data berisi tautan untuk setiap gambar.
image.failedTidak ada gambar yang berhasil dibuat: data.error menjelaskan alasannya. Anda tidak dikenai biaya.
Callback video
POST /hooks/zurelay HTTP/1.1
content-type: application/json
user-agent: Zurelay-Webhooks/1.0 (+https://zurelay.com/docs/webhooks)
webhook-id: msg_2c1f9e0b7a4d4f6e8b3a91c05d7e6f42
webhook-timestamp: 1791000094
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
"type": "video.completed",
"timestamp": "2026-10-03T03:21:34.000Z",
"data": {
"id": "video_Qm3v8Rk2Lx7Pa1Zt9Ws4Yn6B",
"object": "video",
"model": "seedance-2.0",
"status": "completed",
"progress": 100,
"created_at": 1791000000,
"completed_at": 1791000093,
"expires_at": 1793592000,
"seconds": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"size": "1280x720",
"generate_audio": true,
"url": "https://…/video_Qm3v8Rk2Lx7Pa1Zt9Ws4Yn6B.mp4?token=…",
"url_expires_at": 1791086494,
"cost": 0.45,
"error": null,
"callback_url": "https://example.com/hooks/zurelay"
}
}
Callback gambar (body)
{
"type": "image.completed",
"timestamp": "2026-10-03T03:20:41.000Z",
"data": {
"id": "req_6fJd0kQ3pW9xT2mB7cZ1hR4u",
"object": "image.generation",
"model": "nano-banana-pro",
"status": "completed",
"created_at": 1791000000,
"completed_at": 1791000041,
"data": [{ "url": "https://…/req_6fJd0kQ3pW9xT2mB7cZ1hR4u-1.png?token=…" }],
"url_expires_at": 1791086441,
"cost": 0.07,
"error": null,
"callback_url": "https://example.com/hooks/zurelay"
}
}
HeaderNilai
webhook-idmsg_…: tetap sama pada setiap pengiriman ulang callback ini. Gunakan untuk melewati callback yang sudah Anda tangani.
webhook-timestampWaktu percobaan pengiriman ini dikirim, dalam detik Unix.
webhook-signaturev1, lalu tanda tangan (lihat di bawah). Ada dua tanda tangan, dipisahkan spasi, selama sehari setelah Anda merotasi kunci rahasia.
user-agentZurelay-Webhooks/1.0

Verifikasi tanda tangan

Callback mengikuti format Standard Webhooks, sama seperti webhook OpenAI. Tanda tangannya adalah HMAC-SHA256 dari <webhook-id>.<webhook-timestamp>.<body>, menggunakan kunci rahasia penandatanganan ruang kerja Anda, dalam base64. Temukan kunci rahasia tersebut (diawali whsec_) di halaman API Kunci pada dasbor, dan simpan hanya di server Anda.

import os
from fastapi import FastAPI, HTTPException, Request
from standardwebhooks.webhooks import Webhook # pip install standardwebhooks
webhook = Webhook(os.environ["ZURELAY_WEBHOOK_SECRET"]) # whsec_…
app = FastAPI()
@app.post("/hooks/zurelay")
async def zurelay_hook(request: Request):
body = await request.body() # the raw bytes: the signature covers exactly these
try:
event = webhook.verify(body, dict(request.headers))
except Exception:
raise HTTPException(status_code=400)
handle(event) # event["type"], event["data"]; keep it quick, or queue it
return {"ok": True}
  • Periksa body mentah: mengurai lalu menserialisasi ulang JSON akan mengubah byte-nya, sehingga tanda tangannya juga berubah.
  • Tolak webhook-timestamp yang berusia lebih dari lima menit: ini mencegah siapa pun mengirim ulang callback yang mereka sadap. Library menangani pemeriksaan ini untuk Anda.
  • Merotasi kunci rahasia (oleh pemilik dan admin, di halaman Kunci API) langsung menghasilkan kunci baru. Kunci lama tetap digunakan untuk menandatangani bersama kunci baru selama 24 jam, jadi alihkan server Anda ke kunci baru dalam waktu tersebut.

Respons dulu, lalu proses

Balas dengan 2xx apa pun dalam 15 detik agar callback dianggap terkirim. Simpan peristiwanya dan balas terlebih dahulu; unduh video atau gambar setelahnya. Callback bisa tiba lebih dari sekali (misalnya jika balasan Anda hilang di perjalanan), jadi gunakan webhook-id agar setiap callback hanya diproses sekali.

Percobaan ulang

Selain itu (status error, pengalihan, tidak ada balasan dalam 15 detik, atau server tidak bisa dijangkau), pengiriman akan dicoba ulang, dengan total enam percobaan:

PercobaanDikirim
1Segera setelah tugas berakhir
21 menit kemudian
35 menit setelahnya
430 menit setelahnya
52 jam setelahnya
68 jam setelah itu, lalu ditandai gagal

Setiap callback ditampilkan bersama permintaannya di Permintaan pada dasbor: tujuan pengiriman, respons setiap percobaan, dan jadwal percobaan berikutnya. Coba ulang callback di sana langsung mengirimkannya lagi, baik sudah terkirim maupun belum. Tautan di dalamnya diperbarui pada setiap percobaan.

Callback hanya dikirim ke URL https di internet publik: bukan localhost, alamat privat, atau alamat apa pun yang mengarah ke alamat privat. Callback tidak mengikuti pengalihan, jadi gunakan URL tujuan akhir. Untuk mencobanya selama pengembangan, gunakan tunnel (ngrok, Cloudflare Tunnel) atau alat pemeriksa permintaan.

Ada pertanyaan, atau ada yang kurang? Tanyakan kepada tim dukungan kami dan kami akan menjawab lewat email, biasanya dalam satu menit.