Webhooks
Webhooks tell your server when a post publishes, fails, or when an account disconnects. Set one endpoint per UGC API account, and verify each request with its signature.
Updated
Set your endpoint
curl -X PUT https://ugcapi.dev/v1/webhook \
-H "Authorization: Bearer ugc_live_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://yourapp.com/webhooks/ugcapi"}'The URL must be HTTPS. Your webhook secret is in the dashboard. Calling PUT /v1/webhook again replaces the URL.
Events
| Type | Sent when |
|---|---|
post.published | One result of a post went live. Includes the public url. |
post.failed | One result of a post failed. Includes error.code and error.message. |
account.disconnected | An account's access was revoked or its token stopped working. |
Post events are sent per result. A video to TikTok and Instagram with a Story sends three events.
Payloads
{
"type": "post.published",
"created_at": "2026-11-01T17:00:42Z",
"data": {
"post_id": "post_abc",
"account_id": "acc_ig_01",
"platform": "instagram",
"placement": "reel",
"url": "https://www.instagram.com/reel/...",
"external_id": "deliverable-981"
}
}{
"type": "post.failed",
"created_at": "2026-11-01T17:01:10Z",
"data": {
"post_id": "post_abc",
"account_id": "acc_tt_01",
"platform": "tiktok",
"placement": "video",
"external_id": "deliverable-981",
"error": { "code": "platform_rejected", "message": "..." }
}
}{
"type": "account.disconnected",
"created_at": "2026-11-02T08:12:00Z",
"data": { "account_id": "acc_tt_01", "platform": "tiktok", "external_id": "creator-204" }
}Verify the signature
Every request has an X-UGCAPI-Signature header. It is the hex-encoded HMAC-SHA256 of the raw request body, keyed with your webhook secret. Compute it over the exact bytes you received, before parsing JSON, and compare in constant time.
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(header || "");
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Delivery
- Return any 2xx status quickly. Do slow work after responding.
- Non-2xx responses and timeouts are retried, so the same event can arrive more than once. Make your handler idempotent, keyed on
post_id,account_idandplacement. - Events can arrive out of order. Use
created_atwhen it matters. - You can always read the current state with
GET /v1/posts/:id.
For handler design, see webhooks for social publishing. Error codes are in errors.
FAQ
Can I have more than one webhook URL?
No. There is one URL per UGC API account. Fan out to other systems from your handler.
Why does my signature check fail?
Usually because the body was parsed and re-serialized. Compute the HMAC over the raw bytes, with the exact secret from the dashboard.
Is there an event when a post is scheduled?
No. The create response is your confirmation. Events cover publishing, failures and disconnects.