---
title: "Toa Workflow Partner Integration Guide"
url: "https://api-catalog-test.8451.com/apis/kroger-ad-platform-api-1/docs/documentation/toa-workflow-partner-integration-guide"
image: "https://api-catalog-test.8451.com/_og/d/c_Ocean.takumi,title_Toa+Workflow+Partner+Integration+Guide,props_eyJ0aGVtZSI6eyJtb2RlIjoibGlnaHQiLCJjb2xvcnMiOnsicHJpbWFyeSI6IiM4NDUxRUMifX19,p_Ii9hcGlzL2tyb2dlci1hZC1wbGF0Zm9ybS1hcGktMS9kb2NzL2RvY3VtZW50YXRpb24vdG9hLXdvcmtmbG93LXBhcnRuZXItaW50ZWdyYXRpb24tZ3VpZGUi,s_VN0uJFVsLoWvAgnd.png"
---

# Targeted Onsite Ad (TOA) — Partner Integration Guide

## [Overview](#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](https://mp-help.8451.com/mp-help/content/how-to/create-campaign.htm) guide.

---

## [Before You Begin](#before-you-begin)

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

---

## [What Makes TOA Different](#what-makes-toa-different)

| Aspect                               | PLA / Carousel                           | TOA                                                                           |
| :----------------------------------- | :--------------------------------------- | :---------------------------------------------------------------------------- |
| Product targeting                    | entities array on the ad group           | Dedicated promoted products and proxy products endpoints                      |
| Bidding                              | Per-product / entity                     | Per sub-commodity (base bid + optional overrides)                             |
| Bid updates via general bid endpoint | Supported                                | Not supported — use the dedicated sub-commodity bids endpoint                 |
| Click-through destination            | Not applicable                           | Required — curated_product_list (with optional generated URL) or manual_entry |
| Placements                           | Search & Browse, Basket Builder, Savings | Search & Browse, Savings, Shop & Discover (min bid $22)                       |
| Creative review required             | Video carousels only                     | Always required                                                               |
| Campaign objective field             | Not applicable                           | AWARENESS, CONSIDERATION, or CONVERSION                                       |

---

## [Workflow Overview](#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.

```mermaid
flowchart TD
    S1["1. POST /v2/campaigns<br/>campaignType: TOA, objective: ..."] --> S2["2. POST /v2/ad_groups<br/>Set placements/targets"]
    S2 --> S3["3a. POST /v2/ad_groups/{id}/products/promoted"]
    S3 --> S3B["3b. POST /v2/ad_groups/{id}/products/proxy (optional)"]
    S3B --> S3C["3c. POST /v2/ad_groups/{id}/bids/sub_commodity"]
    S3C --> S3D["3d. POST /v2/ad_groups/{id}/click_through"]
    S3D --> S4A["4a. POST /v2/ad_groups/{ad_group_id}/creative"]
    S4A --> S4B["4b. GET creative<br/>Retrieve design ID + field IDs"]
    S4B --> S5["5. PATCH /v2/ad_groups/{id}/creative<br/>Assign headline/subtext/image fields"]
    S5 --> VERIFY{"validationErrors<br/>clear?"}
    VERIFY -- No --> FIX["Resolve validation errors"]
    FIX --> S5
    VERIFY -- Yes --> S6["6. PATCH /v2/ad_groups/{id}/creative/status<br/>Submit: UNDER_REVIEW"]
    S6 --> OUTCOME{"Review outcome"}
    OUTCOME -- APPROVED --> SUB["7. PATCH /v2/ad_groups/{id}/submit"]
    OUTCOME -- REJECTED --> REFETCH["Update creative, resubmit"]
    REFETCH --> S5
    SUB --> PUB["8. PATCH /v2/ad_groups/{id}/publish"]
```

---

## [Step-by-Step Instructions](#step-by-step-instructions)

### [Phase 1: Campaign Setup](#phase-1-campaign-setup)

#### [Step 1 — Create a TOA Campaign](#step-1-create-a-toa-campaign)

```text
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**

```json
{
  "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
}
```

**Response** — `200 OK`

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

> Save `data.id` as `CAMPAIGN_ID`.

**Objective values**

| Value         | Meaning                                                   |
| :------------ | :-------------------------------------------------------- |
| AWARENESS     | Optimize for reach/visibility                             |
| CONSIDERATION | Optimize for engagement                                   |
| CONVERSION    | Optimize 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](#phase-2-ad-group-setup)

#### [Step 2 — Create a TOA Ad Group](#step-2-create-a-toa-ad-group)

```text
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**

| Placement       | Minimum bid | Id  |
| :-------------- | :---------- | :-- |
| Savings         | $22.00      | 4   |
| Search & Browse | $22.00      | 5   |
| Shop & Discover | $22.00      | 6   |

**Request**

```json
{
  "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 }
  ]
}
```

**Response** — `200 OK`

```json
{
  "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](#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](#step-3a-set-promoted-products)

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

**Request**

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

**Response** — `200 OK`

```json
{
  "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:

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

#### [Step 3b — Set Proxy Products (Optional)](#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.

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

**Request**

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

**Response** — `200 OK`

```json
{
  "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](#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.

```text
POST /v2/metadata/sub_commodities
```

```json
{ "upcs": ["0004300000287", "0004300000294"] }
```

```json
[
  { "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.

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

**Request**

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

**Response** — `200 OK`

```json
{
  "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](#step-3d-set-the-click-through-destination)

```text
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**

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

**Request — curated product list**

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

**Response** — `200 OK`

```json
{
  "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:

```text
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](#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. The creative endpoints and payloads are shared with the [Video Carousel Creative — Partner Integration Guide](https://api-catalog-test.8451.com/apis/kroger-ad-platform-api-1/docs/documentation/video-carousel-creative-partner-integration-guide.md); see its Step 3 (retrieve design/field IDs) and Step 6 (assign assets) for additional detail.

> **Always send `shape=API` on the creative `GET` and `PATCH` endpoints.** The `shape` query parameter selects the response representation. When it is omitted, Creative Service returns only the top-level fields and the nested collections — `designs`, `groupFields`, `assetReferences`, and `accountInfo` — are omitted from the response. Their absence means _"not included in the requested shape,"_ **not** that the data is empty; the values are still saved and are returned when you request `shape=API`.

#### [Step 4a — Create creative for the ad group](#step-4a-create-creative-for-the-ad-group)

```text
POST /v2/ad_groups/{ad_group_id}/creative
```

**Request**

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

**Response** — `201 OK`

```json
{
  "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](#step-4b-retrieve-the-creative-design-id-and-field-ids)

```text
GET /v2/ad_groups/{AD_GROUP_ID}/creative?shape=API&include=validationErrors
```

**Query parameters**

| Parameter | Value            | Purpose                                                                          |
| :-------- | :--------------- | :------------------------------------------------------------------------------- |
| shape     | API              | Returns the machine-readable API shape with populated designs, groupFields, assetReferences, and accountInfo. Omitting it omits these fields from the response. |
| include   | validationErrors | Returns any current validation errors                                            |

**Response** — `200 OK`

```json
{
  "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](#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_url` → `PUT {uploadUrl}` → `POST /v2/creative_asset`), then assign it — along with the text fields — to the design.

```text
PATCH /v2/ad_groups/{AD_GROUP_ID}/creative?shape=API
```

**Request**

```json
{
  "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. Include `shape=API` so the response returns the fully populated creative (with `designs`, `groupFields`, and `assetReferences`) rather than omitting those nested fields.

**Verify before submitting:**

```text
GET /v2/ad_groups/{AD_GROUP_ID}/creative?shape=API&include=validationErrors
```

Resolve any `included.validationErrors` entries before proceeding.

#### [Step 6 — Submit the Creative for Review](#step-6-submit-the-creative-for-review)

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

**Request — first submission**

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

**Request — resubmission after rejection**

```json
{
  "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](#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.

```text
PATCH /v2/ad_groups/{AD_GROUP_ID}/submit
```

```text
PATCH /v2/ad_groups/{AD_GROUP_ID}/publish
```

**Response** — `200 OK` (shape shared by all lifecycle actions)

```json
{
  "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](#endpoints-quick-reference)

| Step | Method             | Endpoint                                                                 | Description                                                |
| :--- | :----------------- | :----------------------------------------------------------------------- | :--------------------------------------------------------- |
| 1    | POST               | /v2/campaigns                                                            | Create a TOA campaign with an objective                    |
| 2    | POST               | /v2/ad_groups                                                            | Create the TOA ad group                                    |
| —    | GET                | /v2/ad_groups/{id}/available_products                                    | List products eligible for targeting                       |
| 3a   | POST / PATCH       | /v2/ad_groups/{id}/products/promoted                                     | Set or merge promoted UPCs                                 |
| 3b   | POST / PATCH       | /v2/ad_groups/{id}/products/proxy                                        | Set or merge proxy UPCs + onShelfDate                      |
| 3c   | POST               | /v2/metadata/sub_commodities                                             | Resolve sub-commodity IDs from UPCs                        |
| 3c   | POST / PATCH       | /v2/ad_groups/{id}/bids/sub_commodity                                    | Set or merge sub-commodity bids                            |
| 3d   | POST / GET / PATCH | /v2/ad_groups/{id}/click_through                                         | Create, retrieve, or update the click-through destination  |
| 3d   | POST               | /v2/ad_groups/{id}/click_through/generate                                | Generate a curated product list URL                        |
| 4    | GET                | /v2/ad_groups/{id}/creative?shape=API&include=validationErrors           | Retrieve creative design ID and field IDs                  |
| 5    | PATCH              | /v2/ad_groups/{id}/creative?shape=API                                    | Assign headline/subtext/image to the creative              |
| 6    | PATCH              | /v2/ad_groups/{id}/creative/status                                       | Submit the creative for review or resubmit after rejection |
| 7    | PATCH              | /v2/ad_groups/{id}/submit                                                | Submit the ad group                                        |
| 8    | PATCH              | /v2/ad_groups/{id}/publish                                               | Publish the ad group                                       |
| —    | PATCH              | /v2/ad_groups/{id}/pause \| /unpause \| /archive \| /discard \| /end_now | Additional lifecycle actions                               |

---

**Get TOA campaign and ad group info**

| Method | Endpoint                                    | Description                                                                      |
| :----- | :------------------------------------------ | :------------------------------------------------------------------------------- |
| GET    | /campaigns/{campaign_id}                    | Get campaign                                                                     |
| GET    | /campaigns/{campaign_id}/remaining_budget   | Get campaign remaining budget                                                    |
| GET    | /ad_groups/{ad_group_id}                    | Get ad group base information                                                    |
| GET    | /ad_groups/{ad_group_id}/bids/sub_commodity | Get ad group sub commodity bids                                                  |
| GET    | /ad_groups/{ad_group_id}/click_through      | Get ad group click through destination                                           |
| GET    | /ad_groups/{ad_group_id}/available_products | Get ad group available products                                                  |
| GET    | /ad_groups/{ad_group_id}/products/promoted  | Get ad group promoted products                                                   |
| GET    | /ad_groups/{ad_group_id}/products/proxy     | Get ad group proxy products                                                      |
| GET    | /ad_groups/{ad_group_id}/creative?shape=API | Get ad group creative design (use shape=API for fields with designs, groupFields, assetReferences, and accountInfo) |

---

## [Common Errors](#common-errors)

| Cause                                                             | Resolution                                                                     |
| :---------------------------------------------------------------- | :----------------------------------------------------------------------------- |
| objective sent on a non-TOA campaign                              | Remove objective, or change campaignType to TOA                                |
| baseBid below the placement's $22 minimum                         | Increase baseBid or the relevant sub-commodity bidAmount                       |
| Attempting to update TOA bids through /v2/ad_groups/{id}/entities | Use /v2/ad_groups/{AD_GROUP_ID}/bids/sub_commodity for TOA bids instead        |
| Proxy products submitted without onShelfDate                      | Include a valid onShelfDate (YYYY-MM-DD) with every proxy product POST request |
| manual_entry click-through submitted without url                  | Include url when type is manual_entry                                          |
| Publishing before creative is approved                            | Wait for creative approval, then retry submit/publish                          |
| Modifying a creative that is under review                         | Wait for review to complete before making changes                              |