Reserved Campaigns

Creating a Reserved Campaign

To create a reserved campaign, send a POST request to the following endpoint with the campaign details.

Example:
POST https://api.kevel.co/v1/forecaster/reserved-campaign

{
  "name": "Holiday Campaign 2025",
  "description": "Premium holiday campaign for Q4",
  "salesProbabilityWeight": 75,
  "advertiser": {
    "id": 10
  },
  "flights": [
    {
      "priorityId": 221170,
      "rate": {
        "rateType": 2,
        "price": 5.5
      },
      "startDate": "2025-10-01T00:00:00.000",
      "endDate": "2025-12-31T23:59:59.000",
      "timeZone": "UTC",
      "goalType": 1,
      "goalAmount": 1000000,
      "ads": [
        {
          "adType": 4649
        }
      ]
    }
  ]
}

Response:

{
  "id": 1234,
  "name": "Holiday Campaign 2025",
  "status": "reserved",
  "description": "Premium holiday campaign for Q4",
  "salesProbabilityWeight": 75,
  "advertiser": {
    "id": 10
  },
  "flights": [
    {
      "id": 5678,
      "priorityId": 221170,
      "rate": {
        "rateType": 2,
        "price": 5.5
      },
      "ads": [
        {
          "id": 9012,
          "adType": 4649
        }
      ],
      "startDate": "2025-10-01T00:00:00.000",
      "endDate": "2025-12-31T23:59:59.000",
      "timeZone": "UTC",
      "goalType": 1,
      "goalAmount": 1000000
    }
  ]
}

Key Fields

  • salesProbabilityWeight (0-100): Represents the probability that this reserved campaign will convert to a booked campaign. Higher values indicate a higher likelihood of conversion.
  • status: Can be reserved (campaign is reserved but not yet booked) or booked (campaign is confirmed and won't be considered as a reservation in forecasts).

Listing Reserved Campaigns

To view all reserved campaigns for your network:

Example:
GET https://api.kevel.co/v1/forecaster/reserved-campaign

Response:

{
  "1234": {
    "id": 1234,
    "name": "Holiday Campaign 2025",
    "status": "reserved",
    "salesProbabilityWeight": 75,
    "advertiser": { "id": 10 },
    "flights": [...]
  }
}

Configuring Sales Probability Cut-Off (Optional)

You can configure a network-wide cut-off threshold to filter which reserved campaigns are considered during forecasts. If not configured, all will be considered. Only reserved campaigns with a salesProbabilityWeight equal to or greater than this threshold will be included in forecasts.

Example:
POST https://api.kevel.co/v1/forecaster/network-settings

{
  "salesProbabilityWeight": 50,
  ...
}

Note: The network settings endpoint requires other fields not related to this feature. See the Network Settings API for the full schema.

This means only reserved campaigns with salesProbabilityWeight >= 50 will be considered in forecasts.

Checking Current Settings:
GET https://api.kevel.co/v1/forecaster/network-settings

Response:

{
  "salesProbabilityWeight": 50,
  ...
}

Waiting for Data Ingestion

Reserved campaigns are synchronized with the forecast engine periodically. After creating or updating a reserved campaign, there may be a delay before it is reflected in forecasts. The synchronization timestamp is included in forecast responses (see below).

Running a Forecast

After creating reserved campaigns, you can run an availability forecast to see how they impact inventory. To verify the reserved campaign is being considered, use the BookedBy parameter to break down booked results by campaign. This will show your reserved campaign ID in the results.

Example:
POST https://api.kevel.co/v1/forecaster

{
  "Type": "available",
  "StartDate": "2025-10-01",
  "EndDate": "2025-12-31",
  "Priority": 1,
  "Params": {
    "Sampling": 3,
    "BookedBy": ["campaign"]
  }
}

The BookedBy: ["campaign"] parameter ensures that the booked results are broken down by campaign ID, allowing you to see which campaigns (including reserved ones) are consuming inventory.

Verifying Reserved Campaigns in Forecast Results

After the forecast completes, you can verify that reserved campaigns were considered in two ways:

  1. Check the forecastEnvironment for synchronization metadata.
  2. Look for the reserved campaign ID in the booked.byCampaign results (only campaigns that consumed inventory will appear; if your reserved campaign ID doesn't show up, it means the campaign didn't consume any inventory during the forecast period).

Example:
GET https://api.kevel.co/v1/forecaster/:id

Response:

{
  "id": ":id",
  "status": "finished",
  "progress": 100.0,
  "desc": "Forecast is finished",
  "forecastEnvironment": {
    "campaignSyncDateTime": "2025-10-28T12:16:30.885",
    "seasonalityUsed": false,
    "reservedCampaignsSyncDateTime": "2025-10-28T12:20:15.123",
    "reservedCampaignSalesWeightCutOff": 50
  },
  "result": {
    "total": {
      "available": {
        "impressions": 5000000,
        "clicks": 25000,
        "uniqueUsers": 1200000
      },
      "booked": {
        "impressions": 2000000,
        "clicks": 10000,
        "uniqueUsers": 500000,
        "byCampaign": {
          "rc-1234": {
            "impressions": 750000,
            "clicks": 3750,
            "uniqueUsers": 180000
          },
          "5001": {
            "impressions": 1250000,
            "clicks": 6250,
            "uniqueUsers": 320000
          }
        }
      }
    }
  }
}

In this example, the reserved campaign we created (ID 1234) appears in booked.byCampaign (prefixed by "rc-" to avoid collisions with existing campaigns), showing the forecasted impressions, clicks, and unique users it will consume. The other campaign (5001) represents an existing booked campaign from the ad server.

Verifying the Reserved Campaign

  • Reserved campaign ID 1234 appears in booked.byCampaign with its forecasted delivery metrics.
  • forecastEnvironment.reservedCampaignsSyncDateTime: Confirms when reserved campaigns data was synchronized.
  • forecastEnvironment.reservedCampaignSalesWeightCutOffUsed: Shows the cut-off threshold applied (campaigns with salesProbabilityWeight >= this value were included).

Updating a Reserved Campaign

To update an existing reserved campaign (e.g., change the probability weight):

Example (PATCH - partial update):
PATCH https://api.kevel.co/v1/forecaster/reserved-campaign/1234

{
  "salesProbabilityWeight": 90
}

Example (PUT - full replacement):
PUT https://api.kevel.co/v1/forecaster/reserved-campaign/1234

{
  "id": 1234,
  "name": "Holiday Campaign 2025",
  "salesProbabilityWeight": 90,
  "advertiser": { "id": 10 },
  "flights": [
    {
      "id": 5678,
      "priorityId": 221170,
      "rate": { "rateType": 2, "price": 5.50 },
      "startDate": "2025-10-01T00:00:00.000",
      "endDate": "2025-12-31T23:59:59.000",
      "goalType": 1,
      "goalAmount": 1000000,
      "ads": [{ "id": 9012, "adType": 4649 }]
    }
  ]
}
📘

Note: When using PUT, include all flight and ad IDs to update them in-place. Omitting an ID creates a new entity; omitting an entire flight/ad from the request will delete it.

Note: As with campaign creation, updates are synchronized periodically with the forecast engine. There may be a delay before changes are reflected in forecasts.

Going from Reserved to Booked

Reserved Campaigns and regular Campaigns are separate entities. To transform a Reserved Campaign into a real Campaign, you create a Campaign as you normally would, marking it as Active, containing the same details as the Reserved Campaign plus the missing pieces, if any, such as the Creative assets for each Ad. You can then mark the Reserved Campaign as Booked, to avoid holding the same inventory twice.

If you manage campaigns via API, you can leverage the Reserved Campaign entity by fetching its definition from the Reserved Campaigns API and transform it to the corresponding Campaign with the same Flights and Ads.

Using the UI for now will require the manual Campaign booking as usual. Kevel plans to release a simplified workflow that will enable starting a new Campaign from the definition of a Reserved Campaign to facilitate the process.

Marking a Campaign as Booked

When a reserved campaign is confirmed and booked in Kevel, update its status to booked so it is no longer considered as a reservation in forecasts:

Example:
PATCH https://api.kevel.co/v1/forecaster/reserved-campaign/1234

{
  "status": "booked"
}
📘

Note: As with campaign creation and updates, status changes are synchronized periodically with the forecast engine. There may be a delay before the campaign stops being considered as a reservation in forecasts.