Targeted Onsite Ad (TOA) — Partner Integration Guide

Overview

This guide walks you through the end-to-end workflow for creating and publishing Targeted Onsite Ad (TOA) campaigns using the Kroger Ad Platform (KAP) API v2. TOA is a display-style onsite ad experience — distinct from Product Listing Ads (PLAs) and Promoted Product Carousels — built from modular sub-domain endpoints: products, sub-commodity bids, a click-through destination, and a creative unit that must be reviewed and approved before the ad group can be published.

Note: This guide covers the TOA-specific workflow. For general campaign setup, refer to the Create a Campaign guide.


Before You Begin

PrerequisiteDetails
API accessValid API credentials for the KAP API (api.8451.com/kap/). Contact your account representative if you do not have credentials.
Advertiser accountAn active advertiser account with permissions to create campaigns and manage activations.
Creative contactEvery TOA campaign resolves an internal creative contact automatically — TOA creative requires review before it can go live.
Product/UPC listThe UPCs you intend to promote and/or proxy, ready to submit once the ad group exists.

What Makes TOA Different

AspectPLA / CarouselTOA
Product targetingentities array on the ad groupDedicated promoted products and proxy products endpoints
BiddingPer-product / entityPer sub-commodity (base bid + optional overrides)
Bid updates via general bid endpointSupportedNot supported — use the dedicated sub-commodity bids endpoint
Click-through destinationNot applicableRequired — curated_product_list (with optional generated URL) or manual_entry
PlacementsSearch & Browse, Basket Builder, SavingsSearch & Browse, Savings, Shop & Discover (min bid $22)
Creative review requiredVideo carousels onlyAlways required
Campaign objective fieldNot applicableAWARENESS, CONSIDERATION, or CONVERSION

Workflow Overview

The TOA workflow consists of five phases:

  1. Campaign setup — Create a TOA campaign with an objective.
  2. Ad group setup — Create the ad group with placements/targets and a base bid.
  3. Sub-domain configuration — Set promoted products, proxy products, sub-commodity bids, and the click-through destination.
  4. Creative workflow — Retrieve the creative design, assign assets, and submit for review.
  5. Submission and publication — Submit the ad group and publish once creative is approved.

Step-by-Step Instructions

Phase 1: Campaign Setup

Step 1 — Create a TOA Campaign

POST /v2/campaigns

Set campaignType to TOA. TOA is the only campaign type that accepts objective. A creative contact is resolved automatically for TOA campaigns — it is not a field on the create request.

Request

{
  "name": "Summer TOA Campaign",
  "campaignType": "TOA",
  "objective": "CONSIDERATION",
  "status": "DRAFT",
  "startDate": "2026-07-01",
  "endDate": "2026-07-31",
  "budgetAmount": 50000,
  "budgetType": "MONTHLY",
  "pacingType": "EVEN",
  "accountId": 100,
  "advertiserIds": [12],
  "billingContactId": 102,
  "billingAddressId": 5001
}

Response200 OK

{
  "data": {
    "id": 8101,
    "name": "Summer TOA Campaign",
    "campaignType": "TOA",
    "objective": "CONSIDERATION",
    "status": "DRAFT",
    "creativeContactId": 103,
    ...
  }
}

Save data.id as CAMPAIGN_ID.

Objective values

ValueMeaning
AWARENESSOptimize for reach/visibility
CONSIDERATIONOptimize for engagement
CONVERSIONOptimize for purchase — default when objective is omitted

objective is rejected with 400 if it's set on any non-TOA campaign type.


Phase 2: Ad Group Setup

Step 2 — Create a TOA Ad Group

POST /v2/ad_groups

Include at least one placement target that meets the placement's minimum. TOA creative is configured separately in Phase 4, so omit carousel-only creative fields.

TOA placements

PlacementMinimum bidId
Savings$22.004
Search & Browse$22.005
Shop & Discover$22.006

Request

{
  "campaignId": 8101,
  "name": "Summer TOA Ad Group",
  "startDate": "2026-07-01",
  "endDate": "2026-07-31",
  "budgetAmount": 15000,
  "budgetType": "MONTHLY",
  "status": "DRAFT",
  "baseBid": 22.5,
  "entities": [],
  "targets": [
    { "type": 1, "id": 4 },
    { "type": 1, "id": 5 }
  ]
}

Response200 OK

{
  "data": {
    "adGroupId": 9101,
    "campaignId": 8101,
    "name": "Summer TOA Ad Group",
    "status": "DRAFT",
    ...
  }
}

Save data.adGroupId as AD_GROUP_ID.

Use GET /v2/ad_groups/{AD_GROUP_ID}/available_products at any point to see which products are eligible for targeting under the ad group's divisions/placements.


Phase 3: Sub-Domain Configuration

TOA products, bids, and the click-through destination are managed through dedicated endpoints rather than the ad group's entities array. After the ad group exists, you can complete most sub-steps in the order that fits your workflow, but set promoted products before generating a click-through URL.

Step 3a — Set Promoted Products

POST /v2/ad_groups/{AD_GROUP_ID}/products/promoted

Request

{
  "data": [
    { "id": "0004300000287" },
    { "id": "0004300000294" }
  ]
}

Response200 OK

{
  "data": [
    { "id": "0004300000287" },
    { "id": "0004300000294" }
  ]
}

To incrementally add or remove promoted products instead of replacing the full list, PATCH the same path with a deleted flag per item:

{
  "data": [
    { "id": "0004300000301", "deleted": false },
    { "id": "0004300000294", "deleted": true }
  ]
}

Step 3b — Set Proxy Products (Optional)

Proxy products stand in for promoted products that are not yet available (e.g. a pre-launch item). onShelfDate is required and applies to the full proxy list.

POST /v2/ad_groups/{AD_GROUP_ID}/products/proxy

Request

{
  "onShelfDate": "2026-07-15",
  "products": [
    { "id": "0004300000350" }
  ]
}

Response200 OK

{
  "data": {
    "onShelfDate": "2026-07-15",
    "products": [
      { "id": "0004300000350" }
    ]
  }
}

The PATCH variant accepts the same shape with an optional onShelfDate and per-item deleted flags for incremental updates.

Step 3c — Set Sub-Commodity Bids

TOA bids at the sub-commodity level rather than at the individual product level. Use POST /v2/metadata/sub_commodities with your promoted UPCs to resolve their sub-commodity IDs first.

POST /v2/metadata/sub_commodities
{ "upcs": ["0004300000287", "0004300000294"] }
[
  { "subCommodity": { "id": "0200200007", "name": "Sparkling Water" } },
  { "subCommodity": { "id": "0200200015", "name": "Still Water" } }
]

Then set bids. baseBid is the fallback used whenever an item omits bidAmount. Bid updates via the ad group entities endpoint are not supported for TOA — use the TOA bids endpoint instead.

Note: Multiple UPCs can resolve to the same sub-commodity, so do not assume a 1:1 correspondence between the UPCs you submit and the sub-commodity items returned.

POST /v2/ad_groups/{AD_GROUP_ID}/bids/sub_commodity

Request

{
  "baseBid": 22.5,
  "data": [
    { "id": "0200200007" },
    { "id": "0200200015", "bidAmount": 25.0 }
  ]
}

Response200 OK

{
  "data": [
    { "id": "0200200007", "bidAmount": null },
    { "id": "0200200015", "bidAmount": 25.0 }
  ],
  "errors": []
}

Use PATCH on the same path to merge additional sub-commodity bids into the existing list.

Step 3d — Set the Click-Through Destination

POST /v2/ad_groups/{AD_GROUP_ID}/click_through

Supported type values are curated_product_list and manual_entry. url is required for manual_entry.

Request — manual entry

{
  "type": "manual_entry",
  "url": "https://www.kroger.com/promo/summer-toa"
}

Request — curated product list

{
  "type": "curated_product_list",
  "config": { "keyword": "summer-refresh" }
}

Response200 OK

{
  "data": {
    "type": "curated_product_list",
    "url": null,
    "config": { "keyword": "summer-refresh" }
  }
}

If you used curated_product_list, first set the promoted products with POST /v2/ad_groups/{AD_GROUP_ID}/products/promoted, then generate the resulting URL:

POST /v2/ad_groups/{AD_GROUP_ID}/click_through/generate

Use GET/PATCH on /v2/ad_groups/{AD_GROUP_ID}/click_through to retrieve or update the destination later.


Phase 4: Creative Workflow

TOA reuses the same creative design/asset model as video carousels — a design with text and image fields — but without a video field.

Step 4a — Create creative for the ad group

POST /v2/ad_groups/{ad_group_id}/creative

Request

{
  "shape": "API",
  "include": ["validationErrors", "assetReferences"],
  "designGroupName": "TOA Standard"
}

Response201 OK

{
  "data": {
    "id": "creative-group-toa-001",
    "status": "DRAFT",
    "designs": [
      {
        "id": "design-toa-100",
        "name": "TOA Standard",
        "fields": [
          { "id": "field-headline-001", "displayName": "Headline", "fieldDataType": "TEXT" },
          { "id": "field-subtext-002", "displayName": "Subtext", "fieldDataType": "TEXT" },
          { "id": "field-image-003", "displayName": "Image", "fieldDataType": "ZONE" }
        ]
      }
    ]
  }
}

Step 4b — Retrieve the Creative Design ID and Field IDs

GET /v2/ad_groups/{AD_GROUP_ID}/creative?include=validationErrors

Response200 OK

{
  "data": {
    "id": "creative-group-toa-001",
    "status": "DRAFT",
    "designs": [
      {
        "id": "design-toa-100",
        "name": "TOA Standard",
        "fields": [
          { "id": "field-headline-001", "displayName": "Headline", "fieldDataType": "TEXT" },
          { "id": "field-subtext-002", "displayName": "Subtext", "fieldDataType": "TEXT" },
          { "id": "field-image-003", "displayName": "Image", "fieldDataType": "ZONE" }
        ]
      }
    ]
  }
}

Save data.designs[0].id as DESIGN_ID and each fields[].id mapped to its displayName (e.g. HEADLINE_FIELD_ID, SUBTEXT_FIELD_ID, IMAGE_FIELD_ID).

Step 5 — Upload the Image Asset and Assign Creative Fields

Upload the image asset using the same three-request sequence as other creative types (POST /v2/creative_asset/upload_urlPUT {uploadUrl}POST /v2/creative_asset), then assign it — along with the text fields — to the design.

PATCH /v2/ad_groups/{AD_GROUP_ID}/creative

Request

{
  "persistChanges": true,
  "designs": [
    {
      "id": "design-toa-100",
      "fields": [
        { "id": "field-headline-001", "textValue": "Fresh picks for summer" },
        { "id": "field-subtext-002", "textValue": "Shop now at Kroger" },
        { "id": "field-image-003", "assetId": "asset-img-40021" }
      ]
    }
  ]
}

Set persistChanges to true to save the creative field updates. If persistChanges is omitted or set to false, the request runs validation only and does not persist the submitted creative changes.

Verify before submitting:

GET /v2/ad_groups/{AD_GROUP_ID}/creative?include=validationErrors

Resolve any included.validationErrors entries before proceeding.

Step 6 — Submit the Creative for Review

PATCH /v2/ad_groups/{AD_GROUP_ID}/creative/status

Request — first submission

{
  "status": "UNDER_REVIEW",
  "comment": "TOA creative ready for review."
}

Request — resubmission after rejection

{
  "status": "RE_SUBMITTED",
  "comment": "Updated headline and image per reviewer feedback."
}

When REJECTED, update the flagged fields/assets and resubmit with RE_SUBMITTED.


Phase 5: Submission and Publication

Once creative is approved, move the ad group through its lifecycle. All lifecycle actions are idempotent PATCH calls that return the refreshed ad group.

PATCH /v2/ad_groups/{AD_GROUP_ID}/submit
PATCH /v2/ad_groups/{AD_GROUP_ID}/publish

Response200 OK (shape shared by all lifecycle actions)

{
  "data": {
    "adGroupId": 9101,
    "campaignId": 8101,
    "status": "SCHEDULED",
    ...
  }
}

status reflects the ad group's real, current state (e.g. DRAFT, SCHEDULED, ACTIVE, PAUSED, UNDER_REVIEW, ENDED, FAILED) — it is derived from the underlying activation, not a fixed value per action.

Additional lifecycle actions are available at the same base path: pause, unpause, archive, discard, and end_now.


Endpoints Quick Reference

StepMethodEndpointDescription
1POST/v2/campaignsCreate a TOA campaign with an objective
2POST/v2/ad_groupsCreate the TOA ad group
GET/v2/ad_groups/{id}/available_productsList products eligible for targeting
3aPOST / PATCH/v2/ad_groups/{id}/products/promotedSet or merge promoted UPCs
3bPOST / PATCH/v2/ad_groups/{id}/products/proxySet or merge proxy UPCs + onShelfDate
3cPOST/v2/metadata/sub_commoditiesResolve sub-commodity IDs from UPCs
3cPOST / PATCH/v2/ad_groups/{id}/bids/sub_commoditySet or merge sub-commodity bids
3dPOST / GET / PATCH/v2/ad_groups/{id}/click_throughCreate, retrieve, or update the click-through destination
3dPOST/v2/ad_groups/{id}/click_through/generateGenerate a curated product list URL
4GET/v2/ad_groups/{id}/creative?include=validationErrorsRetrieve creative design ID and field IDs
5PATCH/v2/ad_groups/{id}/creativeAssign headline/subtext/image to the creative
6PATCH/v2/ad_groups/{id}/creative/statusSubmit the creative for review or resubmit after rejection
7PATCH/v2/ad_groups/{id}/submitSubmit the ad group
8PATCH/v2/ad_groups/{id}/publishPublish the ad group
PATCH/v2/ad_groups/{id}/pause | /unpause | /archive | /discard | /end_nowAdditional lifecycle actions

Get TOA campaign and ad group info

MethodEndpointDescription
GET/campaigns/{campaign_id}Get campaign
GET/campaigns/{campaign_id}/remaining_budgetGet campaign remaining budget
GET/ad_groups/{ad_group_id}Get ad group base information
GET/ad_groups/{ad_group_id}/bids/sub_commodityGet ad group sub commodity bids
GET/ad_groups/{ad_group_id}/click_throughGet ad group click through destination
GET/ad_groups/{ad_group_id}/available_productsGet ad group available products
GET/ad_groups/{ad_group_id}/products/promotedGet ad group promoted products
GET/ad_groups/{ad_group_id}/products/proxyGet ad group proxy products
GET/ad_groups/{ad_group_id}/creativeGet ad group creative design

Common Errors

CauseResolution
objective sent on a non-TOA campaignRemove objective, or change campaignType to TOA
baseBid below the placement's $22 minimumIncrease baseBid or the relevant sub-commodity bidAmount
Attempting to update TOA bids through /v2/ad_groups/{id}/entitiesUse /v2/ad_groups/{AD_GROUP_ID}/bids/sub_commodity for TOA bids instead
Proxy products submitted without onShelfDateInclude a valid onShelfDate (YYYY-MM-DD) with every proxy product POST request
manual_entry click-through submitted without urlInclude url when type is manual_entry
Publishing before creative is approvedWait for creative approval, then retry submit/publish
Modifying a creative that is under reviewWait for review to complete before making changes