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

# Campaigns

> Create, change, pause, and archive campaigns.

Use these endpoints to create, change, pause, and archive campaigns with your API key.

> [!WARNING]
>
> These endpoints change your live account. An active campaign can spend money. To test, create the campaign with `"status": "draft"` or `"status": "paused"`.

## POST `/advertiser-dashboard/campaigns`

Create a campaign with ad groups (recommended), or with legacy ad units. The response is `201` with the new campaign.

**name** `string` *required*

The campaign name, at most 255 characters.

**landing\_page** `string` *required*

The destination URL of the campaign.

**daily\_budget** `number`

The average daily budget in dollars. It is necessary, except on `target_cpa` campaigns and total budget campaigns. Spend on one day (UTC) can reach 110% of the daily budget. Then the campaign stops serving until the next day.

**budget\_type** `string` (default: daily)

`daily` or `total`. A total budget needs `total_budget`, `end_time`, and OpenAI-only delivery (`network_mode: "openai"`).

**total\_budget** `number`

The maximum spend in dollars for the full schedule of a total budget campaign. Do not send `daily_budget` in this mode.

**monthly\_budget** `number`

A spend limit for each calendar month. Only `target_cpa` campaigns accept it.

**status** `string` (default: active)

The campaign status: `draft`, `active`, or `paused`.

**campaign\_objective** `string` (default: clicks)

The campaign objective: `clicks`, `reach`, or `conversions`.

**billing\_model** `string` (default: cpm)

Use `cpm` (pay per 1,000 impressions). Gravity does not offer click billing: the field still accepts `cpc` for older campaigns, but don't use it for new ones. You cannot change the value after the campaign launches on OpenAI.

**bid\_strategy** `string` (default: lowest\_cost)

The bid strategy: `lowest_cost` or `target_cpa`.

**is\_active** `boolean` (default: true)

Tells if the campaign is active.

**start\_time** `string`

The start time (ISO 8601).

**end\_time** `string`

The end time (ISO 8601).

**click\_goal** `integer`

A click goal.

**max\_cpc** `number`

Legacy. A fallback bid for the whole campaign. It applies to click-billed ad groups that have no `max_cpc`. Set a bid on each ad group instead. This field does not set the billing model. Gravity reads `0` as no value.

**impression\_goal** `integer`

An impression goal.

**max\_cpm** `number`

The maximum CPM.

**conversion\_type** `string`

The conversion event that the campaign optimizes and reports on. Use a preset (for example `purchase`) or a custom event name that you send with the pixel or CAPI. At most 100 characters.

**max\_cpa** `number`

The maximum CPA.

**demographics** `object`

The demographics settings.

**utm\_params** `array`

UTM parameters as `{ "key": "...", "value": "..." }` pairs.

**allowed\_countries** `array`

The countries to target.

**geo\_targets** `array`

Location IDs of countries, regions, and DMAs, at most 500. Find them with [`GET /advertiser-dashboard/geo-targets`](https://docs.trygravity.com/api-reference/advertiser/targeting#get-/advertiser-dashboard/geo-targets). Gravity combines them with `allowed_countries`. An ID that isn't in the catalog returns `422`.

**allowed\_devices** `array`

The platforms to target: `web`, `ios_app`, `android_app`. Omit the field, or send all three, for no platform limit. Gravity also accepts the legacy values `iphone`, `android`, `desktop`, `tablet`, and `mobile`.

**english\_only** `boolean` (default: false)

Serve only in conversations that Gravity detects as English. Gravity still serves in conversations when the language is not clear.

**network\_mode** `string`

`dynamic`, `gravity`, or `openai`. OpenAI-only delivery needs account access. Omit the field to use the default of your account.

**include\_audience\_ids** `array`

Serve only to members of these [audiences](https://docs.trygravity.com/api-reference/advertiser/targeting.md). Only a campaign that delivers fully on a partner delivery network accepts audiences, and only when your account has partner-network delivery. The campaign launches when the `delivery_status` of each list is ready.

**exclude\_audience\_ids** `array`

Never serve to members of these audiences. The same limits apply. One audience can't be both included and excluded.

**ad\_groups** `array`

The ad groups to create with the campaign. Each item has the same shape as the body of [`POST /advertiser-dashboard/ad-groups`](https://docs.trygravity.com/api-reference/advertiser/ad-groups#post-/advertiser-dashboard/ad-groups), without `campaign_id`. Gravity sets `campaign_id` from the new campaign. Manual groups can include authored `ads`. Generative groups make their own ads and reject authored `ads`.

**ad\_unit\_ids** `array`

Legacy. The IDs of ad units to link. Gravity still supports them, but ad groups are the current model.

> [!NOTE]
>
> Send at least one ad group in `ad_groups` or one ID in `ad_unit_ids`. If both are empty, the response is `422`. The body rejects fields that it does not know, for example `keywords`, `network_type`, and `network_config`, with `422`. Use `network_mode` to select the network.

If you create the campaign with `"status": "active"` and your billing is not ready, the response is `402`.

The ad group objects also have target and budget fields (`context`, `landing_page`, `daily_budget`, `max_cpc`, `allowed_countries`, `allowed_devices`, and more). See [Manage ad groups and ads](https://docs.trygravity.com/api-reference/advertiser/ad-groups.md).

```bash Request theme={null}
curl -X POST https://platform.trygravity.com/advertiser-dashboard/campaigns \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer Sale",
    "landing_page": "https://acme.com/summer",
    "daily_budget": 100,
    "status": "draft",
    "campaign_objective": "clicks",
    "billing_model": "cpm",
    "bid_strategy": "lowest_cost",
    "allowed_countries": ["US", "CA"],
    "allowed_devices": ["web", "ios_app", "android_app"],
    "ad_groups": [
      {
        "name": "Summer Sale (Generative)",
        "group_type": "generative",
        "context": "Summer footwear sale, focus on running shoes"
      },
      {
        "name": "Summer Sale (Manual)",
        "group_type": "manual",
        "ads": [
          {
            "headline": "50% Off Running Shoes",
            "copy": "Summer sale on all running shoes.",
            "cta": "Shop Now",
            "landing_page": "https://acme.com/summer"
          }
        ]
      }
    ]
  }'
```

```json Response 201 theme={null}
{
  "campaign_id": "camp-456",
  "user_id": "adv-123",
  "name": "Summer Sale",
  "landing_page": "https://acme.com/summer",
  "daily_budget": 100,
  "budget_type": "daily",
  "status": "draft",
  "campaign_objective": "clicks",
  "billing_model": "cpm",
  "is_active": true,
  "created_at": "2025-05-01T00:00:00",
  "start_time": "2025-05-01T00:00:00",
  "end_time": null,
  "daily_budget_remaining": 100,
  "allowed_countries": ["US", "CA"],
  "allowed_devices": ["web", "ios_app", "android_app"],
  "utm_params": null,
  "custom_favicon_url": null,
  "deployment_status": "pending",
  "review_status": "not_submitted",
  "submitted_for_review_at": null,
  "reviewed_at": null,
  "review_notes": null,
  "delivery_status": "draft",
  "delivery_issues": [],
  "ad_groups": [
    {
      "id": "adg-789",
      "campaign_id": "camp-456",
      "name": "Summer Sale (Generative)",
      "group_type": "generative",
      "status": "active",
      "ads": [],
      "ads_total": 0
    },
    {
      "id": "adg-790",
      "campaign_id": "camp-456",
      "name": "Summer Sale (Manual)",
      "group_type": "manual",
      "status": "active",
      "ads": [
        {
          "id": "ad-901",
          "ad_group_id": "adg-790",
          "campaign_id": "camp-456",
          "headline": "50% Off Running Shoes",
          "copy": "Summer sale on all running shoes.",
          "cta": "Shop Now",
          "landing_page": "https://acme.com/summer",
          "source": "manual",
          "status": "active",
          "review_status": "pending"
        }
      ],
      "ads_total": 1
    }
  ]
}
```

## PATCH `/advertiser-dashboard/campaigns/{campaign_id}`

Change a campaign, or pause and resume it. Send only the fields that you want to change.

**campaign\_id** `string` *required*

The campaign ID.

**name** `string`

The campaign name.

**landing\_page** `string`

The landing page URL.

**daily\_budget** `number`

The average daily budget. You can send `null` to remove it only on a `target_cpa` campaign.

**budget\_type** `string`

`daily` or `total`. You can change it only before the campaign launches on OpenAI.

**total\_budget** `number`

The maximum spend for a total budget campaign.

**monthly\_budget** `number`

The spend limit for each calendar month (`target_cpa` only). Send `null` to remove it.

**campaign\_objective** `string`

The campaign objective: `clicks`, `reach`, or `conversions`.

**billing\_model** `string`

`cpm`. (`cpc` is still accepted for older campaigns; Gravity does not offer click billing.) You can change it only before the campaign launches on OpenAI. After that, a different value returns `422`.

**conversion\_type** `string`

The conversion event that the campaign optimizes and reports on. Send `null` or an empty string to remove it.

**max\_cpc** `number`

Legacy. The fallback bid for click-billed ad groups that have no `max_cpc`. Send `null` or `0` to remove it. This field does not change the billing model.

**status** `string`

`active`, `paused`, or `draft`.

**start\_time** `string`

The start time (ISO 8601).

**end\_time** `string`

The end time (ISO 8601).

**allowed\_countries** `array`

The countries to target.

**geo\_targets** `array`

Location IDs of countries, regions, and DMAs. Send an empty list or `null` to remove the limit.

**allowed\_devices** `array`

The platforms to target: `web`, `ios_app`, `android_app`. Send an empty list to remove the limit.

**english\_only** `boolean`

Serve only in conversations that Gravity detects as English.

**network\_mode** `string`

`dynamic`, `gravity`, or `openai`.

**utm\_params** `array`

UTM parameters as `{ "key": "...", "value": "..." }` pairs.

**include\_audience\_ids** `array`

Serve only to members of these audiences. Send an empty list to remove them.

**exclude\_audience\_ids** `array`

Never serve to members of these audiences. Send an empty list to remove them.

**custom\_favicon\_url** `string`

The favicon URL. It must start with `http://` or `https://` and have at most 1,000 characters.

When you activate a campaign, Gravity does these checks:

* If your billing is not ready, the response is `402`.
* If all ad groups of the campaign are paused, the response is `409`. Resume one ad group first.
* If a conversion campaign has no CAPI event for its result event, the response is `409`. See [Server-side conversions](https://docs.trygravity.com/advertisers/server-conversions.md).

Activation can send the campaign to review again and start a sync in the background. A change to the budget or the network can move the campaign to Gravity-only delivery. If the body has no fields to change, the response is `422`.

```bash Request theme={null}
curl -X PATCH https://platform.trygravity.com/advertiser-dashboard/campaigns/camp-456 \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "paused"
  }'
```

```json Response theme={null}
{
  "campaign_id": "camp-456",
  "user_id": "adv-123",
  "name": "Summer Sale",
  "landing_page": "https://acme.com/summer",
  "daily_budget": 100,
  "status": "paused",
  "campaign_objective": "conversions",
  "is_active": false,
  "created_at": "2025-05-01T00:00:00",
  "start_time": "2025-05-01T00:00:00",
  "end_time": null,
  "daily_budget_remaining": 87.5,
  "allowed_countries": ["US", "CA"],
  "allowed_devices": ["web", "ios_app"],
  "utm_params": null,
  "custom_favicon_url": null,
  "deployment_status": "deployed",
  "review_status": "approved",
  "submitted_for_review_at": "2025-05-01T00:00:00",
  "reviewed_at": "2025-05-01T01:00:00",
  "review_notes": null
}
```

## PATCH `/advertiser-dashboard/campaigns/{campaign_id}/archive`

Archive a campaign, or restore an archived campaign. Each call changes the archived state.

**campaign\_id** `string` *required*

The campaign ID.

When you archive a campaign, Gravity also sets `is_active = false` and `status = "paused"`. When you restore it, the campaign stays paused. To start it again, send `PATCH /advertiser-dashboard/campaigns/{campaign_id}` with `"status": "active"`.

```bash Request theme={null}
curl -X PATCH https://platform.trygravity.com/advertiser-dashboard/campaigns/camp-456/archive \
  -H "X-API-Key: YOUR_API_KEY"
```

```json Response theme={null}
{
  "campaign_id": "camp-456",
  "is_archived": true
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.