---
title: "Calculate a video payout"
method: POST
path: "/payouts/videos/calculate"
tags: ["Payouts"]
---

# Calculate a video payout

`POST /payouts/videos/calculate`

## Request

- Base URL: `https://viral.app/api/v1`
- URL: `https://viral.app/api/v1/payouts/videos/calculate`
- Auth: API key in header `x-api-key`

Prices specific videos of a creator at terms of their own, outside any billing window — for deals that differ per video. `terms` come from a campaign (`source: "campaign"`; a campaign without creators works as a rate card), are sent inline in the campaign spec's `payouts` vocabulary (`source: "inline"`), or are a fixed amount (`source: "fixed"`). Rule-based terms must price single videos: base payout per video, per-video CPM and per-video bonuses. Every video must sit on one of the creator's own tracked accounts. The payout buys views from the videos' publish day through yesterday, the last complete day; a repeat payout continues the day after the last one. When the creator is assigned to a campaign that would pay the same videos, `campaignRelation` is required: `"instead_of"` takes the videos out of every later campaign calculation for the creator, `"on_top"` is a bonus. Returns the signed `context` and `calculation`, default `lineItems`, `notices`, and the `integrityToken` for `POST /payouts/videos/initiate`; the calculation expires after 24 hours. A request that cannot be priced is a 400 `VIDEO_PAYOUT_INVALID` whose `data.issues` name the path and the reason.

## Request body

- object
  - `creatorId` string, required: Organization creator ID with orgcre_ prefix
  - `videos` object[], required: The videos to pay. Each must sit on one of the creator's own tracked accounts.
    - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
    - `platformVideoId` string, required
  - `terms` union, required
    - object
      - `source` 'campaign', required
      - `campaignId` string, required: The campaign whose payout rules price the videos. It needs no creators assigned — a campaign kept only as a rate card works. Its rules must all price single videos: base payout per video, per-video CPM and per-video bonuses. A fixed salary, a performance cap, all-content, per-platform, per-account or engagement-rate rules are refused with `issues`.
    - object
      - `source` 'inline', required
      - `currency` 'USD' | 'EUR' | 'CHF' | 'GBP' | 'CNY' | 'JPY' | 'KRW' | 'THB' | 'IDR' | 'VND' | 'RUB', required
      - `payouts` object, required: Ad-hoc terms in the campaign spec's `payouts` vocabulary (major units), without creating a campaign. The same per-video restriction applies.
        - `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.
            - `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.
            - `startsAfter` union, required: Videos before the step: a count or "videoTarget". Rises and stays below the end.
              - …
            - `perVideo` number, required: Paid per video from this step on.
          - `limit` object, nullable: Omitted: the base payout stops at the video target. null: unlimited, every extra video is paid.
            - `videos` union: Videos paid per cycle: a count (above the target: an overshoot allowance), or "videoTarget".
              - …
            - `amount` number: Deprecated: payouts.baseCap. Only with one base payout; without videos only this cap limits it.
          - `minViews` integer: 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.
            - `match` 'all' | 'any': all: a video must meet every condition. any: one condition is enough.
            - `items` union[], required
              - …
          - `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.
            - `match` 'all' | 'any': all: a video must meet every condition. any: one condition is enough.
            - `items` union[], required
              - …
          - `tiers` object[], required
            - `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.
            - `from` integer, required: Views where the tier starts (inclusive).
            - `to` integer, nullable: Views where the tier ends (exclusive); the next tier's from must equal it. Omit or null on the last tier for an open end.
            - `rate` number, required: Paid per 1,000 views in this tier.
        - `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.
            - `match` 'all' | 'any': all: a video must meet every condition. any: one condition is enough.
            - `items` union[], required
              - …
          - `tiers` object[], required
            - `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.
            - `from` integer, required: Views where the tier starts (inclusive).
            - `to` integer, nullable: Views where the tier ends (exclusive); the next tier's from must equal it. Omit or null on the last tier for an open end.
            - `amount` number, required: Bonus paid when the tier is reached.
        - `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` integer: 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.
    - object
      - `source` 'fixed', required
      - `currency` 'USD' | 'EUR' | 'CHF' | 'GBP' | 'CNY' | 'JPY' | 'KRW' | 'THB' | 'IDR' | 'VND' | 'RUB', required
      - `amount` number, required: A fixed amount for the videos together, in major units of `currency`.
      - `description` string: Invoice line shown to the creator. Defaults to "Video Payout".
  - `campaignRelation` 'instead_of' | 'on_top': REQUIRED whenever one of the creator's campaign assignments overlaps the paid period — otherwise that campaign would pay the same views again. "instead_of": the videos are paid at these terms INSTEAD of the campaign's, and every later calculation of the creator's campaigns leaves them out. "on_top": a bonus; the campaign still pays the videos normally. To pay videos early at the campaign's OWN terms, use POST /payouts/partial with `includedVideos`.
  - `periodStart` string, date: First UTC day the payout buys views for. Defaults to the videos' publish date, or for a repeat payout to the day after the last one. For "instead_of" it is derived (the day after what was already paid) and cannot be moved earlier.
  - `periodEnd` string, date: Last UTC day the payout buys views for. Defaults to yesterday, the last complete day; today or later is refused, because the rest of a half-finished day could never be paid afterwards.

## Response `200`

OK

- object
  - `context` object, required
    - `videos` object[], required
      - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
      - `platformVideoId` string, required
    - `relation` 'instead_of' | 'advance' | 'on_top' | 'none', required
    - `coveringCampaignId` string, nullable, required: Campaign ID with orgcamp_ prefix
    - `periodStart` string, date, required
    - `periodEnd` string, date, required
    - `termsSource` 'campaign' | 'inline' | 'fixed', required
    - `termsCampaignId` string, nullable, required: Campaign ID with orgcamp_ prefix
    - `currency` 'USD' | 'EUR' | 'CHF' | 'GBP' | 'CNY' | 'JPY' | 'KRW' | 'THB' | 'IDR' | 'VND' | 'RUB', required
    - `fixedAmount` integer, nullable, required
    - `calculatedAt` string, required
  - `calculation` object, nullable, required
    - `period` object, required
      - `index` integer, required
      - `label` string, required
      - `start` string, required
      - `end` string, required
    - `payouts` object, required
      - `total` object, required
        - `amount` number, required
        - `precision` integer, required
        - `currency` string, required
        - `value` number, required
      - `breakdown` object[], required
        - `label` string, required
        - `ruleIds` string[]
        - `ruleName` string, nullable, required
        - `payoutKind` 'flat' | 'rate', required
        - `payoutMode` 'allocation' | 'progressive'
        - `amount` number, required
        - `precision` integer, required
        - `currency` string, required
        - `value` number, required
        - `calculationDetails` object, nullable, required
          - `metricKey` string, required
          - `metricValue` number, required
          - `grain` 'aggregate' | 'per_video' | 'per_platform' | 'per_account'
          - `rawMetricValue` number
          - `metricStartValue` number
          - `metricEndValue` number
          - `eligibleVideoCount` integer
          - `minViewsRequired` integer
          - `conditioned` unknown
          - `creatives` 'once' | 'best' | 'sum': How this row's payouts treat the copies of one creative (the same video posted to several platforms): once, a base payout counts each creative once, on its winner (the copy with the most views); best, a CPM payout or bonus pays only the creative's winner; sum, the per_creative scope: the copies count as one, their views added up on one copy. Absent when the row pays every copy
          - `ratePerUnit` number
          - `perQuantity` integer
          - `mode` 'allocation' | 'progressive'
          - `payoutTiming` 'gains' | 'milestone'
          - `appliedTierIndex` integer
          - `tierBreakdown` union[]
            - union
              - …
          - `maxPayoutApplied` boolean
          - `performanceCapApplied` boolean
          - `baseCapApplied` boolean
          - `earlySettledTopTier` boolean
          - `carriedVideoCount` integer
          - `carriedAmount` number
          - `carriedWindows` object[]
            - `start` string, required
            - `end` string, required
            - `videoCount` integer, required
          - `carriedWaiting` unknown
        - `off` unknown
      - `performanceCap` object
        - `amount` number, required
        - `precision` integer, required
        - `currency` string, required
        - `value` number, required
        - `applied` boolean, required
      - `baseCap` object
        - `amount` number, required
        - `precision` integer, required
        - `currency` string, required
        - `value` number, required
        - `applied` boolean, required
    - `metrics` object, required
      - `facts` object[], required
        - `factName` string, required
        - `metric` 'views' | 'likes' | 'shares' | 'comments' | 'videoCount' | 'engagementRate', required
        - `label` string, required
        - `total` number, required
      - `perVideo` object, nullable, required
        - `metric` 'views' | 'likes' | 'shares' | 'comments' | 'videoCount' | 'engagementRate', required
        - `label` string, required
        - `items` object[], required
          - `id` string, nullable, required
          - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
          - `platformVideoId` string, required
          - `value` number, required
          - `title` string, nullable, required
          - `thumbnailUrl` string, nullable, required
          - `publishedAt` string, nullable, required
          - `accountUsername` string, nullable, required
          - `platformAccountId` string, nullable, required
          - `isCompetitor` boolean, required
          - `contentType` 'video' | 'slideshow', nullable, required: Filter by content types (video, slideshow)
          - `orgAccountId` string, nullable, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
          - `payoutAmount` number, nullable, required
          - `payoutCurrency` string, nullable, required
          - `payoutValue` number, nullable, required
          - `creativeCopies` integer: A per-creative payout's row: this video carries its creative, this many copies in all (itself included). On a CPM payout or bonus its value adds up every copy's views; on a base payout it keeps its own views and the creative counts once. The other copies are listed in metrics.mergedCopies
      - `perCategory` object[], nullable, required
        - `category` string, required
        - `metric` 'views' | 'likes' | 'shares' | 'comments' | 'videoCount' | 'engagementRate', required
        - `metricKey` string, required
        - `metricLabel` string, required
        - `items` object[], required
          - `id` string, nullable, required
          - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
          - `platformVideoId` string, required
          - `value` number, required
          - `title` string, nullable, required
          - `thumbnailUrl` string, nullable, required
          - `publishedAt` string, nullable, required
          - `accountUsername` string, nullable, required
          - `platformAccountId` string, nullable, required
          - `isCompetitor` boolean, required
          - `contentType` 'video' | 'slideshow', nullable, required: Filter by content types (video, slideshow)
          - `orgAccountId` string, nullable, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
          - `payoutAmount` number, nullable, required
          - `payoutCurrency` string, nullable, required
          - `payoutValue` number, nullable, required
          - `creativeCopies` integer: A per-creative payout's row: this video carries its creative, this many copies in all (itself included). On a CPM payout or bonus its value adds up every copy's views; on a base payout it keeps its own views and the creative counts once. The other copies are listed in metrics.mergedCopies
      - `viewBasis` 'total' | 'organic'
      - `manuallyExcludedVideos` object[]
        - `id` string, nullable, required
        - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
        - `platformVideoId` string, required
        - `value` number, required
        - `title` string, nullable, required
        - `thumbnailUrl` string, nullable, required
        - `publishedAt` string, nullable, required
        - `accountUsername` string, nullable, required
        - `platformAccountId` string, nullable, required
        - `isCompetitor` boolean, required
        - `contentType` 'video' | 'slideshow', nullable, required: Filter by content types (video, slideshow)
        - `orgAccountId` string, nullable, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
        - `payoutAmount` number, nullable, required
        - `payoutCurrency` string, nullable, required
        - `payoutValue` number, nullable, required
        - `creativeCopies` integer: A per-creative payout's row: this video carries its creative, this many copies in all (itself included). On a CPM payout or bonus its value adds up every copy's views; on a base payout it keeps its own views and the creative counts once. The other copies are listed in metrics.mergedCopies
        - `ruleIds` string[]
      - `conditionMisses` object[]
        - `category` string, required
        - `ruleIds` string[], required
        - `scope` 'per_video' | 'aggregate', required
        - `items` object[], required
          - `id` string, nullable, required
          - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
          - `platformVideoId` string, required
          - `value` number, required
          - `title` string, nullable, required
          - `thumbnailUrl` string, nullable, required
          - `publishedAt` string, nullable, required
          - `accountUsername` string, nullable, required
          - `platformAccountId` string, nullable, required
          - `isCompetitor` boolean, required
          - `contentType` 'video' | 'slideshow', nullable, required: Filter by content types (video, slideshow)
          - `orgAccountId` string, nullable, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
          - `payoutAmount` number, nullable, required
          - `payoutCurrency` string, nullable, required
          - `payoutValue` number, nullable, required
          - `creativeCopies` integer: A per-creative payout's row: this video carries its creative, this many copies in all (itself included). On a CPM payout or bonus its value adds up every copy's views; on a base payout it keeps its own views and the creative counts once. The other copies are listed in metrics.mergedCopies
          - `failed` union[], required
            - union
              - …
          - `wouldEarn` number, nullable
          - `creative` object: Set when a creative payout left this copy out (the group's creatives option): the reason, and the copy that earns instead. failed is empty then: the creative option, not a condition, left the copy out
            - `reason` 'not_best' | 'counted_before' | 'counted_with' | 'bought_out' | 'earlier_period' | 'creative_views', required: Why this copy earned nothing here. not_best: another copy of the creative has more views and is the one a winner-only CPM payout or bonus pays. counted_with: another copy (the winner) counts the creative once in this period. counted_before: the creative was already counted in an earlier period. bought_out: a copy of the creative was paid on its own (a video payout instead of the campaign's pay), which settles the creative for this payout. earlier_period: the creative was first posted in an earlier period and judged there (a per-creative base payout with a Video views condition). creative_views: the copies' combined views (views) fall outside that Video views condition
            - `views` number: This copy's views over the payout's time frame; for creative_views, the creative's combined views
            - `by` object, required: The copy that earns or counts instead: the creative's winner, the copy counted earlier or the bought-out copy; for earlier_period the copy posted in that earlier period, for creative_views the creative's leading copy
              - …
        - `omittedCount` integer, required
        - `failedGates` object[]
          - `condition` union, required
            - object
              - …
            - object
              - …
          - `total` number, required
        - `forfeit` object
          - `amount` number, required
          - `precision` integer, required
          - `currency` string, required
          - `value` number, required
        - `creatives` union: The group lists copies a creative payout did not pay (each item carries creative): the payout's option (once, best or sum, as on calculationDetails.creatives), or true on calculations from before it was recorded
          - unknown
          - 'once' | 'best' | 'sum'
      - `carriedVideos` object[]: Per base payout row, the earlier periods' videos it pays as carried (a missed total videos minimum with a carry), or that wait for a period that reaches the minimum
        - `category` string, required: The breakdown row's label
        - `ruleIds` string[], required
        - `status` 'paid' | 'waiting', required
        - `items` object[], required
          - `id` string, nullable, required
          - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
          - `platformVideoId` string, required
          - `value` number, required
          - `title` string, nullable, required
          - `thumbnailUrl` string, nullable, required
          - `publishedAt` string, nullable, required
          - `accountUsername` string, nullable, required
          - `platformAccountId` string, nullable, required
          - `isCompetitor` boolean, required
          - `contentType` 'video' | 'slideshow', nullable, required: Filter by content types (video, slideshow)
          - `orgAccountId` string, nullable, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
          - `payoutAmount` number, nullable, required
          - `payoutCurrency` string, nullable, required
          - `payoutValue` number, nullable, required
          - `creativeCopies` integer: A per-creative payout's row: this video carries its creative, this many copies in all (itself included). On a CPM payout or bonus its value adds up every copy's views; on a base payout it keeps its own views and the creative counts once. The other copies are listed in metrics.mergedCopies
          - `carriedFrom` object, required: The missed period the video was posted in (YYYY-MM-DD)
            - `start` string, required
            - `end` string, required
        - `omittedCount` integer, required
      - `mergedCopies` object[]: Per breakdown row of a per-creative payout, the copies that counted with another copy of their creative (dialog and breakdown calculations only). Not misses: they are paid through that copy
        - `category` string, required: The breakdown row's label
        - `ruleIds` string[], required
        - `pay` 'once' | 'sum', required: once: a base payout counted the creative once, on the into copy (each copy keeps its own views). sum: a CPM payout or bonus added the copies' views up on the into copy
        - `items` object[], required
          - `id` string, nullable, required
          - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
          - `platformVideoId` string, required
          - `value` number, required
          - `title` string, nullable, required
          - `thumbnailUrl` string, nullable, required
          - `publishedAt` string, nullable, required
          - `accountUsername` string, nullable, required
          - `platformAccountId` string, nullable, required
          - `isCompetitor` boolean, required
          - `contentType` 'video' | 'slideshow', nullable, required: Filter by content types (video, slideshow)
          - `orgAccountId` string, nullable, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
          - `payoutAmount` number, nullable, required
          - `payoutCurrency` string, nullable, required
          - `payoutValue` number, nullable, required
          - `creativeCopies` integer: A per-creative payout's row: this video carries its creative, this many copies in all (itself included). On a CPM payout or bonus its value adds up every copy's views; on a base payout it keeps its own views and the creative counts once. The other copies are listed in metrics.mergedCopies
          - `into` object, required: The copy the row lists, which this copy counted with
            - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
            - `platformVideoId` string, required
        - `omittedCount` integer, required
      - `conditionTagNames` object
      - `conditionForfeit` object
        - `amount` number, required
        - `precision` integer, required
        - `currency` string, required
        - `value` number, required
      - `videoGates` object[]
        - `ruleId` string, required
        - `required` integer, required
        - `count` integer, required
        - `met` boolean, required
    - `settlement` object
      - `purpose` 'settle' | 'partial', required
      - `calculatedAt` string, required
      - `inProgress` boolean, required
      - `fixedElapsedFactor` number, required
      - `partials` object[], required
        - `payoutId` string, required
        - `amount` integer, required
        - `coveredAmount` integer
        - `paidAt` string, required
      - `separatelyPaidVideos` object[], required
        - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
        - `platformVideoId` string, required
      - `placedVideos` object[]
        - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
        - `platformVideoId` string, required
      - `includedVideos` object[]
        - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
        - `platformVideoId` string, required
      - `carriedVideos` object[]
        - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
        - `platformVideoId` string, required
        - `periodStart` string, required
        - `periodEnd` string, required
        - `ruleId` string
      - `priorCarriedVideos` object[]
        - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
        - `platformVideoId` string, required
      - `carryStops` object[]
        - `ruleId` string, required
        - `periodStart` string, required
      - `timeZone` string: 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.
      - `viewsOpenUntil` string
      - `closure` object
        - `kind` 'covered' | 'written_off', required
        - `overpaidAmount` integer, required
        - `writtenOffAmount` integer, required
    - `customRates` boolean
    - `engine` object, required
      - `prorationFactor` number, required
      - `rulesSnapshot` union[], required
        - union
          - object
            - `version` unknown, required
            - `id` string, required
            - `kind` 'fixed', required
            - `name` 'fixed_salary', required
            - `priority` integer, required
            - `payout` object, required
              - …
          - object
            - `version` unknown, required
            - `id` string, required
            - `kind` 'metric', required
            - `name` string, required
            - `priority` integer, required
            - `fact` union, required
              - …
            - `tier` object, required
              - …
            - `payout` union, required
              - …
          - object
            - `version` unknown, required
            - `id` string, required
            - `kind` 'cap', required
            - `name` 'performance_total_cap', required
            - `priority` integer, required
            - `appliesTo` 'performance', required
            - `cap` object, required
              - …
          - object
            - `version` unknown, required
            - `id` string, required
            - `kind` 'cap', required
            - `name` 'base_total_cap', required
            - `priority` integer, required
            - `appliesTo` 'base', required
            - `cap` object, required
              - …
      - `factTraces` object[], required
        - `id` string, required
        - `ruleId` string, nullable, required
        - `ruleName` string, nullable, required
        - `source` 'global' | 'fact', required
        - `factType` 'fixed' | 'metric' | 'snapshot_ratio', required
        - `requirement` object, nullable, required
          - `type` 'metric' | 'snapshot_ratio', required
          - `metric` 'view_gain' | 'like_gain' | 'comment_gain' | 'share_gain' | 'video_count' | 'engagement_rate', required
          - `grain` 'aggregate' | 'per_video' | 'per_platform' | 'per_account', required
          - `aggregation` 'sum' | 'avg' | 'count' | 'min' | 'max', nullable, required
          - `formula` 'engagement_actions_per_view', nullable, required
          - `windowDays` integer, required
          - `publicationScope` 'include_earlier' | 'billing_period_only', required
          - `assignmentPublicationMode` 'assigned_only' | 'include_pre_assignment'
          - `platformFilter` object
            - `include` string[]
            - `exclude` string[]
          - `videoFilter` object
            - `minViews` integer, required
          - `conditions` object
            - `match` 'all' | 'any', required
            - `items` union[], required
              - …
        - `payoutKind` 'flat' | 'rate', required
        - `payoutMode` 'allocation' | 'progressive'
        - `payoutTiming` 'gains' | 'milestone'
        - `tier` object, nullable, required
          - `min` object, required
            - `value` number, required
            - `bound` 'exclusive' | 'inclusive', required
          - `max` object, nullable, required
            - `value` number, required
            - `bound` 'exclusive' | 'inclusive', required
        - `billingWindow` object, required
          - `start` string, required
          - `end` string, required
          - `anchor` '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.
        - `factCount` integer, required
        - `requiresProgressiveBaseline` boolean, required
        - `requiresMilestoneFacts` boolean, required
      - `engineTotals` object, required
        - `amount` number, required
        - `precision` integer, required
        - `currency` string, required
        - `value` number, required
      - `engineEvents` object[], required
        - `ruleId` string, nullable
        - `ruleName` string, nullable
        - `ruleIndex` integer
        - `payoutKind` 'flat' | 'rate', required
        - `payoutMode` 'allocation' | 'progressive'
        - `source` 'global' | 'fact', required
        - `factTraceId` string, required
        - `factTrace` object, required
          - `id` string, required
          - `ruleId` string, nullable, required
          - `ruleName` string, nullable, required
          - `source` 'global' | 'fact', required
          - `factType` 'fixed' | 'metric' | 'snapshot_ratio', required
          - `requirement` object, nullable, required
            - `type` 'metric' | 'snapshot_ratio', required
            - `metric` 'view_gain' | 'like_gain' | 'comment_gain' | 'share_gain' | 'video_count' | 'engagement_rate', required
            - `grain` 'aggregate' | 'per_video' | 'per_platform' | 'per_account', required
            - `aggregation` 'sum' | 'avg' | 'count' | 'min' | 'max', nullable, required
            - `formula` 'engagement_actions_per_view', nullable, required
            - `windowDays` integer, required
            - `publicationScope` 'include_earlier' | 'billing_period_only', required
            - `assignmentPublicationMode` 'assigned_only' | 'include_pre_assignment'
            - `platformFilter` object
              - …
            - `videoFilter` object
              - …
            - `conditions` object
              - …
          - `payoutKind` 'flat' | 'rate', required
          - `payoutMode` 'allocation' | 'progressive'
          - `payoutTiming` 'gains' | 'milestone'
          - `tier` object, nullable, required
            - `min` object, required
              - …
            - `max` object, nullable, required
              - …
          - `billingWindow` object, required
            - `start` string, required
            - `end` string, required
            - `anchor` '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.
          - `factCount` integer, required
          - `requiresProgressiveBaseline` boolean, required
          - `requiresMilestoneFacts` boolean, required
        - `payout` object, required
          - `amount` number, required
          - `precision` integer, required
          - `currency` string, required
          - `value` number, required
        - `fact` object, required
          - `creatorId` string, required
          - `campaignId` string, required
          - `billingPeriodStart` string, required
          - `billingPeriodEnd` string, required
          - `prorationFactor` number, required
        - `details` object, nullable, required
          - `metricKey` string, required
          - `metricValue` number, required
          - `grain` 'aggregate' | 'per_video' | 'per_platform' | 'per_account'
          - `rawMetricValue` number
          - `metricStartValue` number
          - `metricEndValue` number
          - `eligibleVideoCount` integer
          - `minViewsRequired` integer
          - `conditioned` unknown
          - `creatives` 'once' | 'best' | 'sum': How this row's payouts treat the copies of one creative (the same video posted to several platforms): once, a base payout counts each creative once, on its winner (the copy with the most views); best, a CPM payout or bonus pays only the creative's winner; sum, the per_creative scope: the copies count as one, their views added up on one copy. Absent when the row pays every copy
          - `ratePerUnit` number
          - `perQuantity` integer
          - `mode` 'allocation' | 'progressive'
          - `payoutTiming` 'gains' | 'milestone'
          - `appliedTierIndex` integer
          - `tierBreakdown` union[]
            - union
              - …
          - `maxPayoutApplied` boolean
          - `performanceCapApplied` boolean
          - `baseCapApplied` boolean
          - `earlySettledTopTier` boolean
          - `carriedVideoCount` integer
          - `carriedAmount` number
          - `carriedWindows` object[]
            - `start` string, required
            - `end` string, required
            - `videoCount` integer, required
          - `carriedWaiting` unknown
      - `engineFacts` object[], required
        - `creatorId` string, required
        - `campaignId` string, required
        - `billingPeriodStart` string, required
        - `billingPeriodEnd` string, required
        - `prorationFactor` number, required
        - `interval` 'day' | 'week' | 'month', required
        - `intervalCount` integer, required
        - `fixedElapsedFactor` number
        - `totalViews` number
        - `totalEarnings` number
        - `totalImpressions` number
      - `engineWarnings` string[], required
      - `accountSnapshot` object, required
        - `snapshotVersion` unknown
        - `accounts` object[], required
          - `orgAccountId` string, required: Organization account ID with orgacc_ prefix. Not the same as platform account ID.
          - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
          - `platformAccountId` string, required: Native platform account id
          - `maxVideos` integer, required
          - `hashtagsFilter` string[], nullable
        - `excludedVideosHash` string, required
        - `excludedVideosCount` integer, required
        - `creativeGroups` object
          - `creativeGroupsHash` string, required
          - `creativeGroupsCount` integer, required
  - `lineItems` object[], required
    - `title` string, required
    - `amount` number, required
    - `kind` 'adjustment': Set to "adjustment" for a line whose amount is a percentage of the payout's base (its positive, non-adjustment lines). Send `percent` with it; the amount must equal that percentage of the base, rounded to the currency's minor unit.
    - `percent` number: The adjustment's percentage of the base, -100 to 100 and not 0; a negative value deducts, a positive one adds.
  - `total` object, required
    - `value` number, required
    - `currency` 'USD' | 'EUR' | 'CHF' | 'GBP' | 'CNY' | 'JPY' | 'KRW' | 'THB' | 'IDR' | 'VND' | 'RUB', required
  - `notices` string[], required
  - `integrityToken` string, required: Integrity token returned alongside the calculation. Pass it unchanged to POST /payouts/initiate together with the calculation payload.

## Other responses

- `400`: 400
- `401`: 401
- `402`: 402
- `404`: 404
- `408`: 408
- `429`: 429
- `500`: 500

---

[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)
