REST API · Actions
Create action
Prepare an action: what will change is answered, nothing changes until it is confirmed
POSThttps://api.apogee.ad/api/v1/actions
What it does
Meta types: pause_ad, pause_adset, activate_ad, activate_adset, update_budget, scale_budget, duplicate_ad, duplicate_adset, duplicate_campaign, create_adset, create_ad, update_adset_targeting, update_campaign_status, create_campaign, create_ad_from_post. TikTok / Snapchat types (with platform and that platform's ad account id): set_platform_status, set_platform_daily_budget. Budgets above the safety cap are refused. duplicate_ad: Keeps the SAME Facebook post (its likes, comments and shares): a new ad on its post id, else on its creative (Meta's /copies made a new post). Instagram starts a new post for each ad, as with a post id in Ads Manager. Only inside the same ad account (a copy into another ad account makes a new post); changing a text or the URL afterwards makes a new post too. The result lists each new ad id and the method used (post_id: shown as an existing post in Ads Manager; shared_creative: the same post shown as a new ad).
Request
POST /actions on https://api.apogee.ad/api/v1.
Headers
-
Authorizationstring header requiredBearer apk_…: an API key with the scope of this route. -
Idempotency-Keystring header requiredA unique key per request, e.g. a UUID, reused when you retry it: the first answer comes back instead of a second write (idempotency).
-
Content-Typestring header requiredapplication/json
Body
The body takes one of 17 shapes, chosen by its type:
type: "pause_ad"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredPause one or more ads. Use when user asks to pause, cut, stop, or kill specific ads. Always confirm with the user before executing.
Fields (2)
-
ad_idsarray of strings requiredArray of ad IDs to pause
-
ad_namesarray of strings requiredNames of the ads for confirmation display
-
type: "pause_adset"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredPause one or more ad sets. Use when user asks to pause, cut, stop, or kill specific ad sets. Preferred over pause_ad for 1-ad-per-adset structures. When user wants to pause/cut a creative, use this tool with the adset_id.
Fields (2)
-
adset_idsarray of strings requiredArray of ad set IDs to pause
-
adset_namesarray of strings requiredNames of the ad sets for confirmation display
-
type: "activate_ad"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredReactivate one or more paused ads to ACTIVE status. Use when user asks to reactivate, resume, restart, turn on specific ads.
Fields (2)
-
ad_idsarray of strings requiredArray of ad IDs to activate
-
ad_namesarray of strings optionalNames of the ads for confirmation display
-
type: "activate_adset"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredReactivate one or more paused ad sets to ACTIVE status. Use when user asks to reactivate, resume, restart, turn on specific ad sets.
Fields (2)
-
adset_idsarray of strings requiredArray of ad set IDs to activate
-
adset_namesarray of strings optionalNames of the ad sets for confirmation display
-
type: "update_budget"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredUpdate daily budget for one or more ad sets. Use when user asks to scale, increase, decrease, or change budget.
Fields (1)
-
updatesarray of objects requiredArray of budget updates
Fields of each item (4)
-
adset_idstring optional -
adset_namestring optional -
current_budgetnumber optionalCurrent daily budget in account currency
-
new_budgetnumber optionalNew daily budget in account currency
-
-
type: "scale_budget"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredScale budgets of ad sets by a percentage (e.g. +30% or -20%). Fetches current budgets, calculates new ones, and shows confirmation with before/after. Max +100% per operation, min 1€/day.
Fields (3)
-
adset_idsarray of strings requiredArray of ad set IDs to scale
-
percentagenumber requiredPercentage to scale by (e.g. 30 for +30%, -20 for -20%). Max +100, cannot go below 1€/day.
-
adset_namesarray of strings optionalNames of the ad sets
-
type: "duplicate_ad"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredDuplicate a winning ad into one or more target ad sets of the same ad account, on the SAME Facebook post: its likes, comments and shares stay (a new ad on its post id, else on its creative). Instagram starts a new post for each ad, as with a post id in Ads Manager. Use when user asks to duplicate, replicate, scale a winning creative to other campaigns. Keeps the SAME Facebook post (its likes, comments and shares): a new ad on its post id, else on its creative (Meta's /copies made a new post). Instagram starts a new post for each ad, as with a post id in Ads Manager. Only inside the same ad account (a copy into another ad account makes a new post); changing a text or the URL afterwards makes a new post too. The result lists each new ad id and the method used (post_id: shown as an existing post in Ads Manager; shared_creative: the same post shown as a new ad).
Fields (5)
-
source_ad_idstring requiredID of the ad to duplicate
-
source_ad_namestring requiredName of the source ad (for confirmation display)
-
target_adset_idsarray of strings requiredAd set IDs to duplicate into
-
target_adset_namesarray of strings requiredNames of target ad sets (for confirmation display)
-
rename_suffixstring optionalOptional suffix to append to duplicated ad names (e.g. " - Copy")
-
type: "duplicate_adset"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredDuplicate an ad set WITH its ads into the same campaign or a different one. Each copied ad keeps its post (likes, comments, shares keep adding up). The duplicate is created as PAUSED. Use when user asks to duplicate or copy an ad set.
Fields (4)
-
source_adset_idstring requiredID of the ad set to duplicate
-
target_campaign_idstring optionalOptional: campaign ID to duplicate into (defaults to same campaign)
-
new_namestring optionalOptional: added after the source's name ("<source name> - <new_name>"), e.g. a market or a version
-
new_budgetnumber optionalOptional: new daily budget in account currency
-
type: "duplicate_campaign"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredDuplicate a full campaign with all its ad sets and ads (archived and deleted ones left out). Each copied ad keeps its post. The duplicate is created as PAUSED. Use when user asks to duplicate or copy a campaign.
Fields (3)
-
source_campaign_idstring requiredID of the campaign to duplicate
-
new_namestring optionalOptional: added after the source's name ("<source name> - <new_name>"), e.g. a market or a version
-
status_optionstring optionalStatus for the new campaign (default PAUSED)
-
type: "create_adset"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredCreate a new ad set in an existing campaign. The ad set is created as PAUSED. Use when user asks to create a new ad set or audience. In a campaign budget (CBO) leave daily_budget out: the campaign's budget and bid strategy apply, each ad set gets its own bid_amount / roas_floor. In ABO, daily_budget is required. copy_from_adset_id copies the audience, optimization, conversion event, destination and attribution of an ad set of the same account (then targeting and optimization_goal can be left out).
Fields (12)
-
campaign_idstring requiredCampaign ID to create the ad set in
-
namestring requiredAd set name
-
copy_from_adset_idstring optionalOptional: an ad set of the same account to copy targeting, optimization_goal, billing_event, promoted_object, destination_type and attribution_spec from (its budget, bid and schedule are not copied)
-
daily_budgetnumber optionalABO only: daily budget in cents (e.g. 2000 = 20€). Leave it out in a CBO campaign
-
targetingobject optionalTargeting spec (geo_locations, age_min, age_max, genders, interests, custom_audiences, etc.). Required unless copy_from_adset_id
-
optimization_goalstring optionalOptimization goal (e.g. OFFSITE_CONVERSIONS, LINK_CLICKS, IMPRESSIONS). Required unless copy_from_adset_id
-
promoted_objectobject optionalConversion event, e.g. { pixel_id, custom_event_type: "PURCHASE" }. Required for OFFSITE_CONVERSIONS / VALUE / LEAD_GENERATION unless copied
-
billing_eventstring optionalBilling event (default IMPRESSIONS)
-
bid_strategystring optionalABO: this ad set's bid strategy (default LOWEST_COST_WITHOUT_CAP). CBO: the campaign's (leave it out)
-
bid_amountnumber optionalCost cap / bid cap in cents (e.g. 2500 = 25€): required with COST_CAP and LOWEST_COST_WITH_BID_CAP
-
roas_floornumber optionalMinimum ROAS strategy: the ROAS floor, e.g. 2.5 (optimization becomes VALUE)
-
start_timestring optionalOptional: start time in ISO format
-
type: "create_ad"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredCreate a new ad in an existing ad set. Use an object_story_id (page post ID) to preserve social proof (likes, comments, shares), or a creative_id to reuse an existing creative. The ad is created as PAUSED.
Fields (5)
-
adset_idstring requiredAd set ID to create the ad in
-
namestring requiredAd name
-
object_story_idstring optionalOptional: existing post ID (format pageId_postId) to preserve social proof
-
creative_idstring optionalOptional: existing creative ID to reuse (used if no object_story_id)
-
statusstring optionalAd status (default PAUSED)
-
type: "update_adset_targeting"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredUpdate the targeting of an existing ad set. Use when user asks to change audience, targeting, demographics, interests, or locations.
Fields (3)
-
adset_idstring requiredAd set ID to update
-
targetingobject requiredNew targeting spec (geo_locations, age_min, age_max, genders, flexible_spec, custom_audiences, etc.)
-
adset_namestring optionalAd set name for confirmation display
-
type: "update_campaign_status"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredChange the status of an entire campaign (ACTIVE or PAUSED). Use when user asks to pause or activate a whole campaign.
Fields (3)
-
campaign_idstring requiredCampaign ID
-
statusstring requiredNew status
-
campaign_namestring optionalCampaign name for confirmation display
-
type: "create_campaign"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredA new campaign (the app's Create Campaign), always created PAUSED. Scope launch.
Fields (5)
-
namestring required -
objectivestring required -
budget_typestring requiredabo = budget per ad set; cbo = campaign budget
-
budgetnumber optionalCBO only: daily campaign budget, in the account currency
-
bid_strategystring optionalCBO: the bid strategy shared by all ad sets; each new ad set then gets its own bid_amount / roas_floor (prepare_launch adset_variants). ABO: leave it out and use adset_settings.bid_strategy.
-
type: "create_ad_from_post"
-
ad_account_idstring requiredMeta ad account id (act_…)
-
typeconstant required -
inputobject requiredA new ad on an EXISTING post (Facebook or Instagram): its likes, comments and shares are kept. Advantage+ choices required, every other enhancement OFF.
Fields (11)
-
adset_idstring requiredThe ad set the new ad goes in (list_adsets)
-
namestring requiredThe new ad's name
-
advantage_plusobject requiredRequired: the Advantage+ creative enhancements, all explicit
Fields (1)
-
opt_inarray of strings requiredEnhancements turned ON; every other one is sent OFF. [] = all off
-
-
object_story_idstring optionalA Facebook post: <pageId>_<postId> (list_posts)
-
instagram_media_idstring optionalAn Instagram post (list_posts source instagram or ads)
-
instagram_user_idstring optionalInstagram post: the account that published it
-
page_idstring optionalInstagram post: the Facebook page linked to that account
-
call_to_actionstring optionalInstagram post only: e.g. SHOP_NOW, LEARN_MORE
-
website_urlstring optionalInstagram post only, with call_to_action
-
url_parametersstring optionalUTM parameters added to the post's link
-
statusstring optionalDefault PAUSED
-
type: "set_platform_status"
-
ad_account_idstring requiredThe ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID)
-
platformstring requiredtiktok or snapchat (Meta has its own reads and actions)
-
typeconstant required -
inputobject requiredTikTok / Snapchat: pause or activate campaigns, ad sets (TikTok ad groups, Snapchat ad squads) or ads of the ad account (platform and its own ad account id at the top). TikTok GMV Max and legacy Smart+ campaigns, and the ads TikTok generates inside Smart+ ad groups, are refused.
Fields (3)
-
levelstring requiredcampaign, adset (a TikTok ad group / a Snapchat ad squad) or ad
-
idsarray of strings required1 to 50 campaigns, ad sets or ads of that level
-
statusstring requiredpaused (stops delivering) or active (delivers again)
-
type: "set_platform_daily_budget"
-
ad_account_idstring requiredThe ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID)
-
platformstring requiredtiktok or snapchat (Meta has its own reads and actions)
-
typeconstant required -
inputobject requiredTikTok / Snapchat: a campaign's or an ad set's new daily budget, in the ad account's currency. Refused above the safety cap (converted to the account's currency), on a lifetime budget, on an ad set whose campaign holds the budget, and on TikTok Smart+ budgets.
Fields (3)
-
levelstring requiredcampaign (its campaign budget) or adset (a TikTok ad group / a Snapchat ad squad)
-
idstring requiredThe campaign or ad set (get_platform_performance shows its daily_budget)
-
daily_budgetnumber requiredThe new daily budget, in the ad account's currency (not cents or micro)
-
Response
201.
-
idstring -
statusstring -
typestring or null -
platformstring -
ad_account_idstring or null -
will_dostring or null -
created_atstring or null -
expires_atstring (date-time) -
executed_atstring or null
Every answer carries X-Request-Id (give it to support), Apogee-Version: 2026-10-06, and the RateLimit and RateLimit-Policy headers; a replayed answer also carries Idempotent-Replayed: true.
Errors
Errors are application/problem+json with a stable code to switch on (all the codes).
| Status | Possible codes |
|---|---|
| 400 Bad request | idempotency_key_required, not_supported, request_refused, validation_failed |
| 401 Unauthorized | invalid_api_key |
| 403 Forbidden | account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required |
| 404 Not found | not_found |
| 409 Conflict | action_already_ran, idempotency_in_progress, plan_already_launched |
| 412 Precondition failed | drive_not_connected, meta_not_connected, platform_not_connected |
| 422 Unprocessable content | budget_above_cap, idempotency_key_reused, launch_refused, missing_answers |
| 429 Too many requests | daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited |
| 500 Internal server error | internal_error |
| 502 Bad gateway | drive_error, launch_failed, meta_error, platform_error, upstream_error |
Example
With your key in APOGEE_API_KEY (create one):
curl -X POST 'https://api.apogee.ad/api/v1/actions' \
-H "Authorization: Bearer $APOGEE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"ad_account_id": "act_1234567890",
"type": "pause_ad",
"input": {
"ad_ids": [
"120200000000000003"
],
"ad_names": [
"Nordlicht | Spring launch"
]
}
}' {
"id": "120200000000000001",
"type": "120200000000000001",
"platform": "meta",
"ad_account_id": "act_1234567890",
"status": "prepared",
"will_do": "Pause 1 ad in act_1234567890: Nordlicht | Spring launch.",
"created_at": "2026-10-23T09:00:00.000Z",
"expires_at": "2026-10-23T09:00:00.000Z",
"executed_at": "2026-10-23T09:00:00.000Z"
} Access and cost
| Plans | Pro and Scale. |
|---|---|
| Scope | actions (launch for create_campaign) (scopes). |
| Credits | Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read. A request that fails costs nothing, a replayed one neither (credits). |
| Rate limit | Write: 120 per 15 minutes on Pro, 240 on Scale (rate limits). |
| Meta budget | Reads Meta: spends 1 unit of the account's Meta budget (1,800 per 15 minutes, 900 per ad account); with a TikTok or Snapchat platform, that platform's own budget instead. |
| Daily cap | Counts 2 units toward the 2,000 units one API key can use in 24 hours. |
In the MCP
An AI assistant does the same with these tools of the MCP server:
create_campaign: Prepares a new campaign, always created paused: name, objective, budget per ad set (ABO) or per campaign (CBO, with its bid strategy).execute_actioncreates it after your OK.create_ad_from_post: A new ad on an existing Facebook or Instagram post, which keeps its likes and comments, with every Advantage+ enhancement explicit; paused by default.pause_ad: Pauses ads.pause_adset: Pauses ad sets.update_budget: Sets new daily budgets for ad sets.duplicate_ad: Copies an ad into other ad sets of the same ad account, keeping the same post (likes, comments, shares).activate_ad: Turns paused ads back on.activate_adset: Turns paused ad sets back on.duplicate_adset: Copies an ad set with its ads, each keeping its post; created paused.duplicate_campaign: Copies a campaign with its ad sets and ads; created paused.create_adset: A new ad set in an existing campaign, from scratch or copying another ad set's targeting and conversion; created paused.create_ad: A new ad on an existing post or creative; created paused.update_adset_targeting: Changes an ad set's targeting.update_campaign_status: Pauses or turns on a whole campaign.scale_budget: Scales ad set budgets by a percentage (+100% at most per change).set_platform_status: Prepares pausing or turning on TikTok or Snapchat campaigns, ad sets or ads;execute_actionruns it after your OK. TikTok GMV Max and legacy Smart+ campaigns, and the ads TikTok generates inside Smart+ ad groups, are changed in TikTok Ads Manager.set_platform_daily_budget: Prepares a new daily budget for a TikTok or Snapchat campaign or ad set, in the ad account's currency;execute_actionruns it after your OK. Refused above the safety cap, on a lifetime budget, on an ad set whose campaign holds the budget, and on TikTok Smart+ budgets.
Related operations
- Get action:
GET /actions/{id} - Confirm action:
POST /actions/{id}/confirm