Contents
QA test coverage for the three-endpoint Smart+ Web Conversions API
| Resource | Maximum |
|---|---|
| Ad groups per campaign | 30 |
| Creatives per ad (combined) | 50 |
| Carousel images | 35 |
| Ad texts | 5 |
| CTAs | 3 |
TikTok's Upgraded Smart+ Web Conversions API splits campaign creation into three sequential POST requests: campaign, ad group, and ad.1 The objective_type for all web conversion campaigns must be set to WEB_CONVERSIONS.1 This guide provides a structured set of QA test cases covering happy paths, boundary conditions, negative validations, field dependencies, migration regression, and idempotency for the v1.3 endpoint set.
The upgraded endpoints replace the legacy /campaign/spc/create/ single-call flow, which TikTok will sunset on 2026-03-31.2
Each call depends on the output of its predecessor.1
POST /v1.3/smart_plus/campaign/create/ accepts objective_type, budget_optimize_on, budget_mode, budget, and special_industries; it returns a campaign_id.1POST /v1.3/smart_plus/adgroup/create/ accepts campaign_id, promotion_type, optimization_goal, billing_event, bid_type, targeting, placement, scheduling, and budget fields; it returns an adgroup_id.1POST /v1.3/smart_plus/ad/create/ accepts adgroup_id, creative_list, ad_format, identity_type, ad_text_list, CTA fields, landing page fields, and brand safety fields; it returns a smart_plus_ad_id.1Create a campaign with budget_optimize_on: true, budget_mode: BUDGET_MODE_TOTAL, and a numeric budget.1 Create an ad group with promotion_type: WEBSITE, optimization_goal: CONVERT, billing_event: OCPM, bid_type: BID_TYPE_NO_BID, and targeting_optimization_mode: AUTOMATIC.1 Under AUTOMATIC mode, targeting_spec accepts Controls (guaranteed exclusions/inclusions) and Suggestions (optimization hints).1 Create an ad with ad_format: SINGLE_VIDEO, one video creative in creative_list, up to 5 items in ad_text_list, and landing_page_url_list populated.1 Verify all three calls return 200 with valid IDs.
Create a campaign with budget_optimize_on: false.1 Create an ad group with budget_mode: BUDGET_MODE_DAY, a daily budget, optimization_goal: CLICK, bid_type: BID_TYPE_CUSTOM, and bid_price set (not conversion_bid_price, because the goal is CLICK).1 Set targeting_optimization_mode: MANUAL and fully specify targeting_spec.1 Confirm the ad group returns a valid adgroup_id.
Create an ad with identity_type: AUTH_CODE.1 Spark Ads use authorized creator posts; verify the creative references an existing authorized post.3 Confirm that no non-Spark creatives are included in the same campaign, since mixing Spark and non-Spark in one campaign is prohibited.1
Create an ad with ad_format: CAROUSEL.1 Provide up to 35 images and 1 music track in the creative payload.1 Verify the response returns a valid smart_plus_ad_id.
| Test ID | Field | Limit | Input | Expected Result |
|---|---|---|---|---|
| BND-01 | Ad groups per campaign | 30 | Create exactly 30 ad groups | All 30 succeed.1 |
| BND-02 | creative_list items | 50 | 50 combined Video + Carousel + Post creatives | 200 OK.1 |
| BND-03 | Carousel images | 35 | 35 images + 1 music | 200 OK.1 |
| BND-04 | ad_text_list | 5 | 5 ad text entries | 200 OK.1 |
| BND-05 | call_to_action_list | 3 | 3 standard CTAs | 200 OK.1 |
With budget_optimize_on: true, create two ad groups where the first uses optimization_goal: CONVERT and the second uses optimization_goal: CLICK.1 When CBO is enabled, all ad groups must share identical values for promotion_type, optimization_goal, optimization_event, billing_event, bid_type, bid_price, conversion_bid_price, deep_bid_type, vbo_window, and roas_bid.1 The second ad group creation must return a validation error.
Set bid_type: BID_TYPE_CUSTOM on an ad group with budget_mode: BUDGET_MODE_TOTAL (lifetime budget).1 BID_TYPE_CUSTOM is only valid with daily budget, not lifetime.1 Expect a validation error.
Set optimization_goal: LANDING_PAGE and configure the event source using Events API only, with no Pixel installed.15 LANDING_PAGE optimization requires a Pixel event source.1 Expect a validation error indicating the event source is incompatible.
In one campaign, create an ad with identity_type: AUTH_CODE (Spark) and another with identity_type: CUSTOMIZED_USER (non-Spark).1 The API must reject the second ad because Spark and non-Spark ads cannot coexist in the same campaign.1
In the ad creation payload, populate both landing_page_url_list and page_list.1 These fields are mutually exclusive.1 Expect a validation error.
Provide both call_to_action_id (single CTA) and call_to_action_list (multiple CTAs) in the same ad request.1 These are mutually exclusive.1 Expect a validation error.
Set identity_type: BC_AUTH_TT and provide identity_id but omit identity_authorized_bc_id.1 BC_AUTH_TT requires both fields.1 Expect a validation error.
Set schedule_type: SCHEDULE_FROM_NOW on an ad group using budget_mode: BUDGET_MODE_TOTAL.1 SCHEDULE_FROM_NOW is only valid with daily budgets.1 Expect a validation error.
With budget_optimize_on: false, set min_budget on an ad group.1 The min_budget and max_budget fields are only valid when CBO is on, the ad group uses a daily budget, and bid_type is BID_TYPE_NO_BID.1 Expect a validation error.
After creating 30 ad groups under one campaign, attempt to create a 31st.1 The maximum is 30 ad groups per campaign.1 Expect a validation error.
Include 51 items in creative_list.1 The combined maximum across Video, Carousel, and Post types is 50.1 Expect a validation error.
In a CAROUSEL ad, include 36 images.1 The maximum is 35 images plus 1 music track.1 Expect a validation error.
Provide 6 entries in ad_text_list.1 The maximum is 5.1 Expect a validation error.
Provide 4 entries in call_to_action_list.1 The maximum is 3.1 Expect a validation error.
Set the viewability partner to IAS and the brand safety partner to Zefr.1 When either partner is IAS, both must be IAS and share the same VAST URL; when either is Zefr, both must be Zefr.1 Expect a validation error.
When budget_optimize_on is false, a separate uniformity rule applies: all ad groups under the campaign must share identical bid_type, deep_bid_type, vbo_window, and budget_mode.1 Test by creating two ad groups with differing budget_mode values (e.g., BUDGET_MODE_DAY and BUDGET_MODE_INFINITE) and confirming the second call is rejected.
The table below maps fields that become required or forbidden depending on the value of a controlling field.1
| Controlling Field | Value | Required Fields | Forbidden Fields |
|---|---|---|---|
| budget_optimize_on | true (CBO) | budget_mode: BUDGET_MODE_TOTAL or BUDGET_MODE_DYNAMIC_DAILY_BUDGET; budget | Ad group-level budget fields |
| budget_optimize_on | false | — | min_budget, max_budget (at ad group level) |
| budget_mode (non-CBO) | BUDGET_MODE_INFINITE | — | budget |
| budget_mode (non-CBO) | BUDGET_MODE_DAY or BUDGET_MODE_TOTAL | budget | — |
| bid_type | BID_TYPE_CUSTOM + CONVERT/LANDING_PAGE | conversion_bid_price | — |
| bid_type | BID_TYPE_CUSTOM + CLICK | bid_price | — |
| bid_type | BID_TYPE_CUSTOM | daily budget (not lifetime) | budget_mode: BUDGET_MODE_TOTAL |
| optimization_goal | LANDING_PAGE | Pixel event source | Events API-only source.5 |
| identity_type | BC_AUTH_TT | identity_id, identity_authorized_bc_id | — |
| identity_type | CUSTOMIZED_USER | — | Spark Ad creatives |
| schedule_type | SCHEDULE_FROM_NOW | daily budget | lifetime budget (BUDGET_MODE_TOTAL) |
| landing_page_url_list | populated | — | page_list |
| page_list | populated | — | landing_page_url_list, deeplink fields |
| call_to_action_id | populated | — | call_to_action_list |
| call_to_action_list | populated | — | call_to_action_id |
| CTA disabled | true | — | All destination fields |
| CBO on + daily budget + BID_TYPE_NO_BID | all true | — | (min_budget, max_budget become optional) |
| special_industries | populated | Advertiser in US or Canada | — |
The legacy single-call endpoint POST /v1.3/campaign/spc/create/ is scheduled for deprecation on 2026-03-31.2 After that date, any call to the old endpoint should return an HTTP error (expected 4xx) indicating the endpoint is no longer available.2 QA should verify this by sending a previously valid legacy payload after the sunset date and confirming the error response. Before the sunset date, verify both old and new endpoints function to confirm backward compatibility during the migration window.2
Send an identical campaign creation request with the same request_id twice in succession.1 The second call should return the same campaign_id without creating a duplicate campaign.1 Repeat this test for the ad group and ad endpoints to confirm idempotent behavior across all three calls.1 Test with a modified payload but the same request_id to verify the API returns the original response rather than processing the altered payload.
The placement_type field accepts PLACEMENT_TYPE_NORMAL for manual placement selection or can be omitted for automatic placement.1 When manual, the placements array accepts PLACEMENT_TIKTOK, PLACEMENT_PANGLE, and PLACEMENT_GLOBAL_APP_BUNDLE.1 Under automatic targeting (targeting_optimization_mode: AUTOMATIC), the targeting_spec object distinguishes between Controls (enforced constraints like geo or age) and Suggestions (non-binding signals the algorithm may use for optimization).1 Smart+ web campaigns support both modes, but TikTok recommends automatic targeting for broader reach.4
TikTok's Upgraded Smart+ Web Conversions API splits campaign creation into three sequential POST requests: campaign, ad group, and ad.1 The objective_type for all web conversion campaigns must be set to WEB_CONVERSIONS.1 This guide provides a structured set of QA test cases covering happy paths, boundary conditions, negative validations, field dependencies, migration regression, and idempotency for the v1.3 endpoint set.
| Resource | Maximum |
|---|---|
| Ad groups per campaign | 30 |
| Creatives per ad (combined) | 50 |
| Carousel images | 35 |
| Ad texts | 5 |
| CTAs | 3 |
The upgraded endpoints replace the legacy /campaign/spc/create/ single-call flow, which TikTok will sunset on 2026-03-31.2
Each call depends on the output of its predecessor.1
POST /v1.3/smart_plus/campaign/create/ accepts objective_type, budget_optimize_on, budget_mode, budget, and special_industries; it returns a campaign_id.1POST /v1.3/smart_plus/adgroup/create/ accepts campaign_id, promotion_type, optimization_goal, billing_event, bid_type, targeting, placement, scheduling, and budget fields; it returns an adgroup_id.1POST /v1.3/smart_plus/ad/create/ accepts adgroup_id, creative_list, ad_format, identity_type, ad_text_list, CTA fields, landing page fields, and brand safety fields; it returns a smart_plus_ad_id.1Create a campaign with budget_optimize_on: true, budget_mode: BUDGET_MODE_TOTAL, and a numeric budget.1 Create an ad group with promotion_type: WEBSITE, optimization_goal: CONVERT, billing_event: OCPM, bid_type: BID_TYPE_NO_BID, and targeting_optimization_mode: AUTOMATIC.1 Under AUTOMATIC mode, targeting_spec accepts Controls (guaranteed exclusions/inclusions) and Suggestions (optimization hints).1 Create an ad with ad_format: SINGLE_VIDEO, one video creative in creative_list, up to 5 items in ad_text_list, and landing_page_url_list populated.1 Verify all three calls return 200 with valid IDs.
Create a campaign with budget_optimize_on: false.1 Create an ad group with budget_mode: BUDGET_MODE_DAY, a daily budget, optimization_goal: CLICK, bid_type: BID_TYPE_CUSTOM, and bid_price set (not conversion_bid_price, because the goal is CLICK).1 Set targeting_optimization_mode: MANUAL and fully specify targeting_spec.1 Confirm the ad group returns a valid adgroup_id.
Create an ad with identity_type: AUTH_CODE.1 Spark Ads use authorized creator posts; verify the creative references an existing authorized post.3 Confirm that no non-Spark creatives are included in the same campaign, since mixing Spark and non-Spark in one campaign is prohibited.1
Create an ad with ad_format: CAROUSEL.1 Provide up to 35 images and 1 music track in the creative payload.1 Verify the response returns a valid smart_plus_ad_id.
| Test ID | Field | Limit | Input | Expected Result |
|---|---|---|---|---|
| BND-01 | Ad groups per campaign | 30 | Create exactly 30 ad groups | All 30 succeed.1 |
| BND-02 | creative_list items | 50 | 50 combined Video + Carousel + Post creatives | 200 OK.1 |
| BND-03 | Carousel images | 35 | 35 images + 1 music | 200 OK.1 |
| BND-04 | ad_text_list | 5 | 5 ad text entries | 200 OK.1 |
| BND-05 | call_to_action_list | 3 | 3 standard CTAs | 200 OK.1 |
With budget_optimize_on: true, create two ad groups where the first uses optimization_goal: CONVERT and the second uses optimization_goal: CLICK.1 When CBO is enabled, all ad groups must share identical values for promotion_type, optimization_goal, optimization_event, billing_event, bid_type, bid_price, conversion_bid_price, deep_bid_type, vbo_window, and roas_bid.1 The second ad group creation must return a validation error.
Set bid_type: BID_TYPE_CUSTOM on an ad group with budget_mode: BUDGET_MODE_TOTAL (lifetime budget).1 BID_TYPE_CUSTOM is only valid with daily budget, not lifetime.1 Expect a validation error.
Set optimization_goal: LANDING_PAGE and configure the event source using Events API only, with no Pixel installed.15 LANDING_PAGE optimization requires a Pixel event source.1 Expect a validation error indicating the event source is incompatible.
In one campaign, create an ad with identity_type: AUTH_CODE (Spark) and another with identity_type: CUSTOMIZED_USER (non-Spark).1 The API must reject the second ad because Spark and non-Spark ads cannot coexist in the same campaign.1
In the ad creation payload, populate both landing_page_url_list and page_list.1 These fields are mutually exclusive.1 Expect a validation error.
Provide both call_to_action_id (single CTA) and call_to_action_list (multiple CTAs) in the same ad request.1 These are mutually exclusive.1 Expect a validation error.
Set identity_type: BC_AUTH_TT and provide identity_id but omit identity_authorized_bc_id.1 BC_AUTH_TT requires both fields.1 Expect a validation error.
Set schedule_type: SCHEDULE_FROM_NOW on an ad group using budget_mode: BUDGET_MODE_TOTAL.1 SCHEDULE_FROM_NOW is only valid with daily budgets.1 Expect a validation error.
With budget_optimize_on: false, set min_budget on an ad group.1 The min_budget and max_budget fields are only valid when CBO is on, the ad group uses a daily budget, and bid_type is BID_TYPE_NO_BID.1 Expect a validation error.
After creating 30 ad groups under one campaign, attempt to create a 31st.1 The maximum is 30 ad groups per campaign.1 Expect a validation error.
Include 51 items in creative_list.1 The combined maximum across Video, Carousel, and Post types is 50.1 Expect a validation error.
In a CAROUSEL ad, include 36 images.1 The maximum is 35 images plus 1 music track.1 Expect a validation error.
Provide 6 entries in ad_text_list.1 The maximum is 5.1 Expect a validation error.
Provide 4 entries in call_to_action_list.1 The maximum is 3.1 Expect a validation error.
Set the viewability partner to IAS and the brand safety partner to Zefr.1 When either partner is IAS, both must be IAS and share the same VAST URL; when either is Zefr, both must be Zefr.1 Expect a validation error.
When budget_optimize_on is false, a separate uniformity rule applies: all ad groups under the campaign must share identical bid_type, deep_bid_type, vbo_window, and budget_mode.1 Test by creating two ad groups with differing budget_mode values (e.g., BUDGET_MODE_DAY and BUDGET_MODE_INFINITE) and confirming the second call is rejected.
The table below maps fields that become required or forbidden depending on the value of a controlling field.1
| Controlling Field | Value | Required Fields | Forbidden Fields |
|---|---|---|---|
| budget_optimize_on | true (CBO) | budget_mode: BUDGET_MODE_TOTAL or BUDGET_MODE_DYNAMIC_DAILY_BUDGET; budget | Ad group-level budget fields |
| budget_optimize_on | false | — | min_budget, max_budget (at ad group level) |
| budget_mode (non-CBO) | BUDGET_MODE_INFINITE | — | budget |
| budget_mode (non-CBO) | BUDGET_MODE_DAY or BUDGET_MODE_TOTAL | budget | — |
| bid_type | BID_TYPE_CUSTOM + CONVERT/LANDING_PAGE | conversion_bid_price | — |
| bid_type | BID_TYPE_CUSTOM + CLICK | bid_price | — |
| bid_type | BID_TYPE_CUSTOM | daily budget (not lifetime) | budget_mode: BUDGET_MODE_TOTAL |
| optimization_goal | LANDING_PAGE | Pixel event source | Events API-only source.5 |
| identity_type | BC_AUTH_TT | identity_id, identity_authorized_bc_id | — |
| identity_type | CUSTOMIZED_USER | — | Spark Ad creatives |
| schedule_type | SCHEDULE_FROM_NOW | daily budget | lifetime budget (BUDGET_MODE_TOTAL) |
| landing_page_url_list | populated | — | page_list |
| page_list | populated | — | landing_page_url_list, deeplink fields |
| call_to_action_id | populated | — | call_to_action_list |
| call_to_action_list | populated | — | call_to_action_id |
| CTA disabled | true | — | All destination fields |
| CBO on + daily budget + BID_TYPE_NO_BID | all true | — | (min_budget, max_budget become optional) |
| special_industries | populated | Advertiser in US or Canada | — |
The legacy single-call endpoint POST /v1.3/campaign/spc/create/ is scheduled for deprecation on 2026-03-31.2 After that date, any call to the old endpoint should return an HTTP error (expected 4xx) indicating the endpoint is no longer available.2 QA should verify this by sending a previously valid legacy payload after the sunset date and confirming the error response. Before the sunset date, verify both old and new endpoints function to confirm backward compatibility during the migration window.2
Send an identical campaign creation request with the same request_id twice in succession.1 The second call should return the same campaign_id without creating a duplicate campaign.1 Repeat this test for the ad group and ad endpoints to confirm idempotent behavior across all three calls.1 Test with a modified payload but the same request_id to verify the API returns the original response rather than processing the altered payload.
The placement_type field accepts PLACEMENT_TYPE_NORMAL for manual placement selection or can be omitted for automatic placement.1 When manual, the placements array accepts PLACEMENT_TIKTOK, PLACEMENT_PANGLE, and PLACEMENT_GLOBAL_APP_BUNDLE.1 Under automatic targeting (targeting_optimization_mode: AUTOMATIC), the targeting_spec object distinguishes between Controls (enforced constraints like geo or age) and Suggestions (non-binding signals the algorithm may use for optimization).1 Smart+ web campaigns support both modes, but TikTok recommends automatic targeting for broader reach.4