Campaign lifecycle
Immutable plans, bounded budgets, and explicit provider reconciliation.
Create a plan
POST /api/v1/campaigns requires campaigns:write and Idempotency-Key. The key contains 8–128 letters, digits, dots, colons, underscores, or hyphens. Reusing it with identical normalized input returns the existing campaign; different input returns idempotency_conflict. Keep the key across timeouts.
A live plan requires a ready creative assigned to this owner, the assigned Facebook Page, and a destination within the approved HTTPS origin.
{
"name": "Bookkeeping launch",
"url": "https://example.com/pricing",
"objective": "purchase",
"budget_cents": 50000,
"currency": "USD",
"geo": ["US"],
"target_cac_cents": 4000,
"mode": "live",
"meta": {
"starts_at": "2030-01-02T12:00:00Z",
"ends_at": "2030-01-09T12:00:00Z",
"headline": "Bookkeeping that keeps up",
"primary_text": "See how it works for your business.",
"creative_file_id": "file_REPLACE",
"facebook_social_account_id": "sacc_REPLACE"
}
}
Replace the example dates and resource IDs. Start at least five minutes in the future; duration must be 24 hours to 30 days. Budget bounds are 100–10000000 cents. Geography uses 1–20 uppercase ISO country codes. A supplied conversion_event must match the objective: purchase→purchase, signup→signup, traffic→page_view. target_cac_cents is a planning target, not an enforced acquisition guarantee.
Plans are immutable. A changed destination, creative, copy, schedule, geography, or budget requires a new campaign and approval. The response contains plan_hash, approval_url, mode, status, and provider resource IDs once prepared.
Prepare, approve, observe
| Local state | Meaning / next action |
|---|---|
| awaiting_approval | Stored locally. Live: call prepare. Sandbox: request owner approval. |
| preparing | Provider draft submission in progress or outcome uncertain. Retry the same prepare operation within its retry window. |
| draft | Non-delivering Whop hierarchy exists; owner can inspect the exact plan. |
| launching | Owner approved and budget reserved; activation is in progress or uncertain. Reconcile. |
| active | Configured active or in review. Check delivery_status for actual network delivery. |
| pausing | Pause request in progress or uncertain. Reconcile. |
| paused | Whop confirmed pause. Accrued costs remain payable. |
| attention_required | Provider rejection, mismatch, or another issue requires review. |
| completed | Provider reports completed delivery. |
POST /api/v1/campaigns/{id}/prepare creates one Whop draft, ad group, and ad. It does not enable spending. Owner approval uses a separate endpoint. POST /api/v1/campaigns/{id}/reconcile retrieves current provider state and metrics. GET endpoints return persisted snapshots and do not poll Whop implicitly.
Pause and retry
POST /api/v1/campaigns/{id}/pause requires campaigns:write. It waits for provider confirmation. A timeout is not a successful pause. Reconcile and retain the original campaign; do not make a duplicate launch. Provider mutations use durable operation keys and no automatic network retries. After 23 hours an ambiguous POST needs operator reconciliation rather than a new idempotency key.
There is no resume or edit operation in this release. A new campaign needs a new plan and owner approval. Reserved budget is not automatically released on pause: provider spend can arrive late.
Read and paginate
GET /api/v1/campaigns?limit=20 returns data.items and data.next_cursor. Pass the returned cursor with the same limit for the next page. Limits are 1–100; order is descending campaign UUID, not chronological. GET /api/v1/campaigns/{id} returns one owner-scoped record; foreign IDs return 404.