Webhooks

Webhooks

Receive real-time notifications when prediction lifecycle events occur.

How Webhooks Work

POST /v1/predictions pending prediction.started 200 OK prediction.succeeded 200 OK Your App Skytells API Your Server

Webhooks are configured per-prediction by including a webhook object in your prediction request:

POST /v1/predictions
{
  "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

FieldTypeRequiredDescription
urlstringYesThe HTTPS URL to receive webhook events
eventsarrayYesList of event types to subscribe to

Notifications

NOTIFY/your-webhook-url

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.

See prediction schema →


Webhook Events

Subscribe to one or more of the following event types:

EventFired when
prediction.startedPrediction begins processing
prediction.succeededPrediction completed — outputs are ready
prediction.failedPrediction encountered an inference error — output will be null
prediction.cancelledPrediction 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:

Webhook POST payload
{
  "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 } }
  }
}
FieldTypeDescription
typestringThe event type (e.g. prediction.succeeded)
created_atstringISO 8601 timestamp of the event
dataobjectFull 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.

Verify webhook signature

TypeScript
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:

  1. Respond quickly — Return 200 within 10 seconds. Process events asynchronously if needed.
  2. Be idempotent — Skytells may deliver the same event more than once. Use the prediction id to deduplicate.
  3. Handle failures — Non-2xx responses trigger retries with exponential backoff.

Example webhook handler

TypeScript
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.

How is this guide?

On this page