# Run a campaign

Create and activate a creator campaign, invite creators, let them submit through the hosted portal, and approve or reject what they post.

This page takes one CPM campaign from draft to approved submissions. Paying is covered in [Pay creators](https://pozy.ai/docs/creators/payouts).

## Create the campaign

`POST /v1/creator-campaigns` with a `profileId`, a `name`, a `brandName` and `economics`. Send an `Idempotency-Key` so a retry does not create two campaigns.

```bash cURL
curl -X POST https://api.pozy.ai/v1/creator-campaigns \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Idempotency-Key: launch-clips-2026-11" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "6650c1f0a2b3c4d5e6f70812",
    "name": "Launch clips",
    "brandName": "Acme",
    "platforms": ["tiktok", "instagram", "youtube"],
    "economics": { "type": "cpm", "ratePer1kCents": 100, "minViewsToEarn": 1000, "maxPayoutPerClipCents": 50000, "budgetCents": 500000 },
    "contentRules": "Show the product in the first 3 seconds. Tag @acme. No paid boosting.",
    "trackingDays": 30,
    "holdDays": 10,
    "settlement": "treasury"
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/creator-campaigns", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Idempotency-Key": "launch-clips-2026-11",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    profileId: "6650c1f0a2b3c4d5e6f70812",
    name: "Launch clips",
    brandName: "Acme",
    platforms: ["tiktok", "instagram", "youtube"],
    economics: { type: "cpm", ratePer1kCents: 100, minViewsToEarn: 1000, maxPayoutPerClipCents: 50000, budgetCents: 500000 },
    contentRules: "Show the product in the first 3 seconds. Tag @acme. No paid boosting.",
    settlement: "treasury",
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.campaign.id, data.campaign.status); // "draft"
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/creator-campaigns",
    headers={
        "Authorization": f"Bearer {os.environ['POZY_API_KEY']}",
        "Idempotency-Key": "launch-clips-2026-11",
    },
    json={
        "profileId": "6650c1f0a2b3c4d5e6f70812",
        "name": "Launch clips",
        "brandName": "Acme",
        "platforms": ["tiktok", "instagram", "youtube"],
        "economics": {"type": "cpm", "ratePer1kCents": 100, "minViewsToEarn": 1000, "budgetCents": 500000},
        "settlement": "treasury",
    },
    timeout=30,
)
print(resp.json()["campaign"]["id"])
```

That campaign pays $1 per 1,000 verified views, only once a post passes 1,000 views, at most $500 per post, from a $5,000 budget.

| Field | Default | What it does |
| --- | --- | --- |
| `platforms` | | Any of `tiktok`, `instagram`, `youtube`, `threads`, `pinterest`, `facebook`, `twitter` |
| `contentRules` | none | Brief shown to creators on their page, up to 10,000 characters |
| `startsAt`, `endsAt` | none | Campaign window. Posts published before the start cannot earn. |
| `trackingDays` | 30 | How long each post is tracked, up to 365 |
| `holdDays` | 10 | How long approved earnings are held before they become payable, up to 90 |
| `maxPostAgeMinutes` | none | Refuse posts submitted longer than this after they were published |
| `settlement` | `none` | `treasury` pays through Pozy, `external` means you pay on your own rails and record it, `none` only tracks |
| `caps` | | `maxParticipants`, `maxSubmissionsPerParticipantPerDay`, `maxAccountsPerParticipant`, `maxXReadSpendCents` (default $50), `authenticityThreshold` (0 to 100) |
| `branding` | none | `brandName`, `primaryColor`, `logoUrl` and `theme` for the creator page |

## Activate, pause, close

| Call | Effect |
| --- | --- |
| `POST /v1/creator-campaigns/{campaignId}/activate` | `draft` or `paused` to `active`. If there is no `startsAt`, activation is the start. |
| `POST /v1/creator-campaigns/{campaignId}/pause` | Pauses the campaign. Activate it again to resume. |
| `POST /v1/creator-campaigns/{campaignId}/close` | Ends the campaign. `creator_campaign.closed` fires. |
| `POST /v1/creator-campaigns/{campaignId}/budget` | Raise `budgetCents`, for example after `creator_campaign.budget_exhausted` |
| `PATCH /v1/creator-campaigns/{campaignId}` | Edit the campaign |

## Invite creators

Invite one creator by name and email. The response includes their personal `inviteUrl`.

```bash
curl -X POST https://api.pozy.ai/v1/creator-campaigns/6701a2b3c4d5e6f7a8b9c0d1/participants \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "displayName": "Cleo Martin", "email": "cleo@example.com" }'
```

Or create one open link with `POST /v1/creator-campaigns/{campaignId}/invite-link` and post it anywhere. Everyone who joins through it gets their own personal link. `campaign_participant.joined` fires when someone joins.

Manage participants with `.../participants/{participantId}/suspend`, `/reinstate`, `/remove` and `/resend-invite`. Each participant record shows their connected `accounts`, `balances` (`accruing`, `owed`, `payable`, `settling`, `settled`), `flags` and `payout.status`.

## The creator portal

Creators work on a page hosted by Pozy, branded with your campaign `branding`. If you would rather build your own page, the same actions are public endpoints under `/public/creator-campaigns/{token}`. They need no API key: the token in the creator's link is the credential.

| Endpoint | What it does |
| --- | --- |
| `GET /public/creator-campaigns/{token}` | Read an invite (`kind: "invite"`) or a participant page (`kind: "participant"`) |
| `POST /public/creator-campaigns/{token}` | Join with an invite token. Returns a `participantToken`. |
| `POST /public/creator-campaigns/{token}/connect` | Start connecting a creator account. Returns an `authUrl` to send them to. |
| `POST /public/creator-campaigns/{token}/submissions` | Submit a post by `url` |
| `GET /public/creator-campaigns/{token}/payouts` | The creator's earnings history |
| `POST /public/creator-campaigns/{token}/payouts` | Start payout onboarding (`action: "onboard"`). Returns a `url`. |
| `POST /public/creator-campaigns/recover-link` | Email a creator their campaign links by `email` |

```bash cURL
curl -X POST https://api.pozy.ai/public/creator-campaigns/ptok_9fH2kQ/submissions \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://www.tiktok.com/@cleo/video/7430000000000000000" }'
```
```ts Node
// No API key: the participant token from the creator's link authorizes the call.
const res = await fetch(`https://api.pozy.ai/public/creator-campaigns/${participantToken}/submissions`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ url: "https://www.tiktok.com/@cleo/video/7430000000000000000" }),
});
const { submission } = await res.json();
console.log(submission.status, submission.trackingEndsAt);
```
```python Python
import requests

resp = requests.post(
    f"https://api.pozy.ai/public/creator-campaigns/{participant_token}/submissions",
    json={"url": "https://www.tiktok.com/@cleo/video/7430000000000000000"},
    timeout=30,
)
print(resp.json()["submission"]["status"])
```

> **Warning:**
> 
> Treat creator tokens like passwords. Anyone with a participant token can submit posts and start payout onboarding for that creator.

## Submission rules

- The post must come from an account the creator connected, on a platform the campaign allows.
- It must be published after the campaign started. Earlier posts are refused at submission.
- Any post age is accepted unless you set `maxPostAgeMinutes`.
- All verified views count, including views from before the creator submitted. `minViewsToEarn`, `maxPayoutPerClipCents` and the budget still apply.
- Only views Pozy reads from the platform earn.

## Review submissions

A submission moves through `received`, `tracking`, `pending_review`, then `approved` or `rejected` (or `expired`, `withdrawn`). It reaches `pending_review` when a CPM post passes `minViewsToEarn`, a pool post passes `minViewsToQualify`, or a per-post submission first accrues. `campaign_submission.pending_review` fires.

```bash
curl "https://api.pozy.ai/v1/creator-campaigns/6701a2b3c4d5e6f7a8b9c0d1/submissions?status=pending_review&sort=viewsEligible" \
  -H "Authorization: Bearer $POZY_API_KEY"
```

Filter by `status`, `participantId`, `platform`, `flag`, an authenticity score range (`minScore`, `maxScore`) or text (`q`), and sort by `submittedAt`, `viewsEligible`, `earnedCents` or `authenticityScore`.

- **Approve:** `POST .../submissions/{submissionId}/approve`.
- **Reject:** `POST .../submissions/{submissionId}/reject` with a `reason` (up to 1,000 characters). The creator sees it.
- **Reopen:** `POST .../submissions/{submissionId}/reopen` puts a decided submission back up for review.

### Flags

Pozy flags submissions and never rejects them itself. `campaign_submission.flagged` fires when a new flag appears.

| Flag | Meaning |
| --- | --- |
| `duplicate_post`, `duplicate_media` | The post, or the same media, was already submitted here or to another campaign |
| `view_spike` | Views jumped sharply between samples |
| `low_authenticity` | The authenticity score is under the campaign threshold |
| `linked_accounts`, `cross_operator` | Signs that accounts are shared with, or run by the same operator as, other participants |
| `unreachable`, `post_deleted`, `account_restricted` | The post or account can no longer be read |
| `x_read_cap` | The campaign's X read budget is spent, so X views stopped counting |

## Corrections and reporting

- `POST /v1/creator-campaigns/{campaignId}/adjustments` records an `amountCents` adjustment for one participant with a `cause`, such as a bonus or a correction.
- `GET /v1/creator-campaigns/{campaignId}/ledger` lists every money movement on the campaign.
- `GET /v1/creator-campaigns/{campaignId}/report` returns spend (`budgetCents`, `consumedCents`, `settledCents`), eligible and total views, submission counts, per-participant balances and an authenticity histogram.

## Errors

| Status | Code | Cause | Fix |
| --- | --- | --- | --- |
| `403` | `feature_not_available` | Creator campaigns or treasury payouts are not enabled for the workspace | Contact support@pozy.ai |

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/creator-campaigns`](https://pozy.ai/docs/api/listCreatorCampaigns) | List creator campaigns |
| [`POST /v1/creator-campaigns`](https://pozy.ai/docs/api/createCreatorCampaign) | Create creator campaign |
| [`GET /v1/creator-campaigns/{campaignId}`](https://pozy.ai/docs/api/getCreatorCampaign) | Get creator campaign |
| [`PATCH /v1/creator-campaigns/{campaignId}`](https://pozy.ai/docs/api/updateCreatorCampaign) | Update creator campaign |
| [`POST /v1/creator-campaigns/{campaignId}/budget`](https://pozy.ai/docs/api/raiseCreatorCampaignBudget) | Raise creator campaign budget |
| [`POST /v1/creator-campaigns/{campaignId}/activate`](https://pozy.ai/docs/api/activateCreatorCampaign) | Activate creator campaign |
| [`POST /v1/creator-campaigns/{campaignId}/pause`](https://pozy.ai/docs/api/pauseCreatorCampaign) | Pause creator campaign |
| [`POST /v1/creator-campaigns/{campaignId}/close`](https://pozy.ai/docs/api/closeCreatorCampaign) | Close creator campaign |
| [`GET /v1/creator-campaigns/{campaignId}/participants`](https://pozy.ai/docs/api/listCampaignParticipants) | List campaign participants |
| [`POST /v1/creator-campaigns/{campaignId}/participants`](https://pozy.ai/docs/api/createCampaignParticipant) | Invite campaign participant |
| [`GET /v1/creator-campaigns/{campaignId}/participants/{participantId}`](https://pozy.ai/docs/api/getCampaignParticipant) | Get campaign participant |
| [`POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/suspend`](https://pozy.ai/docs/api/suspendCampaignParticipant) | Suspend campaign participant |
| [`POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/reinstate`](https://pozy.ai/docs/api/reinstateCampaignParticipant) | Reinstate campaign participant |
| [`POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/remove`](https://pozy.ai/docs/api/removeCampaignParticipant) | Remove campaign participant |
| [`POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/resend-invite`](https://pozy.ai/docs/api/resendCampaignParticipantInvite) | Resend campaign participant invite |
| [`POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle`](https://pozy.ai/docs/api/settleCampaignParticipant) | Settle campaign participant |
| [`GET /v1/creator-campaigns/{campaignId}/payout-batches`](https://pozy.ai/docs/api/listCampaignPayoutBatches) | List payout batches |
| [`POST /v1/creator-campaigns/{campaignId}/payout-batches`](https://pozy.ai/docs/api/createCampaignPayoutBatch) | Pay creators with one card payment |
| [`POST /v1/creator-campaigns/{campaignId}/invite-link`](https://pozy.ai/docs/api/createCampaignInviteLink) | Create campaign invite link |
| [`GET /v1/creator-campaigns/{campaignId}/submissions`](https://pozy.ai/docs/api/listCampaignSubmissions) | List campaign submissions |
| [`GET /v1/creator-campaigns/{campaignId}/submissions/{submissionId}`](https://pozy.ai/docs/api/getCampaignSubmission) | Get campaign submission |
| [`POST /v1/creator-campaigns/{campaignId}/submissions/{submissionId}/approve`](https://pozy.ai/docs/api/approveCampaignSubmission) | Approve campaign submission |
| [`POST /v1/creator-campaigns/{campaignId}/submissions/{submissionId}/reject`](https://pozy.ai/docs/api/rejectCampaignSubmission) | Reject campaign submission |
| [`POST /v1/creator-campaigns/{campaignId}/submissions/{submissionId}/reopen`](https://pozy.ai/docs/api/reopenCampaignSubmission) | Reopen campaign submission |
| [`GET /v1/creator-campaigns/{campaignId}/payables`](https://pozy.ai/docs/api/listCampaignPayables) | List campaign payables |
| [`POST /v1/creator-campaigns/{campaignId}/adjustments`](https://pozy.ai/docs/api/createCampaignAdjustment) | Create campaign adjustment |
| [`GET /v1/creator-campaigns/{campaignId}/ledger`](https://pozy.ai/docs/api/listCampaignLedger) | List campaign ledger |
| [`GET /v1/creator-campaigns/{campaignId}/report`](https://pozy.ai/docs/api/getCampaignReport) | Get campaign report |
| [`POST /public/creator-campaigns/recover-link`](https://pozy.ai/docs/api/recoverCreatorPortalLinks) | Email a creator their campaign links |
| [`GET /public/creator-campaigns/{token}`](https://pozy.ai/docs/api/getCreatorPortal) | Read a creator invite or participant page |
| [`POST /public/creator-campaigns/{token}`](https://pozy.ai/docs/api/joinCreatorCampaign) | Join a campaign |
| [`POST /public/creator-campaigns/{token}/connect`](https://pozy.ai/docs/api/startCreatorConnect) | Start connecting a creator account |
| [`POST /public/creator-campaigns/{token}/submissions`](https://pozy.ai/docs/api/submitCreatorPost) | Submit a post |
| [`GET /public/creator-campaigns/{token}/payouts`](https://pozy.ai/docs/api/getCreatorPayouts) | List a creator's earnings history |
| [`POST /public/creator-campaigns/{token}/payouts`](https://pozy.ai/docs/api/startCreatorPayouts) | Start payout onboarding |
| [`POST /public/creator-campaigns/{token}/payout-login`](https://pozy.ai/docs/api/createCreatorPayoutLogin) | Open the creator's Stripe dashboard |
| [`GET /public/creator-campaigns/{token}/payouts/{transferId}/statement`](https://pozy.ai/docs/api/getCreatorPayoutStatement) | Download a payout statement |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`campaign_submission.received`](https://pozy.ai/docs/webhooks/events#campaign-submission-received) | Campaign submission received. |
| [`campaign_submission.flagged`](https://pozy.ai/docs/webhooks/events#campaign-submission-flagged) | Campaign submission flagged. |
| [`campaign_submission.pending_review`](https://pozy.ai/docs/webhooks/events#campaign-submission-pending-review) | Campaign submission pending review. |
| [`campaign_submission.approved`](https://pozy.ai/docs/webhooks/events#campaign-submission-approved) | Campaign submission approved. |
| [`campaign_submission.rejected`](https://pozy.ai/docs/webhooks/events#campaign-submission-rejected) | Campaign submission rejected. |
| [`creator_campaign.closed`](https://pozy.ai/docs/webhooks/events#creator-campaign-closed) | Creator campaign closed. |
| [`campaign_participant.joined`](https://pozy.ai/docs/webhooks/events#campaign-participant-joined) | Campaign participant joined. |
| [`campaign_participant.suspended`](https://pozy.ai/docs/webhooks/events#campaign-participant-suspended) | Campaign participant suspended. |
| [`campaign_participant.removed`](https://pozy.ai/docs/webhooks/events#campaign-participant-removed) | Campaign participant removed. |
| [`creator_campaign.budget_exhausted`](https://pozy.ai/docs/webhooks/events#creator-campaign-budget-exhausted) | Creator campaign budget exhausted. |
