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

# Campaign management

> Create, update, and activate campaigns and line items via the Management API.

Campaign write endpoints require an advertiser API key with the **`campaigns`**
permission. See [Management API overview](/advertisers/api/overview) for base URL,
authentication, and error format.

All routes are org-scoped through the key. You can only read or mutate campaigns
owned by a user in your organization.

## Concepts

A **campaign** is the top-level container (name, brand, objective, schedule).
**Line items** are the deliverable ad units under a campaign. In the database, line
items are stored in the `ads` table; the API uses the term "line item" throughout.

| Term      | API path prefix             |
| --------- | --------------------------- |
| Campaign  | `/v1/advertiser/campaigns`  |
| Line item | `/v1/advertiser/line-items` |

<Warning>
  Every campaign create requires **`user_id`** in the JSON body. The API key resolves
  to an **organization**, not a single user. Pass a UUID for a member of that org. A
  `user_id` outside your org returns `403 forbidden`.
</Warning>

There is no `org_id` field on campaigns. Tenancy is enforced through
`campaigns.user_id` and the `org_users` membership table.

## Typical workflow

<Steps>
  <Step title="Validate (optional)">
    `POST /v1/advertiser/campaigns/validate` checks campaign and line item fields
    without writing.
  </Step>

  <Step title="Create">
    Create a campaign alone (`POST /campaigns`), a line item under an existing
    campaign (`POST /campaigns/:id/line-items`), or both atomically
    (`POST /campaigns/compose`).
  </Step>

  <Step title="Configure line items">
    Set budget, bids, targeting, and attach creatives on the line item subresource
    routes.
  </Step>

  <Step title="Activate">
    `POST /v1/advertiser/campaigns/:id/activate` validates the campaign has at least
    one line item and sets the campaign and its line items to `active`.
  </Step>
</Steps>

`compose` and `duplicate` run in a single database transaction. If any step fails,
nothing is left behind.

## Endpoints

| Method   | Path                                      | Purpose                                 |
| -------- | ----------------------------------------- | --------------------------------------- |
| `GET`    | `/v1/advertiser/campaigns`                | List campaigns (`?status=` optional)    |
| `POST`   | `/v1/advertiser/campaigns`                | Create a campaign                       |
| `POST`   | `/v1/advertiser/campaigns/validate`       | Dry-run validation                      |
| `POST`   | `/v1/advertiser/campaigns/compose`        | Create campaign + line item in one call |
| `GET`    | `/v1/advertiser/campaigns/:id`            | Get a campaign                          |
| `PATCH`  | `/v1/advertiser/campaigns/:id`            | Update a campaign                       |
| `DELETE` | `/v1/advertiser/campaigns/:id`            | Delete a campaign (cascades line items) |
| `POST`   | `/v1/advertiser/campaigns/:id/duplicate`  | Clone campaign and line items           |
| `POST`   | `/v1/advertiser/campaigns/:id/activate`   | Activate campaign and line items        |
| `GET`    | `/v1/advertiser/campaigns/:id/line-items` | List line items for a campaign          |
| `POST`   | `/v1/advertiser/campaigns/:id/line-items` | Create a line item                      |
| `GET`    | `/v1/advertiser/line-items/:id`           | Get a line item                         |
| `PATCH`  | `/v1/advertiser/line-items/:id`           | Update a line item                      |
| `PUT`    | `/v1/advertiser/line-items/:id/budget`    | Upsert budget                           |
| `PUT`    | `/v1/advertiser/line-items/:id/bids`      | Upsert bids                             |
| `PUT`    | `/v1/advertiser/line-items/:id/targeting` | Replace targeting                       |
| `POST`   | `/v1/advertiser/line-items/:id/creatives` | Attach creatives                        |

## Create a campaign

```bash theme={null}
curl -X POST "https://api.adgentek.ai/webapi/v1/advertiser/campaigns" \
  -H "x-api-key: adv_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q3 Awareness",
    "brand_name": "Acme",
    "user_id": "YOUR_ORG_MEMBER_UUID",
    "objective": "brand_awareness"
  }'
```

Required fields: `name`, `brand_name`, `user_id`.

Response (`201`):

```json theme={null}
{
  "data": {
    "id": "…",
    "name": "Q3 Awareness",
    "brand_name": "Acme",
    "user_id": "…",
    "status": "paused",
    "objective": "awareness",
    "created_at": "…"
  }
}
```

## Compose a campaign and line item

Use `compose` when you want both records created together:

```bash theme={null}
curl -X POST "https://api.adgentek.ai/webapi/v1/advertiser/campaigns/compose" \
  -H "x-api-key: adv_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign": {
      "name": "Q3 Awareness",
      "brand_name": "Acme",
      "user_id": "YOUR_ORG_MEMBER_UUID",
      "objective": "brand_awareness"
    },
    "line_item": {
      "title": "Primary Line Item",
      "objective": "brand_awareness",
      "primary_kpi": "impressions",
      "budget": { "daily": 100, "currency": "USD" },
      "bids": { "bid_type": "cpc", "bid_value": 1.25 }
    }
  }'
```

Response (`201`):

```json theme={null}
{
  "data": {
    "campaign": { "id": "…", "name": "Q3 Awareness", "status": "paused" },
    "line_item": { "id": "…", "campaign_id": "…", "title": "Primary Line Item", "status": "draft" }
  }
}
```

## List and update line items

List line items under a campaign:

```bash theme={null}
curl "https://api.adgentek.ai/webapi/v1/advertiser/campaigns/CAMPAIGN_ID/line-items" \
  -H "x-api-key: adv_YOUR_KEY"
```

Update budget on a line item:

```bash theme={null}
curl -X PUT "https://api.adgentek.ai/webapi/v1/advertiser/line-items/LINE_ITEM_ID/budget" \
  -H "x-api-key: adv_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "daily": 75, "currency": "USD" }'
```

## Activate

Activation requires at least one line item. The campaign and all its line items are
set to `active`:

```bash theme={null}
curl -X POST "https://api.adgentek.ai/webapi/v1/advertiser/campaigns/CAMPAIGN_ID/activate" \
  -H "x-api-key: adv_YOUR_KEY"
```

## Value normalization

The API accepts common product terms and maps them to database constraints:

| You send                     | Stored as   |
| ---------------------------- | ----------- |
| `objective: brand_awareness` | `awareness` |
| `primary_kpi: impressions`   | `CPM`       |
| `bid_type: cpc`              | `CPC`       |
| `bid_type: cpm`              | `tCPM`      |
| `status: draft` (campaign)   | `paused`    |

Campaigns allow only `active`, `paused`, and `completed`. There is no `draft`
status for campaigns; newly created campaigns start as `paused`. Line items may
remain `draft` until activation.

## Errors

| HTTP  | Code                            | Typical cause                                           |
| ----- | ------------------------------- | ------------------------------------------------------- |
| `400` | `bad_request`                   | Malformed JSON                                          |
| `401` | `auth_missing` / `auth_invalid` | Missing or invalid API key                              |
| `403` | `forbidden`                     | Missing `campaigns` permission, or `user_id` not in org |
| `404` | `not_found`                     | Campaign or line item not in your org                   |
| `422` | `validation_error`              | Missing required field or constraint violation          |

## OpenAPI reference

Field-level schemas (`CampaignInput`, `LineItemInput`, and others) live in the
machine-readable contract:

```
GET https://api.adgentek.ai/webapi/openapi.yaml
```

Prefer the spec when this page and the running service diverge.
