Build a sales channel

Channel types and what each can do, the channel name merchants see, publishing products, product feeds and orders.

A sales channel app sends a store's products to another place where they are sold or advertised, such as a marketplace, a social shop or an ads catalog, and, for some channel types, brings the orders placed there back into the store. Installing one adds a sales channel to the store: merchants see it in their navigation, choose which products are published to it, and see its orders and feedback in Store Admin.

Choose the channel type#

Create the app with the app type Sales Channel. Its subcategory sets the channel type, and neither can change later; see Create an app. The channel type decides which capabilities the app may switch on:

Capability

What it lets the app do

Marketplace

Social commerce

Advertising

Store API scopes it needs

Order sync (syncs_orders)

Create the orders placed on the channel in the store.

Yes

Yes

No: shoppers buy on the store itself

read_orders, write_orders

External IDs (external_ids)

Give each product and variant the ID the channel knows it by. See Product identities.

Yes

Yes

Yes

read_products, write_products

Product feed (product_feed)

Have Tringify generate and host an XML product feed the channel fetches.

Yes

Yes

Yes

read_products

Tracking (tracking)

Send shopper and order events to the channel for attribution, through event delivery.

No

Yes

Yes

read_products

Customer matching (customer_matching)

Receive the consented, privacy-safe customer match field on server events, for advertising measurement.

No

Yes

Yes

read_customers

Tringify's own channels, such as the online store, are built in and can't be created by developers. The Developer Portal lists the capabilities each channel type allows, and saving an app refuses a capability its type doesn't allow (APP_CAPABILITY_NOT_ALLOWED_BY_CHANNEL_TYPE) or a capability without the Store API scopes it needs (APP_UPDATE_REQUIRED_SCOPE_MISSING). A new sales channel app starts with no capabilities; switch on at least one before you submit it for review (APP_SUBMIT_CHANNEL_CAPABILITY_REQUIRED).

Name the channel#

The channel name is what merchants see for the channel in their navigation and on the product form, for example "Example Ads". Leave it empty to use the app's name. It follows the same rules as the app name: invisible characters and text-direction overrides are refused, and so is a reserved name such as Tringify, also in look-alike letters (see App name); the form shows the length limit. Only sales channel apps have a channel name (APP_UPDATE_CHANNEL_NAME_TYPE_MISMATCH). Like the rest of the configuration, a change reaches stores with the next release.

Declare it in tringify.app.json#

The sales_channel section of the app configuration file holds the channel type, name, capabilities, identity declarations and feed specification. tringify app config push saves the whole section in one step or refuses it with the first problem it finds.

json
{
  "sales_channel": {
    "channel_type": "advertising",
    "channel_name": "Example Ads",
    "enabled_capabilities": [
      "external_ids",
      "product_feed"
    ],
    "identity_declarations": [
      {
        "key": "example_product_id",
        "label": "Example product ID",
        "role": "catalog_item",
        "owner_type": "product",
        "allowed_sources": [
          "canonical_id",
          "sku",
          "generated"
        ],
        "required": true
      },
      {
        "key": "example_variant_id",
        "label": "Example variant ID",
        "role": "catalog_item",
        "owner_type": "variant",
        "allowed_sources": [
          "canonical_id",
          "sku",
          "generated"
        ],
        "required": true
      },
      {
        "key": "example_group_id",
        "label": "Example item group ID",
        "role": "catalog_group",
        "owner_type": "product",
        "allowed_sources": [
          "canonical_id",
          "generated"
        ],
        "required": true
      }
    ],
    "feed_spec": {
      "formats": [
        "xml"
      ],
      "variant_handling": "expand",
      "identity": {
        "simple": {
          "id_field": "id"
        },
        "variable": {
          "id_field": "id",
          "group_id_field": "item_group_id"
        }
      },
      "fields": [
        {
          "name": "id",
          "type": "string",
          "required": true,
          "label": "Item ID"
        },
        {
          "name": "item_group_id",
          "type": "string",
          "label": "Item group ID"
        },
        {
          "name": "title",
          "type": "string",
          "required": true,
          "label": "Title",
          "merchant_mapping": {
            "sources": [
              {
                "source": "product_field",
                "allowed_field_refs": [
                  "product.title"
                ]
              },
              {
                "source": "metafield",
                "allowed_owner_types": [
                  "product"
                ]
              }
            ]
          }
        },
        {
          "name": "link",
          "type": "url",
          "required": true,
          "label": "Product page"
        },
        {
          "name": "image_link",
          "type": "url",
          "required": true,
          "label": "Main image"
        },
        {
          "name": "price",
          "type": "price",
          "required": true,
          "label": "Price"
        },
        {
          "name": "availability",
          "type": "string",
          "required": true,
          "label": "Availability"
        }
      ],
      "default_mappings": {
        "id": {
          "source": "channel_identity",
          "config": {
            "simple_declaration_key": "example_product_id",
            "variant_declaration_key": "example_variant_id"
          }
        },
        "item_group_id": {
          "source": "channel_identity",
          "config": {
            "group_declaration_key": "example_group_id"
          }
        },
        "title": {
          "source": "product_field",
          "config": {
            "field": "product.title"
          }
        },
        "link": {
          "source": "product_url"
        },
        "image_link": {
          "source": "primary_image"
        },
        "price": {
          "source": "regular_price"
        },
        "availability": {
          "source": "inventory_status"
        }
      }
    }
  }
}

Publish products#

Merchants choose which products are on the channel with the publication controls on the product form and the bulk actions on the product list. Your app doesn't keep its own list of included products.

  • Subscribe to product_publications/create, product_publications/update and product_publications/delete (they need read_products). They are delivered only to the app that owns the channel, and each carries the publication's stable ID and version, so retries and out-of-order deliveries are easy to spot.

  • After downtime or a missed webhook, page through GET /channel-publications: it is the complete current list.

  • Report what happened to each publication version with feedback: synced, or action_required and rejected with a stable uppercase code and a message the merchant reads in Store Admin. Feedback never publishes or unpublishes a product.

Product feeds#

With the product feed capability, Tringify builds the feed for you from the feed specification in your release: which fields the feed has, which are required, where each field's value comes from by default, and which fields a merchant may map to another source. Feeds are XML. variant_handling is expand (one row per variant, which needs a variant ID field and an item group ID field), parent_only or configurable. Fields without a merchant_mapping block keep the mapping your release sets.

Mapping sources include product and variant fields (product_field, variant_field with a field such as product.title), channel_identity, product_url, primary_image, additional_images, regular_price, sale_price, inventory_status, inventory_quantity, shipping_weight, category_path, metafield and static_value. A field that takes its value from channel_identity must name a declared identity of the right kind: the simple-product ID a product item, the variant ID a variant item, and the item group ID a product group. Saving refuses a mapping that names an unknown field or identity, so a typo is never a blank column in a live feed.

Once installed, your app reads the store's selling targets with GET /channel-feed/targets: one per country, language and currency the store really sells in. Create a feed unit for each target you need, choose its schedule and generate it. Tringify serves the latest generation at the unit's public_url (https://{store domain}/feeds/{unit id}.xml); give that URL to the channel. Diagnostics and jobs show what was left out and why. See Product feeds.

Bring orders back#

With order sync, create each order placed on the channel through POST /orders with your app token. Send the channel's own order number as external_order_id and an Idempotency-Key, so a retry never creates a second order; reusing either for different order details is refused (ORDER_IDEMPOTENCY_CONFLICT). When the channel sets the shipping charge, send shipping_override with the method name and amount instead of choosing a store shipping option. The order records your channel, and merchants can filter their orders by it.

Install and uninstall#

Installing the app creates its channel in the same step, with the channel type and name from the installed release. Uninstalling removes the channel; products are no longer published to it. Delete what you hold for the store when you receive app.uninstalled, as for any app; see Install lifecycle.