---
title: "Campaign deleted"
method: POST
path: "campaign.deleted"
tags: ["Webhook Events"]
---

# Campaign deleted

`POST campaign.deleted` (webhook)

A campaign was deleted, with the assignments it still had (each reported as `assignment.canceled`). `campaign` is its last state.

## Headers

- `webhook-id` string, required
- `webhook-timestamp` string, required
- `webhook-signature` string, required

## Payload

- CampaignDeletedWebhookEvent: A campaign was deleted, with the assignments it still had (each reported as `assignment.canceled`). `campaign` is its last state.
  - `id` string, required: Event id; the `webhook-id` header. The same on every retry: dedupe on it.
  - `type` 'campaign.deleted', required
  - `timestamp` string, date-time, required: When the change happened
  - `organizationId` string, required
  - `actor` WebhookActor, required: Who caused the event: a member (`user`), an API key (`apiKeyId`), a creator, or viral.app itself (`system`); `surface` says where the change entered. Skip events your own API key caused to avoid loops.
    - `type` 'user' | 'api_key' | 'creator' | 'system', required
    - `surface` 'ui' | 'api' | 'copilot' | 'system', required
    - `userId` string, nullable, required
    - `apiKeyId` string, nullable, required
    - `impersonated` boolean, required
  - `data` object, required
    - `campaign` WebhookCampaign, required
      - `id` string, required
      - `name` string, required
      - `description` string, nullable, required
      - `currency` 'USD' | 'EUR' | 'CHF' | 'GBP' | 'CNY' | 'JPY' | 'KRW' | 'THB' | 'IDR' | 'VND' | 'RUB', required
      - `interval` 'day' | 'week' | 'month', required
      - `intervalCount` integer, required
      - `billingAnchor` 'rolling' | 'fixed', required
      - `timeZone` string, required: IANA time zone the campaign's periods and dates follow (a period ends at midnight in this zone). Views are still counted per UTC day.
      - `videoTarget` integer, required
      - `videoTargetUnit` 'videos' | 'creatives' | 'per_platform', required: What the video target counts: "videos" (every post on every platform, a cross-post on three platforms counts 3), "creatives" (each video once, its copies on other platforms included) or "per_platform" (a count per platform in videoTargetPlatforms; videoTarget is their sum).
      - `videoTargetPlatforms` object, nullable, required
      - `validFrom` string, date-time, required
      - `validUntil` string, date-time, nullable, required
      - `status` 'upcoming' | 'active' | 'closing' | 'ended', required
      - `revision` integer, required
      - `spec` object, nullable, required: A campaign spec with every default filled in.
        - `name` string, required: Campaign name
        - `description` string: Campaign description
        - `currency` 'USD' | 'EUR' | 'CHF' | 'GBP' | 'CNY' | 'JPY' | 'KRW' | 'THB' | 'IDR' | 'VND' | 'RUB', required: Currency used for campaign payouts
        - `schedule` object, required: Payout cadence and window.
          - `interval` 'day' | 'week' | 'month', required: Payout cycle unit. Monthly = month × 1, biweekly = week × 2, weekly = week × 1.
          - `intervalCount` number, required: How many units make one payout cycle.
          - `anchor` 'rolling' | 'fixed', required: rolling: each creator's cycle starts the day they join, so creators are added and paid on their own dates. fixed: everyone shares the same calendar cycle and payouts go out in one batch per cycle; a creator added mid-cycle gets that cycle as a first period cut to the join day, a fixed salary in it paid pro rata. Required — it locks once the campaign is in use.
          - `startsOn` string, required: Campaign start (YYYY-MM-DD).
          - `timeZone` string: IANA time zone the periods and dates follow: a period ends at midnight there, a video belongs to the period it was published in there, and "today" is the zone's day. Views are still counted per UTC day. Defaults to "UTC" on create; editable until a payout depends on its periods or a period is canceled (`CAMPAIGN_FIELD_LOCKED`).
        - `videoTarget` number, required: Videos a creator should publish per cycle, in videoTargetUnit: every post on every platform ("videos", the default), each video once whatever platforms it is posted to ("creatives"), or the sum of videoTargetPlatforms ("per_platform", where the platforms' sum replaces the value sent).
        - `videoTargetUnit` 'videos' | 'creatives' | 'per_platform': What the video target counts: "videos" (every post on every platform, a cross-post on three platforms counts 3), "creatives" (each video once, its copies on other platforms included) or "per_platform" (a count per platform in videoTargetPlatforms; videoTarget is their sum).
        - `videoTargetPlatforms` object, nullable: Videos per platform per cycle, with videoTargetUnit "per_platform".
        - `briefId` string, nullable: Campaign brief ID with orgbrief_ prefix
        - `payouts` object, required
          - `fixedSalary` object: A guaranteed amount per cycle, regardless of videos.
            - `id` string: Stable id of the stored rule (read it from GET). Send it back to keep the rule, and any creator's custom rate on it, attached through edits; omit it for new entries.
            - `amount` number, required: Amount in major units of the campaign currency (10 = 10.00).
          - `basePayouts` object[]
            - `id` string: Stable id of the stored rule (read it from GET). Send it back to keep the rule, and any creator's custom rate on it, attached through edits; omit it for new entries.
            - `perVideo` number, required: Paid per published video (until the first step).
            - `steps` object[]: Rate steps: a video is paid the rate of the step its place in the period falls in. An update that omits them keeps them; [] removes them.
              - …
            - `limit` object, nullable: Omitted: the base payout stops at the video target. null: unlimited, every extra video is paid.
              - …
            - `minViews` number: Only videos reaching this many total views by the end of the payout period they were published in earn the base payout. The same as a views condition with countWhen byPublicationPeriodEnd; send one or the other.
            - `platforms` string[]: Platforms this entry applies to. Omit for all platforms. Entries of the same family may share platforms: a video on a shared platform earns each entry that covers it, and the dry-run warns.
            - `conditions` object: Which videos take part in this payout: content type (video/slideshow), tags (custom, project or geography tag ids; anyOf, allOf, noneOf), hashtags (anyOf, allOf, noneOf), views (atLeast or below, per video), totalViews (atLeast or below, the creator's total views) and totalVideos (atLeast or below, the creator's number of videos). On a per-video payout a video must match to be paid; on an all-content payout only matching videos' views add up. Views conditions work per video: base payouts count a video in the period it reaches an atLeast threshold (countWhen reached, the default) or only by the end of its publication period (countWhen byPublicationPeriodEnd, the same as minViews, and the only variant of a base below); per-video CPM payouts and bonuses pay a video's earlier views once it reaches an atLeast threshold, and pay a below one only for periods the video ends under the count. totalViews judges each payout period on the creator's total views over the payout's platforms, whatever the other conditions say (countFrom periodVideos: views of the videos published in the period, counted to its end; eligibleVideos: views gained in the period by every video inside its eligibility window): a period that misses pays nothing through this payout, and its views are never paid later. totalVideos judges each payout period on every video the creator published in it, on every platform (a post on three platforms counts 3), whatever the other conditions and the payout's platforms say; its value is a count or "videoTarget" (the campaign's video target, or the creator's own with custom rates, never prorated, counted in the target's unit: under creatives each video once whatever platforms it is on, under per_platform every listed platform must reach its own count and other platforms do not count). A period that misses pays nothing through this payout; on a base payout an atLeast row may carry the missed period's videos instead (carry previousPeriod: paid in the next period if that one reaches its own minimum; untilPaid: every missed period waits for the first that does), priced as if paid in the period they were posted in (its rate steps, its end and its base payout cap, never the paying period's), only directly in a match all group. One atLeast and one below make a range. Periods follow the campaign's schedule.timeZone; views are counted per UTC day. The platform list always applies on top.
              - …
            - `scope` 'per_video' | 'per_creative': per_video (the default): every video counts. per_creative: copies of one video posted to several of this payout's platforms (grouped as a creative) count as one creative, their views added up: it earns one base payout, and views conditions judge the creative's combined views (directly in a match all group, never next to a totalVideos row that carries). It counts in the first period at whose end its views meet a reached atLeast condition, otherwise only in the period its first copy was posted in. Needs two or more platforms; not with creatives once.
            - `creatives` 'once': once: only each creative's winner counts. When the creator posts the same video to several of this payout's platforms (copies grouped as a creative), the copy with the most views earns one base payout, in the first period any copy is counted; views conditions judge each copy on its own. Needs two or more platforms; not with scope per_creative. Omit to count every copy.
          - `cpmPayouts` object[]
            - `scope` 'per_video' | 'per_creative' | 'all_content': per_video: applied to every video with its own views. per_creative: copies of one video posted to several platforms (grouped as a creative) count as one video, their views added up; a video in no creative counts on its own. all_content: applied once per cycle to the creator's aggregated views.
            - `creatives` 'bestInPeriod' | 'bestInWindow': Pay only each creative's winner: when the creator posts the same video to several of this payout's platforms (copies grouped as a creative), only the copy with the most views earns it, judged in the payout period (bestInPeriod) or in each copy's eligibility window (bestInWindow). A creative is never paid twice. Needs two or more platforms; not with scope per_creative or a current_period_only eligibility; bonuses per video take bestInWindow only, and CPM payouts per video with an atLeast views condition too. Omit to pay every copy.
            - `platforms` string[]: Platforms this entry applies to. Omit for all platforms. Entries of the same family may share platforms: a video on a shared platform earns each entry that covers it, and the dry-run warns.
            - `conditions` object: Which videos take part in this payout: content type (video/slideshow), tags (custom, project or geography tag ids; anyOf, allOf, noneOf), hashtags (anyOf, allOf, noneOf), views (atLeast or below, per video), totalViews (atLeast or below, the creator's total views) and totalVideos (atLeast or below, the creator's number of videos). On a per-video payout a video must match to be paid; on an all-content payout only matching videos' views add up. Views conditions work per video: base payouts count a video in the period it reaches an atLeast threshold (countWhen reached, the default) or only by the end of its publication period (countWhen byPublicationPeriodEnd, the same as minViews, and the only variant of a base below); per-video CPM payouts and bonuses pay a video's earlier views once it reaches an atLeast threshold, and pay a below one only for periods the video ends under the count. totalViews judges each payout period on the creator's total views over the payout's platforms, whatever the other conditions say (countFrom periodVideos: views of the videos published in the period, counted to its end; eligibleVideos: views gained in the period by every video inside its eligibility window): a period that misses pays nothing through this payout, and its views are never paid later. totalVideos judges each payout period on every video the creator published in it, on every platform (a post on three platforms counts 3), whatever the other conditions and the payout's platforms say; its value is a count or "videoTarget" (the campaign's video target, or the creator's own with custom rates, never prorated, counted in the target's unit: under creatives each video once whatever platforms it is on, under per_platform every listed platform must reach its own count and other platforms do not count). A period that misses pays nothing through this payout; on a base payout an atLeast row may carry the missed period's videos instead (carry previousPeriod: paid in the next period if that one reaches its own minimum; untilPaid: every missed period waits for the first that does), priced as if paid in the period they were posted in (its rate steps, its end and its base payout cap, never the paying period's), only directly in a match all group. One atLeast and one below make a range. Periods follow the campaign's schedule.timeZone; views are counted per UTC day. The platform list always applies on top.
              - …
            - `tiers` object[], required
              - …
          - `flatBonuses` object[]
            - `scope` 'per_video' | 'per_creative' | 'all_content': per_video: applied to every video with its own views. per_creative: copies of one video posted to several platforms (grouped as a creative) count as one video, their views added up; a video in no creative counts on its own. all_content: applied once per cycle to the creator's aggregated views.
            - `creatives` 'bestInPeriod' | 'bestInWindow': Pay only each creative's winner: when the creator posts the same video to several of this payout's platforms (copies grouped as a creative), only the copy with the most views earns it, judged in the payout period (bestInPeriod) or in each copy's eligibility window (bestInWindow). A creative is never paid twice. Needs two or more platforms; not with scope per_creative or a current_period_only eligibility; bonuses per video take bestInWindow only, and CPM payouts per video with an atLeast views condition too. Omit to pay every copy.
            - `mode` 'highest' | 'aggregated': highest: only the highest unlocked tier is paid. aggregated: every unlocked tier adds its amount. Defaults to highest with two or more tiers or a tier that carries `to`, aggregated for a single open-ended tier.
            - `platforms` string[]: Platforms this entry applies to. Omit for all platforms. Entries of the same family may share platforms: a video on a shared platform earns each entry that covers it, and the dry-run warns.
            - `conditions` object: Which videos take part in this payout: content type (video/slideshow), tags (custom, project or geography tag ids; anyOf, allOf, noneOf), hashtags (anyOf, allOf, noneOf), views (atLeast or below, per video), totalViews (atLeast or below, the creator's total views) and totalVideos (atLeast or below, the creator's number of videos). On a per-video payout a video must match to be paid; on an all-content payout only matching videos' views add up. Views conditions work per video: base payouts count a video in the period it reaches an atLeast threshold (countWhen reached, the default) or only by the end of its publication period (countWhen byPublicationPeriodEnd, the same as minViews, and the only variant of a base below); per-video CPM payouts and bonuses pay a video's earlier views once it reaches an atLeast threshold, and pay a below one only for periods the video ends under the count. totalViews judges each payout period on the creator's total views over the payout's platforms, whatever the other conditions say (countFrom periodVideos: views of the videos published in the period, counted to its end; eligibleVideos: views gained in the period by every video inside its eligibility window): a period that misses pays nothing through this payout, and its views are never paid later. totalVideos judges each payout period on every video the creator published in it, on every platform (a post on three platforms counts 3), whatever the other conditions and the payout's platforms say; its value is a count or "videoTarget" (the campaign's video target, or the creator's own with custom rates, never prorated, counted in the target's unit: under creatives each video once whatever platforms it is on, under per_platform every listed platform must reach its own count and other platforms do not count). A period that misses pays nothing through this payout; on a base payout an atLeast row may carry the missed period's videos instead (carry previousPeriod: paid in the next period if that one reaches its own minimum; untilPaid: every missed period waits for the first that does), priced as if paid in the period they were posted in (its rate steps, its end and its base payout cap, never the paying period's), only directly in a match all group. One atLeast and one below make a range. Periods follow the campaign's schedule.timeZone; views are counted per UTC day. The platform list always applies on top.
              - …
            - `tiers` object[], required
              - …
          - `baseCap` object: The most all base payouts together pay per cycle, rate steps included.
            - `id` string: Stable id of the stored rule (read it from GET). Send it back to keep the rule, and any creator's custom rate on it, attached through edits; omit it for new entries.
            - `amount` number, required: Amount in major units of the campaign currency (10 = 10.00).
          - `performanceCap` object: Maximum combined CPM payouts and flat bonuses per cycle. Base payout and fixed salary are not affected. Needs at least one CPM payout or flat bonus.
            - `id` string: Stable id of the stored rule (read it from GET). Send it back to keep the rule, and any creator's custom rate on it, attached through edits; omit it for new entries.
            - `amount` number, required: Amount in major units of the campaign currency (10 = 10.00).
          - `eligibility` object: How long and which videos count toward performance payouts.
            - `windowDays` number: Days after publication a video keeps counting toward performance payouts.
            - `publicationScope` 'include_earlier' | 'current_period_only': include_earlier: videos published before the period still count while inside their window. current_period_only: only videos published within the period count, and windows are cut off at period end.
      - `briefId` string, nullable, required
      - `createdAt` string, date-time, required
      - `updatedAt` string, date-time, required

## Acknowledgement `200`

Any 2xx within 15 seconds acknowledges the event. Anything else, a timeout or a redirect is retried (10 attempts over about 3 days); 410 disables the endpoint.

---

[API](https://skmtc.dev/viral/apis/viral-app-api.md) · [All operations](https://skmtc.dev/viral/apis/viral-app-api/llms.txt) · [OpenAPI document](https://skmtc.dev/viral/apis/viral-app-api/revisions/31e97fa79ef1?raw)
