Webhooks — Completion

event = completion

Completion

Fires every time a member finishes a batch of videos — i.e. on every successful POST /api/complete. It's the rawest "something happened" signal: one delivery per batch, regardless of whether that batch earned the member a payable reward. Most reward integrations want reward_unlocked instead — reach for completion when you need per-batch visibility (analytics, progress mirroring, engagement tracking).

When it fires

  • On every non-blocked completion, for every promotion shape — both cumulative/threshold promotions and per-completion promotions.
  • Fires only to webhooks that have completion selected in their Events subscription (dashboard → Webhooks → edit → Events).
  • Does not fire when the completion trips a block-severity fraud rule — that transaction emits fraud_flagged instead, never completion.

Completion vs. Reward Unlocked

These two events look similar but answer different questions. completion means "a batch was finished." reward_unlocked means "the member has earned a payable reward." On some promotion shapes those are the same moment; on others they are not.

Promotion shape completion fires reward_unlocked fires
Cumulative / threshold
(member completes N batches to earn one larger reward)
On every batch — N times per member. Once, on the batch that crosses the threshold.
Per-completion
(no threshold — each batch is its own reward)
On every completion. On every completion — the two coincide.
Fraud-blocked completion Does not fire. Does not fire — fraud_flagged is sent instead.

Don't subscribe one webhook to both events on a per-completion promotion. Because completion and reward_unlocked fire on the same moment there, your endpoint would receive two callbacks per completion. Pick the one event that matches your intent, or branch on {{event}} and deduplicate on {{transaction_id}} so you only act once.

Which event should I subscribe to?

  • Crediting the member a reward → use reward_unlocked. On a threshold promotion it fires exactly once, so you won't over-credit.
  • Per-batch analytics, engagement metrics, or mirroring our progress meter → use completion. You get one delivery for every batch, with that batch's {{user_payout}} and the running {{cumulative_user_payout}}.
  • Reversing payouts on abuse → use fraud_flagged.

Example payload

With the default body template (no custom body configured), the JSON we POST looks like this. The example below is the second of three batches on a threshold promotion — note cumulative_user_payout is still climbing toward the full reward:

{
  "event":                  "completion",
  "member_id":              "abc123",
  "user_payout":            "0.0250",
  "cumulative_user_payout": "0.0500",
  "org_retention":          "0.0050",
  "org_gross":              "0.0300",
  "platform_cut":           "0.0100",
  "gross_revenue":          "0.0400",
  "points_earned":          "25",
  "promotion_id":           "42",
  "promotion_slug":         "winter-promo",
  "transaction_id":         "1830",
  "completed_at":           "2026-04-21T16:06:11Z"
}

The payload shape is identical to every other event — only {{event}} changes. Each completion carries its own {{transaction_id}}; store it for idempotency in case of redelivery.

Handler sketch

// Pseudocode — verify signature, then record per-batch progress.
// Crediting the reward is reward_unlocked's job, not completion's.
app.post('/webhooks/rewardedmedia', (req, res) => {
  if (!verifyHMAC(req.headers['x-signature'], req.rawBody, SECRET)) {
    return res.status(401).end();
  }

  const {
    event, member_id, transaction_id,
    user_payout, cumulative_user_payout
  } = req.body;

  if (event === 'completion' && !alreadySeen(transaction_id)) {
    recordBatch(member_id, transaction_id, parseFloat(user_payout));
    updateProgress(member_id, parseFloat(cumulative_user_payout));
    markSeen(transaction_id);
  }

  res.status(200).end();
});

Full macro reference

The complete list of macros, the default body format, signing, and delivery/retry behavior all live on the Webhooks overview page.