> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.usegrade.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a campaign creator contract version

> Add a dated contract version to a campaign creator's contract timeline.

## POST /sdk/v1/campaign-creators/:campaignCreatorRecordId/contract-history

Adds a contract version that becomes effective for media uploaded on or after
the selected date. This is the same contract timeline used throughout Grade.

All requests require `X-Application-Id` and `X-Api-Key`.

### Path parameter

* `campaignCreatorRecordId` (required): the campaign creator whose contract you
  want to update.

### Body

* `effectiveDate` (required): a real date in `YYYY-MM-DD` format.
* `basedOnVersionId` (required): the `versionId` of the contract version that
  currently governs `effectiveDate`. Get it from
  [Read campaign creator contract history](/campaign-creators/contract-history).
  Send an explicit JSON `null` only when that response has an empty `history`
  and both `current` and `nextScheduled` are `null`. This creates the first
  contract without guessing that the timeline is still empty.
* `contract` (required): the complete contract terms that should apply from
  that date.

<Warning>
  The contract must be complete and explicit. Do not omit fields to imply a
  default or to inherit an earlier value. Start with the complete `contract`
  returned by the history GET, change the intended values, and send every
  field back. Use `null`, `false`, or `[]` explicitly where shown.
</Warning>

Grade-managed test-phase cutoffs, template identity, and internal history IDs
are intentionally not writable through this public shape. Existing values are
preserved from the governing unified version; for a first contract, test-phase
guards are copied from the creator record. Payment amounts, source currencies,
conversion behavior, eligibility dates, grouping, timing, and schedules are
all explicit in the request and are never inferred from omitted fields.

<Note>
  To create a creator's first contract, send the same complete contract shape
  with `"basedOnVersionId": null`. The server locks the creator record and
  rejects the request with `409 CONTRACT_VERSION_STALE` if another writer
  creates a contract first.
</Note>

### Contract fields

* `type` (required): `NONE`, `FIXED`, `PERFORMANCE`, `BONUS`, or `HYBRID`. It
  must match the enabled payment sections.
* `currency` (required): `USD`, `EUR`, `GBP`, `CAD`, `AUD`, `CHF`, `JPY`,
  `HKD`, `NZD`, `CNY`, `INR`, or `SGD`.
* `fixedPayment` (required): a complete fixed-payment object, or `null`.
  * `currency`: the currency in which the fixed amount is authored.
  * `conversionPolicy`: `null` when this matches `contract.currency`; otherwise
    it must be `LEGACY_PAYMENT_REPOSITORY_FX`.
  * `amount`: payment amount.
  * `per`: `CREATOR` or `POST`.
  * `calculation`: `STANDARD`, `PER_POST`, `SPLIT`, `PRORATED`, or
    `UPLOAD_THRESHOLD`.
  * Also required: `upfrontPercentage`, `expectedPosts`, `minimumPosts`,
    `prorateByTime`, `platforms`, and `crossposts`. Use `null`, `false`, or `[]`
    when a field does not apply.
  * `upfrontPercentage` is non-null only for `SPLIT`; `expectedPosts` is
    non-null only for `PRORATED`; `minimumPosts` is non-null only for
    `UPLOAD_THRESHOLD`; `crossposts` is `EACH_POST` or
    `ONE_PAYMENT_PER_GROUP` only for `PER_POST`, and otherwise is `null`.
    `prorateByTime` can be `true` only for `STANDARD`, and `platforms` must be
    empty when `per` is `CREATOR`.
* `cpmPayment` (required): a complete view-based payment object, or `null`.
  * Required: `currency`, `conversionPolicy`, `rate`, `perViews`, `viewsFrom`,
    `minimumViews`, `maximumViews`, `excludeMinimumViews`, `maximumSpend`,
    `crossposts`, and `pays`. Currency rules are the same as fixed payments.
  * `maximumSpend` is `null` or an object containing `amount`, `per`
    (`CREATOR` or `POST`), and `period` (`LIFETIME`, `DAY`, `WEEK`, or
    `MONTH`).
  * `crossposts` is `EACH_POST`, `BEST_POST`, or `COMBINE_POSTS`. `pays` is
    `CAMPAIGN_COMPLETION`, `NOW`, or `NOW_AND_STOP`.
* `bonusPayment` (required): complete bonus rules, or `null`.
  * `appliesTo`: `CREATOR`, `POST`, or `BEST_POST`.
  * `pays`: `CAMPAIGN_COMPLETION` or `NOW`.
  * `rules`: `VIEWS` rules and their `minimum`/`amount` tiers. Every rule also
    requires `currency` and `conversionPolicy` under the same currency rules.
    `VIEWS` is the only supported metric.
* `crosspostRequirements` (required): `minimumPlatforms`,
  `requiredPlatforms`, and `captionContains`, or `null`.
* `recurringPayment` (required): `paymentsPerPeriod`, `period`, `weekday`,
  `dayOfMonth`, `startsImmediately`, `firstPaymentAt`, and `timeZone`, or
  `null`.
  * Use `weekday` only for weekly schedules and `dayOfMonth` only for a
    once-monthly schedule. `firstPaymentAt` must be `null` when
    `startsImmediately` is `true`; otherwise provide an ISO timestamp.
* `postsUploadedOnOrAfter` (required): a `YYYY-MM-DD` eligibility date or
  `null`.

### Example request

```bash theme={null}
curl -sS "$BASE_URL/sdk/v1/campaign-creators/CCR_123/contract-history" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "X-Application-Id: $APP_ID" \
  -H "X-Api-Key: $API_KEY" \
  -d '{
    "basedOnVersionId": "CCCH_CURRENT",
    "effectiveDate": "2026-08-15",
    "contract": {
      "type": "HYBRID",
      "currency": "USD",
      "fixedPayment": {
        "currency": "USD",
        "conversionPolicy": null,
        "amount": 500,
        "per": "CREATOR",
        "calculation": "STANDARD",
        "upfrontPercentage": null,
        "expectedPosts": null,
        "minimumPosts": null,
        "prorateByTime": false,
        "platforms": [],
        "crossposts": null
      },
      "cpmPayment": {
        "currency": "USD",
        "conversionPolicy": null,
        "rate": 5,
        "perViews": 1000,
        "viewsFrom": "POST",
        "minimumViews": 1000,
        "maximumViews": null,
        "excludeMinimumViews": false,
        "maximumSpend": null,
        "crossposts": "EACH_POST",
        "pays": "NOW"
      },
      "bonusPayment": {
        "appliesTo": "POST",
        "pays": "CAMPAIGN_COMPLETION",
        "rules": [
          {
            "currency": "USD",
            "conversionPolicy": null,
            "metric": "VIEWS",
            "tiers": [
              { "minimum": 1000, "amount": 10 },
              { "minimum": 10000, "amount": 50 },
              { "minimum": 100000, "amount": 100 }
            ]
          }
        ]
      },
      "crosspostRequirements": null,
      "recurringPayment": null,
      "postsUploadedOnOrAfter": "2026-08-15"
    }
  }'
```

<Note>
  Bonus tier amounts are totals, not increments. A post with 50,000 views in
  the example above earns the 10,000-view tier amount of 50.
</Note>

### Response

Successful creation returns `201` with the public contract that was saved.

```json theme={null}
{
  "success": true,
  "data": {
    "campaignId": "camp_123",
    "campaignCreatorRecordId": "CCR_123",
    "creatorProfileId": "CP_123",
    "versionId": "CCCH_NEW",
    "effectiveDate": "2026-08-15",
    "contract": {
      "type": "HYBRID",
      "currency": "USD",
      "fixedPayment": {
        "currency": "USD",
        "conversionPolicy": null,
        "amount": 500,
        "per": "CREATOR",
        "calculation": "STANDARD",
        "upfrontPercentage": null,
        "expectedPosts": null,
        "minimumPosts": null,
        "prorateByTime": false,
        "platforms": [],
        "crossposts": null
      },
      "cpmPayment": {
        "currency": "USD",
        "conversionPolicy": null,
        "rate": 5,
        "perViews": 1000,
        "viewsFrom": "POST",
        "minimumViews": 1000,
        "maximumViews": null,
        "excludeMinimumViews": false,
        "maximumSpend": null,
        "crossposts": "EACH_POST",
        "pays": "NOW"
      },
      "bonusPayment": {
        "appliesTo": "POST",
        "pays": "CAMPAIGN_COMPLETION",
        "rules": [
          {
            "currency": "USD",
            "conversionPolicy": null,
            "metric": "VIEWS",
            "tiers": [
              { "minimum": 1000, "amount": 10 },
              { "minimum": 10000, "amount": 50 },
              { "minimum": 100000, "amount": 100 }
            ]
          }
        ]
      },
      "crosspostRequirements": null,
      "recurringPayment": null,
      "postsUploadedOnOrAfter": "2026-08-15"
    }
  }
}
```

### Safeguards

The endpoint applies the same contract-history safeguards as the Grade app. It
will not add a version that changes protected paid or approved work, conflicts
with another effective date, bypasses a pending payout approval, or changes a
completed campaign where edits are not allowed.

The server also checks `basedOnVersionId` twice: once before validating the
contract and again in the transaction that writes it. For a first contract,
the explicit `null` assertion is also checked while the creator record is
locked. If the timeline changes between your read and write, the API returns
`409 CONTRACT_VERSION_STALE`.

Writing the same `effectiveDate` is allowed and replaces that date's version
when `basedOnVersionId` identifies the existing same-date version and all
other safeguards pass.

### Common errors

* `400 CONTRACT_VERSION_REQUIRED`: `basedOnVersionId` is omitted or invalid;
  it must be a governing version ID or explicit `null` for an empty timeline
* `400`: invalid date, incomplete contract, or unsupported contract terms
* `401`: missing or invalid SDK credentials
* `404`: campaign creator record not found
* `409 CONTRACT_VERSION_STALE`: the governing contract version changed; read
  history again before retrying
* `409`: no existing contract covers the effective date, or the change crosses
  a protected contract boundary

Read the updated timeline with
[Read campaign creator contract history](/campaign-creators/contract-history).
