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

# Calculate payout details

`POST /payouts/calculate`

## Request

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

Runs the payout engine for a specific creator and billing window, returning the finalized calculation payload, the default server-generated line items, the `remainder` still to pay after earlier partial payments, and an `integrityToken`. Use the returned `calculation` plus `integrityToken` as the basis for `POST /payouts/initiate` (`purpose: "settle"`, the default) or `POST /payouts/partial` (`purpose: "partial"`). A calculation of a window that is still in progress is a to-date number and expires after 24 hours.

## Request body

- object
  - `campaignId` string, required: Campaign ID with orgcamp_ prefix
  - `creatorId` string, required: Organization creator ID with orgcre_ prefix
  - `billingPeriodStart` string, required
  - `billingPeriodEnd` string, required
  - `windowValidation` 'completed' | 'projection'
  - `excludedVideos` object[]: Manually exclude these videos from the calculation before the rules they name evaluate (every rule without `ruleIds`) — base counts, performance sums, and milestone gates all recompute without them. The result echoes the applied set as metrics.manuallyExcludedVideos, one entry per video with its merged `ruleIds`. Note: excluding a video whose one-time milestone bonus settles in this billing window forfeits that bonus permanently (it cannot re-fire in a later window).
    - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
    - `platformVideoId` string, required
    - `ruleIds` string[]: Take the video out of these rules only — the `ruleIds` of the calculation's breakdown rows (a row sums its rules), e.g. out of a performance bonus while it still counts toward the base payout. Omit to take it out of every rule. Each id must be a metric rule of the campaign.
  - `purpose` 'settle' | 'partial': "settle" prices the payout that closes the billing window: POST /payouts/initiate, which for a window still in progress needs `windowValidation: "projection"` plus `settleToday: true` here and `allowInProgress: true` there. "partial" prices a payment on account for POST /payouts/partial, on a window still in progress only (a window that has ended answers 400): the window stays open, and fixed salary is prorated by the days elapsed so far. Either way the result lists the partial payments already made toward the window (`settlement.partials`), and a settle carries them as negative line items.
  - `settleToday` boolean: Settle & end: price a window that is still in progress through today, as the final period of an assignment that ends today (POST /payouts/initiate with `allowInProgress: true` then ends it). The result's `period.end` is today and the `integrityToken` pins that period, so send `period.end` back as `billingPeriodEnd`. Needs `windowValidation: "projection"` and `purpose: "settle"`. Without it, a projection is a preview of the whole window.
  - `includedVideos` object[]: Price ONLY these videos — a partial payment for specific videos (`purpose: "partial"`). A fixed salary is left out, and every other rule runs on the selected videos as if they were the whole window (campaign-wide tiers and a performance cap evaluate on that subset alone). Echoed as settlement.includedVideos. Cannot be combined with excludedVideos.
    - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
    - `platformVideoId` string, required

## Response `200`

OK

- object
  - `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
            - object
              - …
            - object
              - …
        - `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
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
        - `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
            - `platform` 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'snapchat', required: Social media platform (facebook, instagram, tiktok, youtube, snapchat)
            - `platformVideoId` string, required
            - `views` number
            - `publishedDate` string: That copy's UTC publish day (YYYY-MM-DD)
      - `omittedCount` integer, required
      - `failedGates` object[]
        - `condition` union, required
          - object
            - `field` 'totalViews', required
            - `operator` 'atLeast' | 'below', required
            - `value` integer, required
            - `countFrom` 'periodVideos' | 'eligibleVideos', required
          - object
            - `field` 'totalVideos', required
            - `operator` 'atLeast' | 'below', required
            - `value` union, required
              - …
            - `carry` 'previousPeriod' | 'untilPaid'
            - `count` 'creatives'
            - `platforms` 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
            - `type` 'flat', required
            - `amount` integer, required
            - `precision` integer, required
        - object
          - `version` unknown, required
          - `id` string, required
          - `kind` 'metric', required
          - `name` string, required
          - `priority` integer, required
          - `fact` union, required
            - object
              - …
            - object
              - …
          - `tier` object, required
            - `index` integer
            - `min` object, required
              - …
            - `max` object, nullable, required
              - …
          - `payout` union, required
            - object
              - …
            - object
              - …
        - object
          - `version` unknown, required
          - `id` string, required
          - `kind` 'cap', required
          - `name` 'performance_total_cap', required
          - `priority` integer, required
          - `appliesTo` 'performance', required
          - `cap` object, required
            - `amount` integer, required
            - `precision` integer, 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
            - `amount` integer, required
            - `precision` integer, 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
            - union
              - …
      - `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
            - `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
      - `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
            - object
              - …
            - object
              - …
        - `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.
  - `remainder` object, required
    - `amount` integer, required
    - `value` number, required
    - `overpaidAmount` integer, 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)
