Marketplaces / TikTok Marketplace Integration v2 / TikTok - Product management / TikTok Promotions

TikTok Promotions

Version Date Created / Updated Notes
v1.0 25/02/2025 Hristiyan Georgiev Initial version
v1.1 03/04/2025 Hristiyan Georgiev Added description for some fields
v1.2 09/04/2025 Hristiyan Georgiev Added two more cases for get activities

TikTok Shop provides a variety of promotion activities to enhance the shopping experience for users, all of which are seller-funded. The Promotion APIs allow sellers to manage two key activities: Product Discount and Flash Deal, each designed to increase product visibility and encourage purchases. Product Discount offers two types—fixed price or percentage off—displayed across various shopping touchpoints, including product pages and livestreams. Flash Deals offer time-limited discounts, often highlighting products with a countdown timer, and can only feature products priced the same or lower than their previous 30-day prices. There is a third activity called Coupons or seller vouchers which can also be applied alongside other promotional campaigns, however creating them via API is not supported. Thus we will focus into the creating of the two main promotions - product discount and flash deal.

Below you can find UML diagram to hopefully explain the workflows. Please note that the calls are not synchronous and don’t follow any specific order. This is just purely for (hopefully) understanding how the promotions work as they could seem a bit complicated.

We aim to develop a comprehensive promotions functionality, which includes downloading existing promotions, creating, updating, or deactivating promotions, and adding or removing products from these promotions. Each of these actions on a promotion has its own dedicated API call.

DO NOT READ the below strike-through text.

We want to develop the TikTok promotions using the Promotion functionality in MC Pro. This means we should be able to control all flows through this functionality so it needs to be incorporated. We want to use the Promotions wizard (info on the Wizard can be found here - Hemisphere Promotion Manager Technical Scope) as well :

  1. [Channel] We start with creating the promotion by selecting the marketplace (TikTok)
  2. [Type and account] Then we need to select the Promotion type, account and seller. For type options please see the TikTok Promotions table below
  3. [Promotion Setup] We then create the promotion setup with all the fields as per the TikTok Promotions table below
  4. [Items] We then select the listings to include in the promotion. At the moment we are only able to add listings via file import. If the Promotion type is = DIRECT_DISCOUNT, we only need the SKU of the product in the import file, as the other info will be filled in the promotion setup. If the Promotion type = FIXED_PRICE or FLASHSALE, we need to have the SKU, Discounted value, Quantity limit and Quantity limit per buyer information in the import file. (more info and templates below)
  5. [Preview] Once items are selected we then show a preview of the promotion. Once we click the Finish button we should send a request to create the promotion (based on a flag trigger) and the imported file should create/update records in the Listings TikTok table (explanation and mapping below)

For this to work, we need a new table called TikTok Promotion and it should be visible in the Promotion menu in Mc Pro. In this table we will need the following fields :

Field Name Type Required Notes
Account Dropdown Yes This is a dropdown with all available accounts for distinct marketplace TikTok
Type of promotion Radio button Yes Possible values are:
  • FIXED_PRICE - We display as “Fixed Price” -DIRECT_DISCOUNT - We display as “Direct Discount”
  • FLASHSALE - We display as “Flashsale”. <v1.1>We want to also have a description on this field stating “Please note that this field cannot be updated on an existing promotion”</v1.1> | | External ID | varchar | No | This should be filled by the integration once we have created the promotion or once we have stored a promotion via the Search Activities call. Mapping is provided in the respective calls. This should be readonly | | Action | radio button | Yes | We should have three options : Create, Update and Deactivate. | | Action status | radio button | Yes | This will act as trigger, so we want to have the standard radio buttons (Pending, Sent, Completed and Error) | | Error message | varchar | No | This field will store the external error if any. Mapping is provided in the respective calls. | | Start time | date with time | Yes | We want this field to be of a date type and with a calendar, just like any other date field in MC Pro | | End time | date with time | Yes | We want this field to be of a date type and with a calendar, just like any other date field in MC Pro | | Created time | date | No | This should be filled by the integration once we have created/updated/ the promotion or once we have stored a promotion via the Search Activities call.Mapping is provided in the respective calls. Should be readonly. | | Product level | radio button | Yes | Whether the activity covers products or SKUs. Possible enumerations are:
  • PRODUCT - We display this as “Variation Group Level - Selecting this, you will be able to add promotions on the whole variation group.”
  • VARIATION - We display this as “SKU Level - Selecting this, you will be able to add promotions on individual SKUs within the product”. <v1.1>We want to also have a description on this field stating “Please note that this field cannot be updated on an existing promotion”</v1.1> | | External status | varchar | No | This should be filled by the integration once we have created/updated/deactivated the promotion or once we have stored a promotion via the Search Activities call. Mapping is provided in the respective calls. Should be readonly. | | Title | varchar | Yes | This is the title of the activity. | | Updated time | varchar | No | This should be filled by the integration once we have created the promotion or once we have stored a promotion via the Search Activities call. Mapping is provided in the respective calls. Should be readonly. |

We also need a few new fields in the Listings TikTok table under a new tab called Promotions . Those fields will be from where we basically control the products in promotion.

Listings TikTok Field Field type Required Listings TikTok Field notes
Promotion title enum table No This should be an enum table of TikTok Promotion displaying the Title but linked to the MC Pro ID. In other words, users should be able to import the MC Pro ID but we display the Title of the promotion. We should have internal validation and if we are sending a promotion and this field is empty, we return internal error in Listings TikTok > Action error.
Promotion info join field from TikTok Promotions No This field should display info from TikTok Promotion > Promotion level and TikTok Promotion > Type of promotion to the product. It should be concatinated from both values. Example : “Variation Group Level - Fixed Price
Discount value varchar No This field should be filled from the TikTok Promotion > Discount Value OR should be available for items import.
Description of the field : “This field stores the discount value based on the promotion type: • For Direct Discount, it contains the percentage off the product price. • For Fixed Price or Flash Sale, it contains the monetary amount the product will be sold for”.We should have internal validation and if we are sending a promotion and this field is empty, we return internal error in Listings TikTok > Action error. Quantity limit integer No Field should be available for items import. We need to have a logic and if the field is empty we always send “-1” as value. We need another logic : -If our Product level is = VARIATION we send -1 as value in the products object of the payload. And we pick the value from this field for the skus object. (mapping is provided in the Add Products to Activity section below) -If our Product_level is = PRODUCT, this field should be available to be filled. Description of the field - “The quantity limit of the products involved in the promotion. The range is between 1 - 99. Leave empty for no quantity limit”. We want to have internal validation for the limit and return internal error if less than 1 or more than 99 is input.
Quantity limit per buyer integer No Field should be available for items import. We need to have a logic and if the field is empty we always send “-1” as value.

We need another logic : -If our Product level is = VARIATION we send -1 as value in the products object of the payload. And we pick the value from this field for the skus object (mapping is provided in the Add Products to Activity section below) -If our Prodcut_level is = PRODUCT, this field should be available to be filled. Description of the field - “The quantity limit of the products involved in the promotion. The range is between 1 - 99. Leave empty for no quantity limit”. We want to have internal validation for the limit and return internal error if less than 1 or more than 99 is input. | | Action | radio button | No | This field will act as a trigger for adding/updating/removing the item(s) into the selected promotion. Values are : Add, Update, Remove. It should be available for items import. | | Action status | radio button | No | This will act as trigger, so we want to have the standard radio buttons (Pending, Sent, Completed and Error) | | Action error | varchar | No | This will act as a field where we store any errors that occurred when updating |

We also need to create a new system custom table view called “TikTok Promotion Products” . This custom table should be visible in the Promotion menu in Mc Pro. It should contain the Product >SKU, Listings >Title , Listings > Price and all the new fields from the Promotions tab in the Listings TikTok table.

We should allow users to add/remove products to promotions via standard import functionality so we should allow all the fields from the Promotions tab in Listings TikTok to be imported via standard Items import.

With every call, we need to pass the shop_cipher as query property which is not visible in the UI but kept in account_tiktok > shop_cipher

Search Activities

Using this call we can query and store all the existing activities on a particular TikTok account.

API Call : POST /promotion/202309/activities/search

API Docs : https://partner.tiktokshop.com/docv2/page/650acfbaf1fd3102b9315a3a?external_id=650acfbaf1fd3102b9315a3a#Back To Top

We need to pass a body with our request, and the body needs to contain only the status of the promotion, which should be hardcoded to “ONGOING” as we don’t want to download any expired or deactivated promotions.

Example cURL request :

curl -X POST \
 'https://open-api.tiktokglobalshop.com/promotion/202309/activities/search?app_key=29a39d&sign=bc721f0e0182914e3487b81df204de37a352fc3aa96947efda6dc1e5dd0d5290&timestamp=1623812664&shop_cipher=GCP_XF90igAAAABh00qsWgtvOiGFNqyubMt3' \
-H 'x-tts-access-token: TTP_pwSm2AAAAABmmtFz1xlyKMnwg74T2GJ5s0uQbS8jPjb_GkdFVCxPqzQXSyuyfXdQa0AqyDsea2tYFNVf4XeqgZHFfPyv0Vs659QqyLYfsGzanZ5XZAin3_ZkcIxxS0_In6u6XDeU96k' \
-H 'content-type: application/json' \
-d '{
  "status": "ONGOING"
}'

Example response :

{
    "code": 0,
    "data": {
        "activities": [
            {
                "activity_type": "FIXED_PRICE",
                "begin_time": 1739456031,
                "create_time": 1739536355176,
                "end_time": 1741875229,
                "id": "7471251228950071072",
                "product_level": "PRODUCT",
                "status": "ONGOING",
                "title": "Bai_Ivan_Promotion",
                "update_time": 1739536355176
            }
        ],
        "total_count": 1
    },
    "message": "Success",
    "request_id": "202502141232458CC57AF79C5A42081CA3"
}

When storing a brand new promotion, we want to create a new record in TikTok Promotions table and store the info of the promotion.

Mapping :

TikTok Field Mc Pro Field Notes
code
data
activities
activity_type TikTok Promotion > Type of promotion Possible values are:
  • FIXED_PRICE -DIRECT_DISCOUNT
  • FLASHSALE | | | | begin_time | TikTok Promotion > Start time | We will receive unix timestamp but want to display human readable format | | | | create_time | TikTok Promotion > Created time | We will receive unix timestamp but want to display human readable format | | | | end_time | TikTok Promotion > End time | We will receive unix timestamp but want to display human readable format | | | | id | TikTok Promotion > External ID | | | | | product_level | TikTok Promotion > Product level | | | | | status | TikTok Promotion > Status | | | | | title | TikTok Promotion > Title | | | | | update_time | TikTok Promotion > Updated time | We will receive unix timestamp but want to display human readable format | | | total_count | | | | | message | | | | | | request_id | | | | |

Once we have stored the activity, we want to query the products that are in that activity and store the info in Hemi. This is done with the Get Activity call.

Get Activity

API Call: GET /promotion/202309/activities/{activity_id} API Docs: https://partner.tiktokshop.com/docv2/page/650acd920fcef602bf36ee2b?external_id=650acd920fcef602bf36ee2b

Please note that we want to overwrite any existing information in the Promotions tab in Listings TikTok, as the info we get from the response will be the most up to date.

We also want to put a condition and only call promotions that DO NOT have TikTok Promotion > Status = DEACTIVATED OR NOT_EFFECTIVE.

<v1.2> Example Response (there are four different responses that we can receive depending on the product_level and activity_type </v1.2> :

//product_level = VARIATION and activity_type = DIRECT_DISCOUNT
{
    "code": 0,
    "data": {
        "activity_id": "7475302437151115040",
        "activity_type": "DIRECT_DISCOUNT",
        "begin_time": 1740479608,
        "create_time": 1740479621054,
        "end_time": 1742898807,
        "product_level": "VARIATION",
        "products": [
            {
                "activity_price": {
                    "currency": "GBP"
                },
                "id": "1729422230267661771",
                "quantity_limit": -1,
                "quantity_per_user": -1,
                "skus": [
                    {
                        "activity_price": {
                            "currency": "GBP"
                        },
                        "discount": "33",
                        "id": "1729422232232693195",
                        "quantity_limit": -1,
                        "quantity_per_user": -1
                    }
                ]
            },
            {
                "activity_price": {
                    "currency": "GBP"
                },
                "id": "1729401093096574411",
                "quantity_limit": -1,
                "quantity_per_user": -1,
                "skus": [
                    {
                        "activity_price": {
                            "currency": "GBP"
                        },
                        "discount": "15",
                        "id": "1729427972856516043",
                        "quantity_limit": -1,
                        "quantity_per_user": -1
                    },
                    {
                        "activity_price": {
                            "currency": "GBP"
                        },
                        "discount": "25",
                        "id": "1729427972856384971",
                        "quantity_limit": -1,
                        "quantity_per_user": -1
                    },
                    {
                        "activity_price": {
                            "currency": "GBP"
                        },
                        "discount": "10",
                        "id": "1729427972856450507",
                        "quantity_limit": -1,
                        "quantity_per_user": -1
                    }
                ]
            }
        ],
        "status": "ONGOING",
        "title": "DirektenDebitDIscountAiMo",
        "update_time": 1740480384610
    },
    "message": "Success",
    "request_id": "202502251049595CD5120E7F634C04F392"
}
//product_level = PRODUCT and activity_type = DIRECT_DISCOUNT
{
    "code": 0,
    "data": {
        "activity_id": "7475307457720796961",
        "activity_type": "DIRECT_DISCOUNT",
        "begin_time": 1740479608,
        "create_time": 1740480973665,
        "end_time": 1742898807,
        "product_level": "PRODUCT",
        "products": [
            {
                "activity_price": {
                    "currency": "GBP"
                },
                "discount": "15",
                "id": "1729446813639018955",
                "quantity_limit": -1,
                "quantity_per_user": -1
            },
            {
                "activity_price": {
                    "currency": "GBP"
                },
                "discount": "10",
                "id": "1729422231650799051",
                "quantity_limit": -1,
                "quantity_per_user": -1
            }
        ],
        "status": "ONGOING",
        "title": "DirectDiscountProduct",
        "update_time": 1740481009815
    },
    "message": "Success",
    "request_id": "20250225110142F1517C77B93DF2062946"
}

<v1.2>

//product_level = PRODUCT and activity_type = FIXED_PRICE/FLASHSALE
{
  "code": 0,
  "data": {
      "activity_id": "7490492011347298070",
      "activity_type": "FIXED_PRICE",
      "begin_time": 1744016880,
      "create_time": 1744016496341,
      "end_time": 1746522060,
      "product_level": "PRODUCT",
      "products": [
          {
              "activity_price": {
                  "amount": "48",
                  "currency": "GBP"
              },
              "id": "1729461614103531454",
              "quantity_limit": -1,
              "quantity_per_user": -1
          },
          {
              "activity_price": {
                  "amount": "20",
                  "currency": "GBP"
              },
              "id": "1729456335479934910",
              "quantity_limit": -1,
              "quantity_per_user": -1
          }
      ],
      "status": "ONGOING",
      "title": "MSS EXTENSION",
      "update_time": 1744198087881
  },
  "message": "Success",
  "request_id": "20250409114251D1B7BC16EB97B4177D1D"
}
// product_level = VARIATION and activity_type = FIXED_PRICE/FLASHSALE
{
    "code": 0,
    "data": {
        "activity_id": "7491280011954194198",
        "activity_type": "FIXED_PRICE",
        "begin_time": 1744146000,
        "create_time": 1744199935193,
        "end_time": 1746392400,
        "product_level": "VARIATION",
        "products": [
            {
                "activity_price": {
                    "currency": "GBP"
                },
                "id": "1729479244015311307",
                "quantity_limit": -1,
                "quantity_per_user": -1,
                "skus": [
                    {
                        "activity_price": {
                            "amount": "429",
                            "currency": "GBP"
                        },
                        "id": "1729479251421075915",
                        "quantity_limit": 5,
                        "quantity_per_user": 5
                    }
                ]
            }
        ],
        "status": "ONGOING",
        "title": "Product Level and Variation TEST TEST",
        "update_time": 1744200037019
    },
    "message": "Success",
    "request_id": "2025040912021585B800024F2281196C44"
}

</v1.2>

Response Mapping :

TikTok FIeld Mc Pro Field Notes
code N/A
data
activity_id <v1.2> We need to map the activity_id with TikTok Promotion > External ID to find the TikTok Promotion > ID (our internal ID) and then fill it in Listing TikTok > Promotion title</v1.2>
activity_type TikTok Promotion > Type of promotion We want to overwrite this if we have it filled already.
begin_time TikTok Promotion > Start Time We want to overwrite this if we have it filled already.
create_time N/A
end_time TikTok Promotion > End Time We want to overwrite this if we have it filled already.
product_level TikTok Promotion > Product level We want to overwrite this if we have it filled already.
products
activity_price
amount <v1.2.>Listings TikTok > Discount Value We will either have amount or discount depending on the type of promotion.
This will not be present if product_level = VARIATION </v1.2.>
currency N/A
discount Listings TikTok > Discount Value <v1.2> We will either have amount or discount depending on the type of promotion.
This will not be present if product_level = VARIATION </v1.2.>
id We only want to look at this field if we have product_level = PRODUCT.
We use it to map with Listings > Channel Item ID to find out which product we need to fill the info to. Please note that we can have a single Channel Item ID for many listings in Mc Pro and we need to import the info across all of them.
quantity_limit Listings TikTok >Quantity limit If we have Product level = PRODUCT, we will receive a value of “-1”. In this case don’t want to store it and keep the field empty.
If we have Product level = VARIATION, we ignore this field in the products object.
quantity_per_user Listings TikTok > Quantity limit per buyer If we have Product level = PRODUCT, we will receive a value of “-1”. In this case don’t want to store it and keep the field empty.
If we have Product level = VARIATION, we ignore this field in the products object.
skus We will only receive this object if we have Product level = VARIATION
activity_price
amount <v1.2>Listings TikTok > Discount Value We will either have amount or discount depending on the type of promotion. </v1.2>
currency N/A
discount Listings TikTok > Discount Value <v1.2>We will either have amount or discount depending on the type of promotion.</v1.2>
id This will be our Listings TikTok > SKU ID which we need to map in order to understand to which listing(s) we need to import the info.
quantity_limit Listings TikTok >Quantity limit We might receive a value of “-1”. In this case don’t want to store it and keep the field empty.
quantity_per_user Listings TikTok > Quantity limit per buyer We might receive a value of “-1”. In this case don’t want to store it and keep the field empty.
status TikTok Promotion > External Status We want to overwrite this if we have it filled already.
title N/A
update_time TikTok Promotion > Updated Time We want to overwrite this if we have it filled already.
message N/A
request_id N/A

Example Error response :

{
    "code": 17029009,
    "data": null,
    "message": "Promotion ID does not exist: 747343120735156816",
    "request_id": "2025022511204684998403BA403E05BA3C"
}

Incase there was an error, we will receive a code different than 0. In this case we want to store the message into TikTok Promotion > Error Message

Update Activity

Using this call we are able to update a promotion activity.

API Call: PUT /promotion/202309/activities/{activity_id} API Docs: https://partner.tiktokshop.com/docv2/page/650c584d82c3a602befa4ab8?external_id=650c584d82c3a602befa4ab8#Back To Top

activity_id needs to be taken from TikTok Promotion > External ID so for update we only need to pick promotions that have External ID not empty. The trigger for this call will be TikTok Promotion > Action = Update and TikTok Promotion > Action Status = Pending.

Example request body:

{
  "activity_type": "DIRECT_DISCOUNT",
  "begin_time": 1739456031,
  "end_time": 1740390034,
  "product_level": "PRODUCT",
  "title": "Updated Activity_bratched"
}

Request Mapping :

TikTok Field Mc Pro Field Notes
activity_type TikTok Promotion > Type of promotion
begin_time TikTok Promotion > Start time Needs to be in unix timestamp. We should put a validation and if begin time is empty we return an internal error.
end_time TikTok Promotion > End time Needs to be in unix timestamp. We want to have a validation and it should not be possible to put a date/time earlier than the start date/time. Also we should return an internal error if end time is empty.
title TikTok Promotion > Title This is required field so we need to have validation and if empty to return internal error.
product_level TikTok Promotion > Product level

Example success response :

{
  "code": 0,
  "data": {
    "activity_id": "7136104329798256386",
    "title": "BlackFridayDiscount",
    "update_time": 1661756811
  },
  "message": "Success",
  "request_id": "202203070749000101890810281E8C70B7"
}

Response mapping :

TikTok Field Mc Pro Field Notes
code N/A
data
activity_id N/A
title N/A
update_time TikTok Promotion > Updated time
message N/A
request_id N/A

Example error response :

{
    "code": 17029007,
    "data": null,
    "message": "Promotion period must not exceed 30 days. Current period length in seconds: 2836984",
    "request_id": "20250217101911E154A8E8CBAC1903AE26"
}

Incase there was an error, we will receive a code different than 0. In this case we want to mark the TikTok Promotion > Action Status = Error and store the message into TikTok Promotion > Error Message

Create Activity

Using this call we create a brand new activity with all the details. The creation of the activity does not include products, but rather the promotion itself.

API Call : POST /promotion/202309/activities API Docs : https://partner.tiktokshop.com/docv2/page/650c33c155bc3202b762b507

The trigger for this call will be TikTok Promotion > Action = Create and TikTok Promotion > Action Status = Pending.

Example call body :

{
  "activity_type": "FIXED_PRICE",
  "begin_time": 1739456031,
  "end_time": 1741875229,
  "product_level": "PRODUCT",
  "title": "Na_Ivan_Activity-to2"
}

Request mapping :

TikTik Field Mc Pro Field Notes
activity_type TikTok Promotion > Type of promotion
begin_time TikTok Promotion > Start time
end_time TikTok Promotion > End time
product_level TikTok Promotion > Product level
title TikTok Promotion > Title

Example success response :

{
    "code": 0,
    "data": {
        "activity_id": "7472747558339643168",
        "create_time": 1739885388,
        "status": "ONGOING",
        "update_time": 1739885388
    },
    "message": "Success",
    "request_id": "202502181329473FC0C68F3A5DF701B2C6"
}

Response mapping :

TikTok Field Mc Pro Field Notes
code N/A
data activity_id TikTok Promotion > External ID
create_time TikTok Promotion > Created time
status TikTok Promotion > External Status
update_time N/A We don’t want to map this field when creating a new activity.
message
request_id

Example error response :

{
    "code": 17029006,
    "data": null,
    "message": "Promotion period must be longer than 10 minutes. Current period length in seconds: -11",
    "request_id": "20250218134657BF45099AC4E6E0025A3C"
}

Incase there was an error, we will receive a code different than 0. In this case we want to mark the TikTok Promotion > Action Status = Error and store the message into TikTok Promotion > Error Message

Deactivate Activity

Using this call we are able to deactivate an ongoing or upcoming promotion. Once a

promotion is deactivated it cannot be activated again.

API Call: POST /promotion/202309/activities/{activity_id}/deactivate API Docs: https://partner.tiktokshop.com/docv2/page/650acf9adefece02be7380cf

activity_id needs to be taken from TikTok Promotion > External ID so for update we only need to pick promotions that have External ID not empty. The trigger for this call will be TikTok Promotion > Action = Deactivate and TikTok Promotion > Action Status = Pending.

There is no information to be sent into the call body, we need to submit it as empty body {}.

Example success response :

{
    "code": 0,
    "data": {
        "activity_id": "7473436014611187489",
        "status": "DEACTIVATED",
        "title": "Variation Activity_DIRECT_DISCOUNT",
        "update_time": 1740406261
    },
    "message": "Success",
    "request_id": "2025022414110017F502714961740B0A2A"
}

Mapping :

TikTok Field Mc Pro Field Notes
code N/A
data
activity_id N/A
status TikTok Promotion > External Status
title N/A
update_time TikTok Promotion > Update Time
message N/A
request_id N/A

Example error response :

{
    "code": 17029010,
    "data": null,
    "message": "The promotion has been deactivated. Promotion ID: 7473436014611187489",
    "request_id": "20250224142049681956321985520AE222"
}

Incase there was an error, we will receive a code different than 0. In this case we want to mark the TikTok Promotion > Action Status = Error and store the message into TikTok Promotion > Error Message

Add Products to Activity

Once we have created/updated the promotion, we need to assign products to this activity. There is a limit of up to 300 products per API call so we must abide to that. As we know, TikTok considers the variation a “product” and then each individual SKU inside this variation has a separate SKU id. So in MC Pro terms we can include up to 300 SKUs per call.

A single product can be assigned to a single promotion at a time, so we don’t want to have any validations and if the user updates the promotion id , we don’t want to block anything. We just send the product into the new promotion.

API Call : PUT /promotion/202309/activities/{activity_id}/products API Docs : https://partner.tiktokshop.com/docv2/page/650d32c42aaa3602b86ccb5c

The activity_id is picked from the TikTok Promotion > External ID .

The triggers for this call will be Listings TikTok > Action = Add and Listings TikTok > Action Status = Pending

With the following validations :

Listings > Closed = No

Listings > Protect Price = No

The information that we send for the products and their discounts is different based on the different types of promotions so we need to create logic and handle that properly. Below sample payloads with explanations in the notes in the mapping section.

Example call body :

//for promotion with product_level = PRODUCT and activity_type = DIRECT DISCOUNT
{
  "activity_id": "7472747558339643168",
  "products": [
    {      
      "discount": "15",
      "id": "1729428722337484235",
      "quantity_limit": 10,
      "quantity_per_user": 2      
    }
  ]
}
//for promotion with product_level = PRODUCT and activity_type = FIXED_PRICE or FLASHSALE
{
  "activity_id": "7472745957399234336",
  "products": [
    {      
      "activity_price_amount": "160",
      "id": "1729428656127512011",
      "quantity_limit": 10,
      "quantity_per_user": 2      
    }
  ]
}
//for promotion with product_level = VARIATION and activity_type = DIRECT_DISCOUNT 
{
  "activity_id": "7473436014611187489",
  "products": [
    {     

      "id": "1729428656127512011",
      "quantity_limit": -1,
      "quantity_per_user": -1,
      "skus": [
        {
          "discount": "15",          
          "id": "1729428657912974795",
          "quantity_limit": -1,
          "quantity_per_user": 5
        },
        {
          "discount": "20",          
          "id": "1729428657912843723",
          "quantity_limit": -1,
          "quantity_per_user": 50
        }
      ]     
    }

  ]
}
//for promotion with product_level = VARIATION and activity_type = FIXED_PRICE or FLASHSALE
{
  "activity_id": "7473431207351568161",
  "products": [
    {     

      "id": "1729428656127512011",
      "quantity_limit": -1,
      "quantity_per_user": -1,
      "skus": [
        {
          "activity_price_amount": "150",          
          "id": "1729428657912974795",
          "quantity_limit": 5,
          "quantity_per_user": 4
        },
        {
          "activity_price_amount": "160",          
          "id": "1729428657912843723",
          "quantity_limit": 30,
          "quantity_per_user": 10
        }
      ]     
    }

  ]
}

Mapping (please note we are providing the full mapping but which payload we use will strictly depend on the product level and activity type)

TikTok Field Mc Pro Field Notes
activity_id TikTok Promotion > External ID
products
activity_price_amount Listigns TikTok> Discount Value We use this only if we have TikTok Promotion > Type of promotion = FIXED_PRICE or FLASHSALE and TikTok Promotion >Product level = PRODUCT . Otherwise we exclude it from the payload.
discount Listigns TikTok> Discount Value We use this only if we have TikTok Promotion > Type of promotion = DIRECT_DISCOUNT and TikTok Promotion > Product level = PRODUCT. Otherwise we exclude it from the payload.
id Listings > Channel Item ID
quantity_limit Listings TikTok > Quantity limit We have logic for this field explained in the beginning of the document
quantity_per_user Listings TikTok > Quantity limit per user We have logic for this field explained in the beginning of the document
skus We include data in this object only if we have Listings TikTok > Promotion product level = VARIATION , otherwise we exclude the whole object from the payload.
activity_price_amount Listigns TikTok> Discount Value We have logic for this field explained in the beginning of the document
discount Listigns TikTok> Discount Value We have logic for this field explained in the beginning of the document
id Listings TikTok > SKU ID
quantity_limit Listings TikTok > Quantity limit We have logic for this field explained in the beginning of the document
quantity_per_user Listings TikTok > Quantity limit per user We have logic for this field explained in the beginning of the document

Example success response :

{
    "code": 0,
    "data": {
        "activity_id": "7472745957399234336",
        "status": "ONGOING",
        "title": "Na_Ivan_Activity-to2",
        "total_count": 1,
        "update_time": 1740397291
    },
    "message": "Success",
    "request_id": "2025022411413074BDDE5BE089A805A6C7"
}

Example error response :

{
    "code": 17029062,
    "data": null,
    "message": "Discount is below the limit, please confirm.",
    "request_id": "20250224114550C3D93AADE0F9D0053D4A"
}

From the success response we don’t want to map anything. We just want to put the flag Listings TikTok > Action Status = Completed as the product(s) were successfully added to the promotion.

Incase there was an error, we will receive a code different than 0. In this case we want to store the message into TikTok Listings > Action error and set Listings TikTok > Action Status = Error

Remove Products from Activity

Using this call we can remove products or SKUs from an existing activity. Again we have a limit of max 300 products or skus to remove with a single API call, so we need to handle this and if we have more than 300 products or skus we need to split them across couple of calls.

API Call: DEL /promotion/202309/activities/{activity_id}/products API Docs: https://partner.tiktokshop.com/docv2/page/650acfd84a0bb702c072b4eb

The activity_id is picked from the TikTok Promotion > External ID .

The triggers for this call will be Listings TikTok > Action = Remove and Listings TikTok > Action Status = Pending

With the following validations :

Listings > Closed = No

Listings > Protect Price = No

Example call body :

//when removing the whole product (i.e the whole variation group) from the promotion
{  
  "product_ids": [
    "1729428722337484235"
  ]
}
//when removing only certain SKUs out of the whole product (variation)
{  
  "sku_ids": [
    "1729428764672888267",
    "1729428764672953803"
  ]
}

Mapping :

TikTok Field Hemi Field Notes
product_ids Listings > Channel Item ID We need to have a logic and only use this if the Listings TikTok >Promotion product level = PRODUCT .

We also need a logic and if we have Listings TikTok >Promotion product level = VARIATION, but all skus in the variation have been selected for activity removal, we still want to use the product_id which will be one for the whole variation. Otherwise we exlcude it from the payload. | | sku_ids | Listings TikTok > SKU ID | We only use this if we have Listings TikTok >Promotion product level = VARIATION and we are removing only part of the SKUs in a variation. Otherwise we exclude it from the paylaod. |

Example success response :

{
    "code": 0,
    "data": {
        "activity_id": "7473436014611187489",
        "status": "ONGOING",
        "update_time": 1740403465
    },
    "message": "Success",
    "request_id": "2025022413242490DA1967BA39A6095A04"
}

Example error response :

{
    "code": 17029024,
    "data": null,
    "message": "SKU(s) not found in this promotion. Product IDs: 1729428764672888267,1729428764672953803",
    "request_id": "2025022413242991C641CE9CE90A090DEE"
}

When we have a success response we want to set Listings TikTok > Action Status = Completed and erase all promotion related info from the Listings TikTok which includes External ID, Promotion product level, Type of promotion, Discount Value, Quantity limit, Quantity limit per buyer and keep theAction flag on Remove.

Incase there was an error, we will receive a code different than 0. In this case we want to store the message into TikTok Listings > Action Error and set Listings TikTok > Action Status = Error. We want to keep the Listings TikTok > Action = Remove in this case.

Is this article helpful?
0 0 0