Skip to main content

Overview

Webhooks send HTTP POST notifications when tasks complete, eliminating polling.
Webhooks are optional. You can always use polling instead.

Setup

Include callBackUrl when creating a task:
const response = await fetch('https://seedance2-pro.com/api/v1/jobs/createTask', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    model: 'seedance-20',
    callBackUrl: 'https://yourapp.com/api/webhook/seedance2',
    inputs: {
      prompt: 'A beautiful landscape',
      resolution: '1280x720',
      duration: '5s'
    }
  })
});

Webhook Payload

Success

{
  "taskId": "task_clxxxxxx",
  "model": "seedance-20",
  "status": "success",
  "creditsUsed": 200,
  "output": [
    {
      "url": "https://static.seedance2-pro.com/generated/video.mp4",
      "width": 1280,
      "height": 720
    }
  ],
  "error": null,
  "createTime": 1735599634000,
  "completeTime": 1735599754000
}

Failure

{
  "taskId": "task_clxxxxxx",
  "model": "seedance-20",
  "status": "fail",
  "creditsUsed": 200,
  "output": null,
  "error": "Invalid prompt content"
}

Webhook Handler Examples

app.post('/api/webhook/seedance2', async (req, res) => {
  const data = req.body;

  if (data.status === 'success') {
    const videoUrl = data.output[0].url;
    await saveVideo(data.taskId, videoUrl);
  } else if (data.status === 'fail') {
    console.error('Failed:', data.taskId, data.error);
  }

  res.status(200).json({ received: true });
});

Best Practices

  • Always respond 200 immediately, process async
  • Implement idempotency using taskId (webhooks may be delivered multiple times)
  • Use HTTPS for webhook endpoints
  • Implement fallback polling for critical tasks

Retry Policy

Failed webhooks (non-2xx response) are retried up to 3 times with exponential backoff (1s, 5s, 15s).

Testing Locally

Use ngrok to expose your local server:
ngrok http 3000
# Then use the ngrok URL as callBackUrl