---
title: "Update a campaign (admin only). `slug` is immutable."
method: PATCH
path: "/api/admin/campaigns/{id}"
tags: ["Admin - Campaigns"]
---

# Update a campaign (admin only). \`slug\` is immutable.

`PATCH /api/admin/campaigns/{id}`

## Request

- Base URL: `https://api.rewritelifestyle.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.rewritelifestyle.ai/api/admin/campaigns/{id}`
- Auth: HTTP bearer

## Path parameters

- `id` number, required

## Request body

- UpdateCampaignDto
  - `name` string
  - `template` 'offer_sheet_v1'
  - `placement` 'home_bottom_sheet' | 'paywall' | 'subscription_tab'
  - `start_at` string
  - `end_at` string
  - `is_active` boolean
  - `priority` number: Higher wins when several campaigns match
  - `dismissible` boolean
  - `cooldown_hours` object: Hours to wait after a `shown` event before showing again. Null = no cooldown.
  - `max_impressions` object: Total `shown` events per user before the campaign stops appearing.
  - `claimable` boolean: Whether POST /campaigns/:id/claim is accepted
  - `max_claims` object: Global stock cap. Null = unlimited.
  - `suppress_after_claim` boolean
  - `requires_address` boolean: Whether claiming requires a shipping address. Only for campaigns that ship a physical item — the address is stored separately and deleted after fulfilment.
  - `eligibility_rule` 'none' | 'subscription_transition': Extra gate on claiming, beyond variant audience targeting. Omit or use `none` for no gate.
  - `eligibility_rule_params` object: Parameters for `eligibility_rule`. For `subscription_transition`: { transition_types: string[] (from GET /campaigns/eligibility-transition-types), since: "campaign_start" | "last_n_days", window_days?: number, extension_reasons?: string[] }
  - `countries` string[]: ISO-3166-1 alpha-2. Empty = anywhere.
  - `allowed_user_ids` string[]: Restrict the campaign to these user ids, on top of every other filter — a listed user still has to pass `countries` and still has to match a variant audience. Omit or send an empty array for no restriction. For internal testing and one-off cohorts; ids differ between environments.
  - `excluded_states` string[]: Excluded subdivisions, matched against profile.state — which nothing currently populates, so this filter is inert today. Restrict via `countries` instead.

## Response `200`

---

[API](https://skmtc.dev/rewritelifestyle/apis/rewrite-lifestyle-api.md) - [All operations](https://skmtc.dev/rewritelifestyle/apis/rewrite-lifestyle-api/llms.txt) - [OpenAPI document](https://skmtc.dev/rewritelifestyle/apis/rewrite-lifestyle-api/revisions/28695827f27a?raw)
