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

# Ad-server signal components

> Build signal components from targeting in a connected ad server

`/api/v2/storefront/esa/{esaId}/signals`

The ad-server signal-component endpoints let a storefront operator build and
manage the targeting components exposed by an ad server source. The `/esa/`
path segment and `esaId` field are stable API identifiers; the operator-facing
object is an **ad server source**.

Keep these two layers distinct:

| Term                    | Meaning                                                                                                                                    |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Ad-server targeting** | Source-native audience segments, custom targeting keys, and custom targeting values already present in the connected ad server             |
| **Signal component**    | A named targeting definition the seller creates from one or more source-native targeting primitives; carried as a `signal` on the API wire |

Browsing ad-server targeting does not create or import a signal component. It
shows the raw material available for authoring.

Start every authoring workflow by reading source capabilities. Capabilities
describe which mapping kinds, API candidate types, value types, composition
models, and parent-filtered types the source supports. Do not assume every ad
server source supports GAM fields.

## Endpoints

| Method | Endpoint                                                      | Description                                                                               |
| ------ | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `GET`  | `/api/v2/storefront/esa/{esaId}/signals`                      | List signal components created on the source                                              |
| `GET`  | `/api/v2/storefront/esa/{esaId}/signals/{signalId}`           | Read one signal component                                                                 |
| `POST` | `/api/v2/storefront/esa/{esaId}/signals`                      | Create a signal component. Add `?dry_run=true` to validate and preview without persisting |
| `GET`  | `/api/v2/storefront/esa/{esaId}/signals/adapter-capabilities` | Describe source support for signal-component authoring                                    |
| `GET`  | `/api/v2/storefront/esa/{esaId}/signals/candidates`           | Browse source-native ad-server targeting; `candidates` is the API response name           |

## Bulk signal-component creation with Murph

When you need to create many signal components, ask Murph to create them as a
batch instead of one by one. Murph still validates and previews the proposed
components before any durable write, and the confirmation gate still fires
before creation. The batch is approved once, then Murph creates the set
together.

* **Paste a list or upload rows.** Murph maps each row to the source's supported signal-component shape after reading source capabilities.
* **One approval for the batch.** You review the full set and approve once rather than approving every component individually.
* **Continues on error.** If one component fails, the rest continue. Murph reports a per-component result so you can retry only the failures.

Use this for requests such as "create signal components from these 49 GAM
values," "set up my first audience components," or "build components from
these key-values."

## Source capabilities

```bash theme={null}
curl https://api.interchange.io/api/v2/storefront/esa/123/signals/adapter-capabilities \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
```

```json theme={null}
{
  "data": {
    "adapter": "google_ad_manager",
    "supportsSignalMappingAuthoring": true,
    "mappingKinds": [
      {
        "mappingKind": "audience_segment",
        "label": "Audience segment",
        "candidateType": "audience_segment",
        "supportsSearch": true
      },
      {
        "mappingKind": "gam_targeting_groups",
        "label": "GAM targeting groups",
        "candidateType": null,
        "supportsSearch": false
      }
    ],
    "supportedCandidateTypes": [
      "audience_segment",
      "custom_targeting_key",
      "custom_targeting_value"
    ],
    "candidateTypes": [
      {
        "candidateType": "custom_targeting_key",
        "label": "Custom targeting key",
        "browseOnly": true,
        "childCandidateTypes": ["custom_targeting_value"],
        "supportsSearch": true
      },
      {
        "candidateType": "custom_targeting_value",
        "label": "Custom targeting value",
        "mappingKind": "custom_key_value",
        "directlyMappable": true,
        "parentCandidateType": "custom_targeting_key",
        "supportsParentFilter": true,
        "supportsSearch": true
      }
    ],
    "targetingSemantics": {
      "supportsComposed": true,
      "compositionModels": ["groups"],
      "supportedModes": ["include", "exclude"],
      "mappingKinds": [
        {
          "mappingKind": "gam_targeting_groups",
          "supportsComposed": true,
          "compositionModels": ["groups"],
          "exclusiveWithOtherSignals": true,
          "notes": "Grouped GAM targeting must be used alone for buyer targeting that consumes this field."
        }
      ]
    },
    "valueTypes": ["audience", "binary"]
  }
}
```

### Capability fields

| Field                            | Notes                                                                                                                                              |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `adapter`                        | Adapter implementation behind the ad server source, for example `google_ad_manager`                                                                |
| `supportsSignalMappingAuthoring` | `false` means the source can list signal components but cannot create drafts from this API                                                         |
| `mappingKinds`                   | Signal mapping kinds the adapter can create, such as `audience_segment`, `custom_key_value`, or `gam_targeting_groups`                             |
| `candidateTypes`                 | API types for browseable ad-server targeting. Types listed under `childCandidateTypes` can require `parent_id` when `supportsParentFilter` is true |
| `targetingSemantics`             | Composition behavior, include/exclude modes, buyer targeting fields, and warnings such as exclusivity                                              |
| `valueTypes`                     | Allowed `valueType` values for signal-component drafts                                                                                             |

## Browse ad-server targeting

Ad-server targeting records are source-native values that can be used to build
a signal-component draft. The endpoint returns them in a `candidates` array;
that is the wire name, not a separate operator-facing object. For hierarchical
API types, first browse the parent type and then pass its `externalId` as
`parent_id` when browsing the nested type.

```bash theme={null}
curl "https://api.interchange.io/api/v2/storefront/esa/123/signals/candidates?candidate_type=custom_targeting_key&q=DAYOFWEEK&limit=10" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
```

```bash theme={null}
curl "https://api.interchange.io/api/v2/storefront/esa/123/signals/candidates?candidate_type=custom_targeting_value&parent_id=17467172&q=FRIDAY&limit=10" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
```

```json theme={null}
{
  "data": {
    "candidates": [
      {
        "candidateType": "custom_targeting_value",
        "externalId": "451422266753",
        "name": "FRIDAY",
        "parentId": "17467172",
        "path": ["DAY OF WEEK", "FRIDAY"],
        "mappingKind": "custom_key_value",
        "defaultSignal": {
          "signalId": "dayofweek_friday",
          "name": "DAY OF WEEK: FRIDAY",
          "valueType": "binary",
          "adapterConfig": {
            "type": "passthrough",
            "kind": "custom_key_value",
            "keyId": "17467172",
            "valueId": "451422266753"
          }
        }
      }
    ],
    "count": 1,
    "nextCursor": null
  }
}
```

### Ad-server targeting query parameters

| Parameter        | Required | Notes                                                                                        |
| ---------------- | -------- | -------------------------------------------------------------------------------------------- |
| `candidate_type` | Yes      | API type from `candidateTypes[].candidateType`                                               |
| `q` or `query`   | No       | Adapter search string                                                                        |
| `parent_id`      | No       | Parent candidate `externalId`, required by candidate types with `supportsParentFilter: true` |
| `limit`          | No       | Page size                                                                                    |
| `cursor`         | No       | Cursor returned as `nextCursor`                                                              |

## Signal-component draft shape

Creation and validation use the same request body:

| Field           | Type           | Required | Notes                                      |
| --------------- | -------------- | -------- | ------------------------------------------ |
| `signalId`      | string         | Yes      | Stable caller-supplied signal identifier   |
| `name`          | string         | Yes      | Human-readable signal name                 |
| `description`   | string or null | No       | Operator-facing description                |
| `valueType`     | string         | Yes      | One of the adapter capability `valueTypes` |
| `tags`          | string\[]      | No       | Optional labels                            |
| `adapterConfig` | object         | Yes      | Adapter mapping configuration              |

## How signal components relate to products

Products describe the sellable inventory package. Signal components describe
targetable attributes that can be exposed to buyers or used in targeting
workflows. For GAM key-values, do not treat "not in the product selector
fields" as "not supported." Key-value signal targeting is supported when the
source capabilities advertise the relevant signal mapping kind.

Use `custom_key_value` for a direct key/value mapping and
`gam_targeting_groups` for composite GAM key-value logic. Then validate the
signal and use it according to the adapter's `targetingSemantics`. If
`targetingSemantics.mappingKinds[].exclusiveWithOtherSignals` is true for
`gam_targeting_groups`, use that grouped signal by itself for the buyer
targeting field it consumes.

Some large networks use GAM key-values to identify site lists or other
seller-defined groups inside their network. Those keys and values are
browseable as ad-server targeting and can be used to create signal components.
Publisher coverage still comes from
`adagents.json`. Buyer property-list references are forwarded and resolved
today; the seller's agent decides whether and how to map the requested subset to
products or ad-server controls. See
[Custom targeting and properties](/v2/storefront/esa/custom-targeting-properties).

### Audience segment

```json theme={null}
{
  "signalId": "auto_intenders",
  "name": "Auto intenders",
  "valueType": "audience",
  "adapterConfig": {
    "type": "passthrough",
    "kind": "audience_segment",
    "segmentId": "987654321"
  }
}
```

### Direct GAM key-value signal

Use a direct `custom_key_value` signal when the adapter exposes a specific
custom targeting value as directly mappable. Browse values with `parent_id` so
the value remains tied to the correct key.

```json theme={null}
{
  "signalId": "dayofweek_friday",
  "name": "DAY OF WEEK: FRIDAY",
  "valueType": "binary",
  "adapterConfig": {
    "type": "passthrough",
    "kind": "custom_key_value",
    "keyId": "17467172",
    "valueId": "451422266753"
  }
}
```

### Grouped GAM targeting

Use `gam_targeting_groups` for composite GAM logic. Groups are OR'd together;
criteria inside a group are AND'd together; multiple values inside one criterion
are OR'd by GAM for that key. A criterion with `exclude: true` excludes matching
values.

Grouped GAM targeting can be exclusive depending on the adapter semantics. If
`targetingSemantics.mappingKinds[].exclusiveWithOtherSignals` is true, do not
combine the grouped signal with another signal targeting the same buyer field.

```json theme={null}
{
  "signalId": "weekday_or_sports",
  "name": "Weekday or sports package",
  "valueType": "binary",
  "adapterConfig": {
    "type": "passthrough",
    "kind": "gam_targeting_groups",
    "groups": [
      {
        "criteria": [
          {
            "keyId": "17467172",
            "values": ["451422266753", "451420638792"]
          },
          {
            "keyId": "98765",
            "values": ["12345"],
            "exclude": true
          }
        ]
      },
      {
        "criteria": [
          {
            "keyId": "24680",
            "values": ["13579"]
          }
        ]
      }
    ]
  }
}
```

## Validate a component draft with `dry_run=true`

Use `POST /api/v2/storefront/esa/{esaId}/signals?dry_run=true` to validate and
preview a signal-component draft without persisting it. The request body is the same as
`POST /signals`, but the response is a validation result rather than a saved
signal object. Read the `valid` flag, not the HTTP status — a dry run returns
`200` even when the draft is invalid, and the `issues[]` array explains why. A
malformed request body still returns `422`.

```bash theme={null}
curl -X POST "https://api.interchange.io/api/v2/storefront/esa/123/signals?dry_run=true" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signalId": "weekday_or_sports",
    "name": "Weekday or sports package",
    "valueType": "binary",
    "adapterConfig": {
      "type": "passthrough",
      "kind": "gam_targeting_groups",
      "groups": [
        {
          "criteria": [
            {
              "keyId": "17467172",
              "values": ["451422266753"]
            }
          ]
        }
      ]
    }
  }'
```

```json theme={null}
{
  "data": {
    "valid": true,
    "issues": []
  }
}
```

A dry run does not persist the signal component. When the draft is valid the response also
includes a `preview` with the rendered wire signal and source projections. Use a dry
run before create when a client is assembling `adapterConfig` manually, or before
update to preview a change.

## Create a signal component

```bash theme={null}
curl -X POST https://api.interchange.io/api/v2/storefront/esa/123/signals \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signalId": "dayofweek_friday",
    "name": "DAY OF WEEK: FRIDAY",
    "valueType": "binary",
    "adapterConfig": {
      "type": "passthrough",
      "kind": "custom_key_value",
      "keyId": "17467172",
      "valueId": "451422266753"
    }
  }'
```

```json theme={null}
{
  "data": {
    "signalId": "dayofweek_friday",
    "name": "DAY OF WEEK: FRIDAY",
    "valueType": "binary",
    "adapterConfig": {
      "type": "passthrough",
      "kind": "custom_key_value",
      "keyId": "17467172",
      "valueId": "451422266753"
    }
  }
}
```

## List and get signal components

```bash theme={null}
curl https://api.interchange.io/api/v2/storefront/esa/123/signals \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
```

```bash theme={null}
curl https://api.interchange.io/api/v2/storefront/esa/123/signals/dayofweek_friday \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
```

Signal-component response objects include at least `signalId` and `name`; sources can
return additional fields such as `description`, `status`, `valueType`,
`adapterConfig`, `coverageForecast`, `createdAt`, and `updatedAt`.

## Errors

* `400 VALIDATION_ERROR` — invalid ad server source id, malformed draft, missing required fields, or a source validation issue.
* `401 UNAUTHORIZED` — missing or invalid bearer token.
* `404 NOT_FOUND` — ad server source or signal component does not exist for the caller.
* `502` or `503` — the ad server source or its upstream service could not complete the request.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Storefront signal-component tasks" href="/v2/storefront/signals/tasks" icon="list-check">
    Create and manage storefront signal components
  </Card>

  <Card title="Custom targeting and properties" href="/v2/storefront/esa/custom-targeting-properties" icon="sliders">
    How GAM key-values relate to site lists and publisher coverage
  </Card>

  <Card title="Signal overview" href="/v2/object-guides/signal" icon="wave-pulse">
    Signal model and lifecycle
  </Card>

  <Card title="Storefront API reference" href="/v2/storefront-api-reference" icon="code">
    OpenAPI reference for storefront routes
  </Card>
</CardGroup>
