Video Automation

Webhooks: get the result of every upload

Webhook events and payload, the HMAC-SHA256 signature headers, a Node.js verification example, automatic retries and the test event in API & webhooks.

A webhook calls your URL as soon as something happens – a post was published or failed, an account was connected or needs to be reconnected. You no longer need to poll the status endpoint.

Set up a webhook

  1. Open API & webhooks.
  2. Under Webhooks click New webhook.
  3. Enter an HTTPS URL that is reachable from the internet and choose the events.
  4. Click Create and copy the signing secret – it is shown only once.

The Upload-Post compatible API offers the same: POST /api/uploadposts/users/notifications with webhook_url and webhook_events; a new secret comes from POST /api/uploadposts/users/webhook-secret. The number of webhooks depends on your plan.

Events

Event When
upload_completed a platform finished a post – successfully or not (one event per platform)
social_account_connected an account was connected or reconnected
social_account_disconnected an account was removed
social_account_reauth_required an access expired and the account must be reconnected

Payload

Every delivery is a POST with a JSON body:

{
  "event": "upload_completed",
  "request_id": "2c0f…",
  "job_id": null,
  "external_id": "video-123",
  "profile_username": "my-brand",
  "platform": "youtube",
  "media_type": "video",
  "title": "My video",
  "caption": "",
  "result": { "success": true, "url": "https://www.youtube.com/watch?v=…", "post_id": "…" },
  "user_email": "owner@example.com",
  "created_at": "2026-11-07T12:00:03.412Z"
}

If the platform added a warning (for example YouTube set a video to private), it is in result.warnings.

Check the signature

Every delivery carries these headers:

  • X-VA-Signature and X-Upload-Post-Signature – both sha256= followed by the hex HMAC-SHA256 of "<timestamp>.<raw body>" with your signing secret
  • X-Upload-Post-Timestamp – Unix time in seconds
  • X-Upload-Post-Event – the event name

Compute the signature over the raw request body, before any JSON parsing, and compare in constant time. Reject old timestamps to block replays.

// Node.js (Express): app.post('/hook', express.raw({ type: 'application/json' }), handler)
const crypto = require('crypto');

function verify(req, secret) {
  const ts = req.get('X-Upload-Post-Timestamp');
  const sig = req.get('X-VA-Signature') || req.get('X-Upload-Post-Signature') || '';
  if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${ts}.${req.body.toString('utf8')}`).digest('hex');
  return sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

function handler(req, res) {
  if (!verify(req, process.env.VA_WEBHOOK_SECRET)) return res.sendStatus(401);
  const event = JSON.parse(req.body.toString('utf8'));
  // … your logic …
  res.sendStatus(200);
}

Retries

Answer with any 2xx status within a few seconds. If your server is unreachable or answers with an error, we try again up to 6 times with growing intervals. Work that takes longer should be done after you have answered. Recent deliveries with status code and error are listed under Recent deliveries in API & webhooks.

Because of retries an event can arrive twice. Use request_id plus platform to recognise duplicates.

Send a test event

Click Send test event in API & webhooks. Every webhook gets an upload_completed event with "test": true and a dummy YouTube URL – handy to test your signature check.

Use with n8n, Make and Zapier

Point the webhook at a webhook trigger of your tool (n8n "Webhook" node, Make "Custom webhook", Zapier "Catch Hook"). Examples are on the n8n, Make and Zapier pages and on the webhooks integration page.