Webhooks

Receive events in real time: posts published, DMs received, etc.

Edit this page·Last updated: July 13, 2026

Webhooks

Webhooks notify you in real time of events on your connected accounts. No need to poll the API.

Create a webhook

POST /v1/webhooks
{
  "url": "https://your-app.com/webhooks/pepe",
  "events": [
    "post.published",
    "post.failed",
    "inbox.message.received",
    "inbox.comment.received"
  ]
}

Response:

{
  "id": "whk_xxx",
  "url": "https://your-app.com/webhooks/pepe",
  "secret": "whsec_xxxxx",
  "status": "active"
}

Signature verification

Each webhook is signed with HMAC-SHA256. Verify it server-side:

import crypto from "crypto";

function verifyWebhook(rawBody: string, signature: string, secret: string) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Express
app.post("/webhooks/pepe", (req, res) => {
  const sig = req.headers["x-pepe-signature"] as string;
  if (!verifyWebhook(req.rawBody, sig, process.env.PEPE_WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  // Processing...
  res.status(200).send("OK");
});

Available events

Events marked with ***** are scheduled for implementation in v1.5.x and documented here as the supported contract.

FamilyEvents
Postspost.published, post.failed, post.scheduled, post.unpublished
Inboxinbox.message.received, inbox.comment.received, inbox.review.received, inbox.mention.received
Accountsaccount.connected, account.disconnected (fired when an OAuth token refresh fails and the account is marked disconnected), account.health_changed
Adsads.campaign.started, ads.campaign.ended, ads.budget_exhausted
Callscall.incoming, call.completed, call.recording.ready
WhatsAppwhatsapp.message.received, whatsapp.template.approved
Phone numbersphone_number.purchased, phone_number.ported
YouTube → Shortsclip.published * — fired when a YouTube → Shorts clip is published to a social account
Clip Rewardsclip_reward.claimed * — fired when a clipper enrolls a published clip in a Clip Rewards campaign; clip_reward.paid * — fired when a clipper's cooled claims are swept to their pending balance; takedown.filed * — fired when a DMCA takedown is filed and auto-processed
Channel Automationchannel_automation.new_upload * — fired when channel automation detects a new YouTube upload and starts a clip job
AI Creative Enginecreative.generated * — fired when the AI Creative Engine finishes generating a batch of creatives
Videosvideo.render.complete * — fired when a VideoJob (YouTube → Shorts or classic) finishes rendering. Supersedes the legacy video.completed event for new jobs; legacy event remains for backward compatibility.

Idempotency

Each webhook includes a unique eventId (X-Pepe-Event-Id). Keep it for 24h to avoid duplicates.

{
  "eventId": "evt_01HXXXXXX",
  "type": "post.published",
  "data": { "postId": "post_xxx", "platforms": ["twitter"] },
  "createdAt": "2026-07-13T10:00:00Z"
}

Retries

Webhooks are retried with exponential backoff:

AttemptDelay
1Immediate
2+30s
3+2 min
4+10 min
5+1 h
6+6 h
7+24 h (last)

If your server returns a non-2xx code after 7 attempts, the webhook is marked failed.

Delivery logs

All attempts are visible in Dashboard > Webhooks > Delivery Logs. You can replay an event manually.

Test

pepe webhooks send-test --event post.published --url https://your-app.com/webhooks/pepe

Was this page helpful?