Create CTWA ad (deprecated)
Deprecated: use POST /v1/ads/messaging with destination: whatsapp. This endpoint stays available for back-compat; no removal planned.
Creates one or more Click-to-WhatsApp (CTWA) ads on Meta under a single campaign and ad set. When tapped, each ad opens a WhatsApp conversation with the business attached to the supplied Facebook Page. The full hierarchy (campaign, ad set, creative(s), ad(s)) is created and activated in one call. The CTA is locked to WHATSAPP_MESSAGE and the destination is hard-coded to api.whatsapp.com/send; Meta resolves the actual WhatsApp number from the Page-to-WA pairing configured in Page settings or Business Manager.
Supports two mutually-exclusive shapes:
-
Single-creative: supply top-level headline, body, and one of imageUrl / video, or a platformPostId / objectStoryId reference. Creates 1 campaign + 1 ad set + 1 ad.
-
Multi-creative: supply a creatives[] array with N entries (each carrying fresh media and copy or an existing post reference). Creates 1 campaign + 1 ad set + N ads sharing budget and targeting so Meta A/Bs the creatives inside a single auction instead of fragmenting budget across N parallel campaigns. Recommended when launching multiple creative variants for the same campaign.
Attach shape. Send adSetId (with either creative shape) to add the ads to an EXISTING messaging ad set instead of building a campaign, so the ad set keeps its learning phase, the way to refresh a CTWA creative without resetting delivery. The ad set then owns budget, targeting and schedule, so budgetAmount, budgetType, endDate, objective, countries, interests and audienceId are rejected with a 400 alongside it rather than silently dropped. The target ad set's destination_type must match the ad's destination (a WhatsApp ad needs a WHATSAPP ad set), otherwise Meta would accept an ad that never delivers.
Prerequisites enforced by Meta (surfaced as platform_error on failure): the Facebook Page must be paired with a verified WhatsApp Business number, the WhatsApp Business Account must be business-verified, and the Meta access token must carry ads_management. Existing posts and reels are supported through platformPostId (alias existingPostId) or objectStoryId, either per creative or at the top level. Omit fresh media and copy for that creative. Optional whatsappPhoneNumber selects a number already paired with the Page (WhatsApp destination only).
Idempotency: this endpoint is not idempotent at the platform level (a blind retry creates a second campaign/ad set/ad). Send an Idempotency-Key header to make retries safe: the first request with a given key creates the ad and we store the response; a retry with the same key replays that exact response (with Idempotent-Replayed: true) instead of creating duplicates. Reusing a key with a different body returns 422; a key whose first request is still in flight returns 409 (retry after a short backoff). Keys are scoped to your credential and expire after 24h.
Headers
Optional client-generated unique key (e.g. a UUID) that makes retries safe. Same key + same body replays the original response; same key + different body → 422; key still processing → 409.
Request body
Example request
{
"creativeFeatures": {
"auto_promotion_tag": "OPT_IN"
},
"creatives": [
{
"creativeFeatures": {
"auto_promotion_tag": "OPT_IN"
}
}
]
}Response
CTWA ad(s) created and submitted to Meta for review. Response is a tagged union discriminated by adType:
- adType: "single" → single-creative request: { adType, ad, message } where ad is the persisted Ad document.
- adType: "multi" → multi-creative request: { adType, ads, platformCampaignId, platformAdSetId, message } where ads is the array of N persisted Ad documents all sharing the returned campaign and ad set IDs.
Generated SDK clients can narrow on adType instead of sniffing for field presence.
Changes
Changed in 15 of the 56 revisions of this API.52
- ○
added the new optional
headerrequest parameterIdempotency-Keynew-optional-request-parameter
- ○
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
This revision also has 2 changes that name no endpoint, such as unreferenced schemas being removed. See the revision's changelog
- ○
- ○
added the new optional request property
//new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
request property
//deprecatedrequest-property-deprecated
- ○
request property
deprecatedrequest-property-deprecated
- ○
- ○
added the new optional request property
new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
- ○
added the new optional request property
/new-optional-request-property
- ○
the request property
/became optionalrequest-property-became-optional
- ○
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
added the optional property
/to the response with the statusresponse-optional-property-added
- ○
- ○
added the new optional request property
new-optional-request-property
- ○
- ○
added the media type
application/jsonfor the response with the statusresponse-media-type-added
- ○
added the non-success response with the status
response-non-success-status-added
- ○
- ○
added the new optional request property
new-optional-request-property
- ○
added the new optional request property
//new-optional-request-property
- ○
added the new optional request property
//new-optional-request-property
- ○
added the new optional request property
//new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
added the new optional request property
new-optional-request-property
- ○
the request property
//became optionalrequest-property-became-optional
- ○
the request property
//became optionalrequest-property-became-optional
- ○