Skip to content
AdCrunch
Esc
↑↓navigate↵open⌘Jpreview
On this page

Make a change

Change what runs on an advertiser with the Mutations API — start a Mutation, poll it until it ends, and read the history of each change.

A change to a live advertiser is asynchronous. A provider write is slow, and it can fail halfway. So the API never does the provider write inside your request. It records your request as a Mutation, and it answers the id of that Mutation. The change runs after the answer. You poll the Mutation until the change ends.

Not every provider accepts a change. See What each provider supports.

Start a Mutation

Send the advertiser and one action to POST /mutations. This request pauses a campaign:

curl -X POST https://api.adcrunch.dev/mutations \
  -H "Authorization: Bearer $ADCRUNCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "advertiserId": "acc_1203456789012345",
    "action": {
      "kind": "meta_set_status",
      "level": "campaign",
      "id": "120210000000000",
      "status": "PAUSED"
    }
  }'

The answer is the id of the Mutation, and not the result of the change:

{ "workflowId": "b3d1f0c4-6a2e-4a1f-9f77-2c0d1e5a8b94" }

Before AdCrunch starts the change, it checks four things: the body, the owner of the advertiser, a usable connection, and write access. A request that fails a check starts nothing. It also adds no row to the history.

Status error Cause
400 invalid_request The body does not match the schema.
403 missing_write_access The connection of the advertiser has no write access.
404 not_found The advertiser does not exist, or it belongs to a different organization.
409 provider_not_connected The advertiser has no usable connection: a person removed it, it expired, or the provider refused it. A person must connect it again.

Poll until it ends

Read the Mutation with its id:

curl https://api.adcrunch.dev/mutations/b3d1f0c4-6a2e-4a1f-9f77-2c0d1e5a8b94 \
  -H "Authorization: Bearer $ADCRUNCH_API_KEY"

status has one of three values:

status Meaning
running The change has not ended. Wait a few seconds, then poll again.
complete AdCrunch reached the provider and has its answer. Read result to learn what that answer was.
errored The change ran into a failure that AdCrunch did not expect. It cannot say whether the provider applied the change, so read the object at the provider before you send the request again.

For a create that succeeds, result holds the id of the new object. For a copy, result also holds the budget that the copy carries: { "dailyBudget": 50 } or { "lifetimeBudget": 500 } in whole units of the currency of the advertiser, "campaign" for an ad set copy that spends the budget of its campaign, or "adset" for a campaign copy whose ad sets each carry a budget. That budget is what the copy can spend after you activate it. For a schedule change that gives an ACTIVE ad set that has ended a new end time, or no end, result also holds deliveryStartsAgain of true: the ad set delivers again.

For a change to an object that exists, the answer also holds priorValue. It holds the values that the change replaced, read from the provider just before the change, with the field names and units of the action: { "dailyBudget": 800 } for a budget, { "status": "ACTIVE" } for a status, { "startTime": "2026-10-01T00:00:00+0200", "endTime": null } for a schedule with no end, { "name": "Spring Prospecting" } for a name. To undo the change, send the same kind again with these values. A create and a copy have no priorValue.

To poll, you need mutation:write, the permission that started the change.

The actions

Each action has a kind. The name of the kind starts with its provider.

kind What it does
meta_set_status Sets the status of a campaign, an ad set, or an ad: ACTIVE, PAUSED, or ARCHIVED.
meta_update_budget Sets the daily budget or the lifetime budget of a campaign or an ad set.
meta_update_bidding Sets the bid strategy of a campaign or an ad set, and the amount that the strategy takes, at the level where the budget sits.
meta_update_schedule Sets the start time or the end time of an ad set, or removes its end time.
meta_rename Sets the name of a campaign, an ad set, or an ad.
meta_create_campaign Creates a campaign.
meta_create_adset Creates an ad set in a campaign.
meta_create_creative Creates a creative from an Asset that you registered to this advertiser.
meta_create_ad Creates an ad from an ad set and a creative.
meta_copy_campaign Copies a campaign, with no ad set and no ad, into the same ad account.
meta_copy_adset Copies an ad set, with no ad, into its own campaign or into another campaign. It takes an optional start time and end time.

Start a mutation shows each field of each action.

To build an ad, create the objects in this order: the campaign, the ad set, the creative, and the ad. Each step takes the new id from the result of the step before it. The creative takes the ast_ id of an Asset that you registered to the same advertiser. See Upload a file.

A copy is shallow: it holds the one object that you copy, and none of the objects under it. Meta adds “ - Copy” to the name of the copy. To give it a full name, send meta_rename. To copy an ad, create an ad with the creativeId of the source ad. A copy starts its own learning phase. Meta refuses to copy an Advantage+ shopping campaign or an Advantage+ app campaign, and a copy that targets the EU needs the default payor and the default beneficiary of the ad account.

A creative also needs the id of a Facebook Page. An ad set that optimizes for a conversion also needs a pixel. The REST API has no operation that lists Pages or pixels. Over MCP, the tools meta_list_pages and meta_list_pixels list them.

Rules for each write

  • A change acts only on an object of the advertiser. Before meta_set_status, meta_update_budget, meta_update_bidding, meta_update_schedule or meta_rename writes, and before a copy, AdCrunch reads the object at Meta. When the object is in another ad account, the Mutation ends complete with result.error not_found, and nothing reaches Meta.
  • An id is the native Meta id, digits only. The body refuses any other id with 400 invalid_request.
  • The API deletes nothing. To retire an object, send meta_set_status with ARCHIVED. On Meta, you cannot make an archived object active again from this API.
  • A budget is in whole units of the currency of the advertiser. 10.5 is 10.50. A budget that you read from Observe is in the same unit. See Conventions. A bid cap and a cost cap use the same unit. A ROAS floor is a ratio: 1.5 is a purchase value of 1.5 times the spend.
  • An action takes only the fields that its schema names. No field sends a value to the provider unchanged.

Read the history

Each Mutation stays in the history of your organization. The history shows the advertiser, the change, who asked for it, and how it ended. A row of a change to an object that exists also holds its priorValue. GET /mutations lists the history, and needs mutation:read:

curl https://api.adcrunch.dev/mutations \
  -H "Authorization: Bearer $ADCRUNCH_API_KEY"

The answer holds data, at most limit rows (100 by default). When more rows exist, it also holds nextCursor. Send that value back as cursor to read the next page. The console shows the same history on the Activity page.

Was this page helpful?