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

# Create the Integration Campaign and Component Model

> Create the campaign, root, and model component with its display metadata, logo, typed sockets, and configuration view.

Part of the [partner integration guide](/technical/building-partner-integrations/integration-lifecycle/management-api-integration).

# Overview

This page builds the container every integration needs before any business event, webhook, or view attaches.

## When You Would Build One

Every build path starts here; [Integration Categories](/technical/building-partner-integrations/integration-types/integration-categories) describes what each adds.

| You want                                                              | This page gives you                           |
| --------------------------------------------------------------------- | --------------------------------------------- |
| A partner integration                                                 | Campaign, root, model component               |
| A tile on the [Integrations page](https://my.extole.com/integrations) | Eight display settings and a resolving `logo` |
| A configuration tab                                                   | The `config-view-v10.0` child                 |

## How to Build

### Create the Integration Campaign

Create an `INTEGRATION` campaign with the `integration` program type. Its tag names the integration component this campaign carries, as published integration bundles do; the component tags added next are what list it on the Integrations page.

```bash theme={null}
curl --request POST "$EXTOLE_API_HOST/v2/campaigns" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Example Integration",
    "description": "Receives partner lifecycle events and maps them to Extole business events.",
    "campaign_type": "INTEGRATION",
    "program_type": "integration",
    "tags": ["internal:integration-component-name:example"]
  }'
```

Record the identifier and version. Creation adds a default `PROGRAM` label equal to the campaign identifier; replace it when the contract defines one:

```bash theme={null}
curl --request POST "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/labels" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "example-integration",
    "type": "PROGRAM"
  }'
```

One active `PROGRAM` label per campaign; creating one replaces the previous.

### Create the Component Model

Create a root component following the Custom Integration Template conventions. Leave `internal:type:integration` off it; the Integrations page excludes roots.

```bash theme={null}
curl --request POST \
  "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/version/$CAMPAIGN_VERSION/components" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "root",
    "description": "Declares campaign-level variables and cross-campaign inheritance.",
    "tags": ["internal:self-managed"]
  }'
```

Record `ROOT_COMPONENT_ID`, [refresh the campaign version](/technical/building-partner-integrations/integration-lifecycle/management-api-integration#refresh-the-campaign-version-between-mutations), and create the model component with all eight display settings in one request:

```bash theme={null}
curl --request POST \
  "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/version/$CAMPAIGN_VERSION/components" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "example",
    "display_name": "Example",
    "description": "Receives Example lifecycle events.",
    "types": [
      "integration-v10.0"
    ],
    "component_ids": [
      "'"$ROOT_COMPONENT_ID"'"
    ],
    "tags": [
      "internal:type:integration",
      "internal:self-managed"
    ],
    "variables": [
      {
        "name": "short.description",
        "type": "STRING",
        "values": {
          "default": "Connect Example lifecycle events to Extole."
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "about",
        "type": "STRING",
        "values": {
          "default": "This integration receives Example events and maps them to Extole business events."
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "documentation.url",
        "type": "STRING",
        "values": {
          "default": "https://docs.extole.com/docs/example"
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "external.url",
        "type": "STRING",
        "values": {
          "default": "PARTNER_PRODUCT_SITE"
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "external.integration.url",
        "type": "STRING",
        "values": {
          "default": "PARTNER_MARKETPLACE_LISTING"
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "categories",
        "type": "STRING",
        "values": {
          "default": "eCommerce Platform"
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "logo",
        "type": "IMAGE",
        "values": {
          "default": "ASSET_HOST_LOGO_URL"
        },
        "tags": [
          "internal:ui-display"
        ]
      },
      {
        "name": "imageKey",
        "type": "STRING",
        "values": {
          "default": "example"
        },
        "tags": [
          "internal:ui-display"
        ]
      }
    ]
  }'
```

Use `variables`; `CampaignComponentCreateRequest` has no `settings` property. Replace `PARTNER_PRODUCT_SITE`, `PARTNER_MARKETPLACE_LISTING`, and `ASSET_HOST_LOGO_URL` with absolute URLs. `integration-v10.0` is the long-standing revision, later ones such as `integration-v10.1` exist, so keep an installed source's own type.

### Set Integration Display Metadata

The type checks only that all eight are present, not that their values are usable. Tag every one `internal:ui-display`, exactly that string — the tag marks tile metadata and hides the setting from the settings list.

* `short.description` — one tile sentence.
* `about` — what the integration receives and sends.
* `documentation.url` — the partner-facing page for this integration, not this guide.
* `external.url` — the partner's product site.
* `external.integration.url` — the partner's marketplace or extension listing, or an empty string.
* `categories` — one `STRING` category other integrations already use, such as `eCommerce Platform`; the admin groups by exact value, so copy it from a working integration.
* `logo` — the `IMAGE` the Integrations page renders, covered below.
* `imageKey` — the key the platform resolves to its own stored partner image, read by the older partner detail view rather than the tile; take it from the partner page.

### Set a Logo That Resolves

The Integrations page renders the **built** value of `logo`:

```bash theme={null}
curl --request GET \
  "$EXTOLE_API_HOST/v1/components/built?having_all_tags=internal:type:integration&limit=50" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN"
```

A resolved logo is an absolute URL on Extole's asset host, such as an `originAssetId` path:

```json theme={null}
{
  "name": "logo",
  "type": "IMAGE",
  "values": { "default": "ASSET_HOST_LOGO_URL" }
}
```

* **A registered component for this partner exists** — copy its built `logo` URL from `/v1/components/built`, never `/v1/components`, whose source listing returns an unresolved expression naming an asset your component does not own.
* **No registered component has it** — take an absolute URL from the partner's site: the `og:image` meta tag, the header logo `<img>`, or a brand-assets download link. Prefer PNG or SVG over a sprite or background image.
* **You have the file** — upload it as an asset, then set `logo` to `spel@buildtime:context.getAsset('example').getUrl()`.
* **The partner has neither** — track `logo` as outstanding until the real file arrives.

Confirm an address answers with the image before storing it. To upload, send multipart form data with the metadata in an `asset` part and the bytes in a `file` part:

```bash theme={null}
curl --request POST \
  "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/components/$INTEGRATION_COMPONENT_ID/assets" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --form 'asset={"name":"example","tags":[],"description":"Example Logo"};type=application/json' \
  --form "file=@example.png;type=image/png"
```

The expression builds into a hosted URL on the account's asset domain. A reward integration repeats this with `reward-supplier-logo`, exposed as `rewardSupplierLogo`, on every supplier template.

`logo` is `IMAGE` and required, so `"default": ""` is rejected:

```json theme={null}
{
  "code": "settings_build_failed",
  "parameters": { "errors": { "logo": {
    "code": "variable_value_invalid_type",
    "expected_types": ["IMAGE"],
    "details": "The value: is not valid for the type:IMAGE"
  } } }
}
```

### Add the Partner Settings

Add partner configuration settings separately, each with a display name, description, type, default, `importance:basic`, and a priority, prefixed with the integration component name — `exampleAccountUrl`, `exampleSetupInstructions`.

<Warning>
  Never tag a partner configuration setting `internal:ui-display`, and never place a credential value in a setting the configuration view displays.
</Warning>

Setup instructions carry the event endpoint, the current program label, the event names, the payload fields, the credential rule, and the documentation link, which [Send Events from the Partner Platform](/technical/building-partner-integrations/integration-types/partner-events-send-from-platform) tells implementers to read from this view. Compute them at build time, substituting the campaign's program label for `PROGRAM_LABEL`:

```json theme={null}
{
  "name": "exampleSetupInstructions",
  "type": "STRING",
  "display_name": "Example Extension Setup",
  "description": "Connection details for the server-side Example extension.",
  "tags": ["category:configuration", "importance:basic"],
  "priority": "20",
  "values": {
    "default": "javascript@buildtime:(function(){ return \"Extole event endpoint: POST /v6/events — see Send Events from the Partner Platform\\nProgram label (send as data.labels): PROGRAM_LABEL\\nEvent names: example_order_created, example_order_shipped, example_order_canceled\\nUse a server-side access token from the Security Center. Do not use a token that can manage campaigns in the partner application.\"; })()"
  }
}
```

<Note>
  The value is interpolated at creation, so replacing the campaign's `PROGRAM` label means updating this setting too.
</Note>

### Add Typed Sockets

Add a `businessEvents` multi-socket:

```bash theme={null}
curl --request POST \
  "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/version/$CAMPAIGN_VERSION/components/$INTEGRATION_COMPONENT_ID/settings" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "businessEvents",
    "display_name": "Business Events",
    "description": "Reusable business events produced from partner input events.",
    "type": "MULTI_SOCKET",
    "filters": [
      {
        "type": "COMPONENT_TYPE",
        "component_type": "business-event-v10.0"
      }
    ]
  }'
```

Add a `views` multi-socket:

```bash theme={null}
curl --request POST \
  "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/version/$CAMPAIGN_VERSION/components/$INTEGRATION_COMPONENT_ID/settings" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "views",
    "display_name": "Views",
    "description": "Views rendered in the integration UI.",
    "type": "MULTI_SOCKET",
    "filters": [
      {
        "type": "COMPONENT_TYPE",
        "component_type": "view-v10.0"
      }
    ]
  }'
```

Declare a type on every socket and attach only components that satisfy it. `view-v10.0` is the parent of every view type, so one filter accepts the configuration view below plus the `report-runner-view-v10.0` and `event-stream-view-v10.0` children from [Add the Activity and Event Views](/technical/building-partner-integrations/integration-lifecycle/integration-activity-views).

### Add a Configuration View

Create a `config-view-v10.0` child attached to the integration model component:

```bash theme={null}
curl --request POST \
  "$EXTOLE_API_HOST/v2/campaigns/$CAMPAIGN_ID/version/$CAMPAIGN_VERSION/components" \
  --header "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "configuration",
    "display_name": "Configuration",
    "description": "Configuration view for the Example integration.",
    "types": [
      "config-view-v10.0"
    ],
    "installed_into_socket": "views",
    "component_ids": [
      "'"$INTEGRATION_COMPONENT_ID"'"
    ],
    "variables": [
      {
        "name": "order",
        "type": "INTEGER",
        "values": {
          "default": 1
        }
      },
      {
        "name": "title",
        "type": "STRING",
        "values": {
          "default": "Configuration"
        }
      },
      {
        "name": "status",
        "type": "STRING",
        "values": {
          "default": "javascript@buildtime:context.getComponent().getParent().getVariableValue(\"exampleAccountUrl\") ? \"READY\" : \"IN_PROGRESS\""
        }
      },
      {
        "name": "settingsToDisplay",
        "type": "STRING_LIST",
        "values": {
          "default": [
            "exampleAccountUrl",
            "exampleSetupInstructions"
          ]
        }
      }
    ]
  }'
```

`component_ids` takes the parent model component, which owns the `views` socket, and `settingsToDisplay` names parent settings only, including `exampleSetupInstructions`. Set `status` from a buildtime expression so the tab reports `READY` only once the settings Extole can verify are complete.

## How to Test

Read the built component with `GET /v1/components/built?having_all_tags=internal:type:integration` and confirm `logo` resolved to an absolute URL on Extole's asset host, every `settingsToDisplay` name appears with its display name, and the component sits under the root with `internal:self-managed` and both sockets filled.

### Troubleshooting

| Symptom                                                                             | Cause                                                                                                                       | Fix                                            |
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `settings_build_failed`, `variable_value_invalid_type`, `expected_types: ["IMAGE"]` | `logo` sent empty                                                                                                           | A resolvable URL or buildtime asset expression |
| Create rejected                                                                     | Only some of the eight display settings sent; no later mutation adds them                                                   | Send all eight in one create                   |
| Grey Extole placeholder                                                             | Built `logo` is an expression, or its URL 404s, redirects, or returns HTML — as Extole documentation images and favicons do | Read `/v1/components/built` and replace it     |
| Display settings show beside the partner's                                          | A tag other than `internal:ui-display`                                                                                      | Retag all eight                                |
| A `settingsToDisplay` name never appears                                            | That setting carries `internal:ui-display`                                                                                  | Remove the tag                                 |
| A `settingsToDisplay` name fails validation                                         | No parent setting has it                                                                                                    | Create it or drop the name                     |
| The integration groups nowhere, or alone                                            | `categories` sent as `STRING_LIST`, or a new spelling                                                                       | One existing category, typed `STRING`          |
| The view installs into the root, which lacks `views`                                | `component_ids` omitted                                                                                                     | Recreate with the parent component             |
| Configuration tab stuck in progress                                                 | `status` hardcoded                                                                                                          | Use a buildtime expression                     |
