Webhooks
Receive real-time notifications when prediction lifecycle events occur.
How Webhooks Work
Webhooks are configured per-prediction by including a webhook object in your prediction request:
{
"model": "skytells/truefusion-pro",
"input": {
"prompt": "A futuristic cityscape at sunset"
},
"webhook": {
"url": "https://your-server.com/webhook",
"events": ["prediction.succeeded", "prediction.failed"]
}
}Webhook Object
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The HTTPS URL to receive webhook events |
events | array | Yes | List of event types to subscribe to |
Notifications
For each prediction status change, your server will receive the complete Prediction object via the URL you provided. The webhook payload contains the same structure as the prediction response, including output URLs to the generated assets.
Your webhook receives the updated prediction object every time the prediction status changes.
Configure your webhook endpoint to properly handle and process these notifications. Store your assets in a secure location as soon as you receive the webhook notification. Skytells does not retain output data more than 5 minutes after a prediction is completed. Implementing proper error handling and retry logic is recommended.
Webhook Events
Subscribe to one or more of the following event types:
| Event | Fired when |
|---|---|
prediction.started | Prediction begins processing |
prediction.succeeded | Prediction completed — outputs are ready |
prediction.failed | Prediction encountered an inference error — output will be null |
prediction.cancelled | Prediction was cancelled by the user |
When a prediction.failed event fires, the payload contains a non-null error field describing the inference failure. See Prediction Errors for the full error schema and resolution guide.
Webhook Payload
When an event fires, Skytells sends a POST request to your URL with this JSON body:
{
"type": "prediction.succeeded",
"created_at": "2026-03-06T20:15:23.000000Z",
"data": {
"id": "d05b96fc-7fdf-4528-8e61-aa1092f48040",
"status": "succeeded",
"model": { "name": "truefusion-pro", "type": "image" },
"output": ["https://delivery.skytells.cloud/us/2026/03/06/7bfdb9a4.png"],
"metrics": { "predict_time": 10.89 },
"metadata": { "billing": { "credits_used": 0.05 } }
}
}| Field | Type | Description |
|---|---|---|
type | string | The event type (e.g. prediction.succeeded) |
created_at | string | ISO 8601 timestamp of the event |
data | object | Full prediction object at the time of the event |
Security
For security reasons, use a webhook URL that is not publicly guessable.
Skytells attaches a verification signature to every webhook notification to ensure it was authentically sent from Skytells and not intercepted by a third party.
You can verify authenticity by comparing the X-Skytells-Signature header with the expected signature.
The expected signature is calculated using the HMAC-SHA256 algorithm and the secret key SKYTELLS_WEBHOOK_SECRET provided in your dashboard if you're using our Enterprise API.
Always verify the X-Skytells-Signature header before processing any webhook event. Never trust unverified payloads — discard any request that fails signature verification.
Verify webhook signature
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Handling Webhooks
Your endpoint should follow these rules:
- Respond quickly — Return
200within 10 seconds. Process events asynchronously if needed. - Be idempotent — Skytells may deliver the same event more than once. Use the prediction
idto deduplicate. - Handle failures — Non-
2xxresponses trigger retries with exponential backoff.
Example webhook handler
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const signature = req.headers['x-skytells-signature'] as string;
const payload = JSON.stringify(req.body);
const secret = process.env.SKYTELLS_WEBHOOK_SECRET!;
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
const { type, data } = req.body;
if (type === 'prediction.succeeded') {
// save data.output[0] to your storage
}
res.sendStatus(200);
});Supported Model Types
Webhooks are available across all model types. Video and audio models require webhooks — their outputs are only accessible via the delivered payload.
Image Models
Webhooks deliver output image URLs. Outputs expire 5 minutes after completion.
Video Models
Webhook is required. Video generation is always asynchronous.
Audio Models
Webhook delivers the audio file URL upon completion.
How is this guide?