Webhooks
Configure a webhook URL to receive signed HTTP POST notifications when jobs complete or fail. One webhook URL per account, configured at the tenant level. Use webhooks instead of polling to build responsive integrations.
Supported Events
job.completedjob.failedBoth events are delivered automatically whenever a webhook URL is configured. There is no per-event subscription — you receive both.Configure Webhook
Set or update your webhook URL and signing secret. Replaces any existing configuration.
/api/v1/tenants/me/webhook• One webhook URL per account. Calling PUT replaces the previous configuration.
• If webhook_secret is omitted, deliveries are sent unsigned (no X-UGen-Signature header).
• Generate a secret with: openssl rand -hex 32 (produces a 64-character hex string).
Body
| Parameter | Type |
|---|---|
webhook_urlREQ | string (URL) |
webhook_secret | string |
Request Example
curl -X PUT https://api.u-gen.ai/api/v1/tenants/me/webhook \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://example.com/webhooks/u-gen",
"webhook_secret": "a7b3c9d2e1f4a8b5c6d7e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1"
}'Get Webhook Config
Retrieve the current webhook configuration. The secret is not returned.
/api/v1/tenants/me/webhook• webhook_configured is true if a URL is set, false otherwise.
• The webhook_secret is never returned in the response for security.
Request Example
curl -X GET https://api.u-gen.ai/api/v1/tenants/me/webhook \
-H "X-API-Key: YOUR_API_KEY"Remove Webhook
Remove your webhook configuration. You will stop receiving notifications.
/api/v1/tenants/me/webhook• Clears both the URL and secret. Any in-flight retries will continue but no new deliveries will be sent.
Request Example
curl -X DELETE https://api.u-gen.ai/api/v1/tenants/me/webhook \
-H "X-API-Key: YOUR_API_KEY"Per-Job Override
webhook_url in the POST /api/v1/jobs request body. The job-level URL takes precedence over the tenant-level configuration. The tenant's signing secret is used for both.Delivery Format
When a job completes or fails, U-Gen sends a POST request to your configured URL with the following payload and headers.
Request Headers
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-UGen-Timestamp | ISO 8601 timestamp of when the delivery was sent |
X-UGen-Signature | HMAC-SHA256 hex signature (only if a signing secret is configured) |
User-Agent | UGen-Webhook/1.0 |
Payload Fields
| Field | Type | Description |
|---|---|---|
event | string | job.completed or job.failed |
job_id | string (UUID) | The job identifier |
status | string | completed or failed |
final_video_url | string | null | Download URL for the final video (null on failure) |
thumbnail_url | string | null | Thumbnail image URL (null on failure) |
credits_consumed | number | Credits consumed by the job (0 on failure) |
error_message | string | null | Error description (null on success) |
timestamp | string | ISO 8601 timestamp of when the event occurred |
Example: job.completed
{
"event": "job.completed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"final_video_url": "https://storage.u-gen.ai/videos/550e8400/final.mp4",
"thumbnail_url": "https://storage.u-gen.ai/videos/550e8400/thumbnail.jpg",
"credits_consumed": 131.0,
"error_message": null,
"timestamp": "2026-04-10T18:30:45.123456+00:00"
}Example: job.failed
{
"event": "job.failed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"final_video_url": null,
"thumbnail_url": null,
"credits_consumed": 0.0,
"error_message": "Segment generation failed: Video timeout after 300s",
"timestamp": "2026-04-10T18:30:45.123456+00:00"
}Signature Verification
If you configured a signing secret, verify the X-UGen-Signature header to ensure the request is authentic. The signature is computed as: HMAC-SHA256(secret, timestamp + "." + body) where the body is the raw minified JSON (no spaces).
Node.js
const crypto = require('crypto');
function verifyWebhook(rawBody, headers, secret) {
const timestamp = headers['x-ugen-timestamp'];
const signature = headers['x-ugen-signature'];
const message = timestamp + '.' + rawBody;
const expected = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}Python
import hashlib, hmac
def verify_webhook(raw_body: str, headers: dict, secret: str) -> bool:
timestamp = headers["x-ugen-timestamp"]
signature = headers["x-ugen-signature"]
message = f"{timestamp}.{raw_body}"
expected = hmac.new(
secret.encode(), message.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Retry Behavior
If your endpoint returns a non-2xx status code or is unreachable, U-Gen retries the delivery up to 3 times with exponential backoff.
| Attempt | Delay Before | Timeout |
|---|---|---|
| 1st | Immediate | 10 seconds |
| 2nd | 1 second | 10 seconds |
| 3rd (final) | 4 seconds | 10 seconds |
After 3 failed attempts, the delivery is abandoned. No delivery log is stored — failed webhooks are only visible in server logs. Delivery is fire-and-forget and does not affect the job status.
Idempotency
job_id and event fields to deduplicate deliveries on your side.