Skip to main content
PUT /api/v2/buyer/advertisers/{advertiserId}/catalogs/{catalogId}/transform Saves the active transform for a catalog: a deterministic rule set that maps raw feed items into campaign groups, budget hints, and creative-asset prompts. Saving a new transform archives the previous active one — a catalog has exactly one active transform at a time.

How catalog activation works

A transform has three parts, applied in order:
  1. Derived fields (transform.fields) — compute new fields from raw item data. Each field pulls from a source dot-path (e.g. location.city), a template of {{field}} placeholders, or a default, and can be normalized with a transform such as slug or title_case. Derived fields feed the grouping and template rules below.
  2. Campaign grouping and budget (transform.campaign) — fan the items out into campaign groups. groupBy names the fields to group on (usually derived fields such as city); nameTemplate/keyTemplate label each group; budget sets a spend hint per group.
  3. Creative-asset prompts (transform.creativeAssets) — define reusable creative generation prompts. Each entry has a promptTemplate, a keyTemplate cache key (assets sharing a key reuse generated media), and an optional groupBy to fan out unique prompts.
The lifecycle across the four catalog-activation operations:
  1. Save transform (this operation) — persist the active transform.
  2. Preview activation — dry-run the plan to see exactly what would be created, or pass save: true to persist it.
  3. Execute activation — materialize the plan into campaign, creative-generation, and seller-syndication jobs.
  4. Refresh catalog — pull the latest feed and, optionally, re-run activation.
Worked example. A catalog of 6 products, each with a raw city, is transformed with a derived city field, campaign.groupBy: ["city"], and nameTemplate: "Acme - {{city}}". Preview fans the 6 items out into 3 campaign groups — Acme - Austin, Acme - Denver, Acme - Portland — each with 2 items. You can build and run the whole flow in the UI under Data sources → Catalogs (Preview activation, Refresh feed, Refresh and activate).

Request

curl
curl -X PUT https://api.interchange.io/api/v2/buyer/advertisers/12345/catalogs/summer-sale-2026/transform \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "City fan-out",
    "transform": {
      "fields": [
        { "key": "city", "source": "location.city" },
        { "key": "citySlug", "source": "location.city", "transform": "slug" }
      ],
      "campaign": {
        "groupBy": ["city"],
        "keyTemplate": "acme-{{citySlug}}",
        "nameTemplate": "Acme - {{city}}",
        "budget": { "mode": "fixed", "amount": 100, "currency": "USD" }
      },
      "creativeAssets": [
        {
          "keyTemplate": "acme-city-{{citySlug}}",
          "promptTemplate": "A vibrant product ad set in {{city}}",
          "groupBy": ["city"]
        }
      ]
    }
  }'

Parameters

FieldTypeRequiredNotes
advertiserIdstring (path)YesUnique identifier for the advertiser
catalogIdstring (path)YesBuyer-assigned catalog_id or the platform catalogId from list catalogs
namestringNoDisplay name for the transform (1–255 chars). Defaults to Default transform
transformobjectYesThe transform definition (three parts below)
transform.fieldsarrayNoDerived fields. Default []
transform.fields[].keystringYesDerived field key. Must start with a letter and contain only letters, numbers, or underscores
transform.fields[].sourcestringNoDot-path into the raw item, e.g. location.city
transform.fields[].templatestringNoTemplate using {{field}} placeholders from raw or derived fields
transform.fields[].defaultanyNoFallback when source/template resolve empty
transform.fields[].transformenumNoValue normalization: string, lowercase, uppercase, title_case, slug
transform.campaignobjectNoCampaign grouping and budget
transform.campaign.groupByarrayYes*Field references to group on (≥1). *Required when campaign is present
transform.campaign.keyTemplatestringNoCampaign group key template
transform.campaign.nameTemplatestringNoHuman-readable campaign name template
transform.campaign.budgetobjectNoBudget rule (see below)
transform.creativeAssetsarrayNoCreative-asset prompt definitions. Default []
transform.creativeAssets[].keyTemplatestringYesStable cache key template; assets with the same key reuse generated media
transform.creativeAssets[].promptTemplatestringYesPrompt template for the creative generation path
transform.creativeAssets[].groupByarrayNoField references to fan out unique prompts. Default []
Each fields[] entry must define at least one of source, template, or default. Budget rule (transform.campaign.budget) is a tagged union on mode:
modeFieldsMeaning
noneNo budget hint
fixedamount, currency (default USD)Same fixed budget per campaign group
per_itemamountPerItem, currencyBudget scales with item count in the group
field_sumfield, multiplier (default 1), currencySum a numeric field across items, times a multiplier

Response

{
  "transform": {
    "transformId": "10",
    "catalogId": "summer-sale-2026",
    "platformCatalogId": "14",
    "name": "City fan-out",
    "status": "active",
    "definitionHash": "a1b2c3…",
    "definition": {
      "fields": [
        { "key": "city", "source": "location.city" },
        { "key": "citySlug", "source": "location.city", "transform": "slug" }
      ],
      "campaign": {
        "groupBy": ["city"],
        "keyTemplate": "acme-{{citySlug}}",
        "nameTemplate": "Acme - {{city}}",
        "budget": { "mode": "fixed", "amount": 100, "currency": "USD" }
      },
      "creativeAssets": [
        {
          "keyTemplate": "acme-city-{{citySlug}}",
          "promptTemplate": "A vibrant product ad set in {{city}}",
          "groupBy": ["city"]
        }
      ]
    },
    "createdAt": "2026-06-07T12:00:00Z",
    "updatedAt": "2026-06-07T12:00:00Z"
  }
}
The saved transform becomes active; the previously active transform (if any) is set to archived.

Errors

  • 400 VALIDATION_ERROR — the transform fails schema validation (e.g. a fields[] entry defines none of source/template/default, or campaign.groupBy is empty).
  • 404 NOT_FOUNDadvertiserId or catalogId does not exist or is not visible to the authenticated account.
See Errors for the full error contract.

Preview activation

Dry-run the campaign/creative fan-out

Execute activation

Materialize the plan into jobs

Refresh catalog

Pull the latest feed and re-activate

Sync catalogs

Push catalog feeds and items