Create messaging ad
Creates a Meta ad that opens a chat with you on WhatsApp, Messenger or Instagram Direct when someone taps it.
Creates a click-to-message ad; destination selects where the tapped ad opens a
conversation: WhatsApp, the Page's Messenger inbox or the linked Instagram account's Direct inbox.
destinations puts two or three of them on one ad set and lets Meta pick the app per viewer.
The ad set is created with the matching destination_type and
CONVERSATIONS optimization; the campaign objective defaults to OUTCOME_ENGAGEMENT.
Supports single-creative and multi-creative shapes. Supersedes POST /v1/ads/ctwa
(deprecated, equivalent to destination: whatsapp).
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).
accountId is a Facebook, Instagram or Meta ads (business login) connection;
pageId picks the Page when that connection was granted several.
Required fields per shape (a missing one is a 400 naming it in param):
- Always
accountId,adAccountId,name, and exactly one ofdestination/destinations. budgetAmountandbudgetTypeunlessadSetIdorexistingCampaignIdis set, plusendDatefor a lifetime budget.- Single image or video:
headline,body, and exactly one ofimageUrl/video. WithplacementAssets,headlineandbodyonly (the media comes fromplacementAssets). carouselCards:bodyand the 2-10 cards;headlineis optional,imageUrl/video/ top-leveldescriptionare a 400.- Existing post (
platformPostIdorobjectStoryId): no copy or media fields. creatives[]: the same rules per entry, with no top-level creative fields.welcomeMessage, when sent:textplus exactly one ofprefillText/quickReplies.
WhatsApp number prerequisite. For the WhatsApp destination, link the WhatsApp number to the Facebook Page used for the ad before creating it: Meta checks the pairing at creation. Link it from the WhatsApp Business app (Settings > Business tools > Facebook & Instagram) or from the Page settings (Linked accounts > WhatsApp). Linking does not affect the number's WhatsApp connection in Zernio (Cloud API or coexistence), so no reconnect or re-sync is needed. If Meta creates a new ad set without the requested whatsappPhoneNumber, the 201 response carries a warnings entry. This can happen even when the number is linked (Meta can drop a Page's second linked number).
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). If the first request is cut off mid-create (for example by a function timeout), its key can stay in progress and keep returning 409 until it expires; use a new key to retry sooner, after checking whether the ad was created. Keys are scoped to your credential and expire after 24h.
Authorization
bearerAuth API key authentication: send your Zernio API key in the Authorization header, prefixed with Bearer.
In: header
Header Parameters
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. A request cut off mid-handler (for example by a function timeout) can leave its key in progress, answering 409 until the key expires after 24 hours.
length <= 255Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
In addition to the required list, the request must use
EXACTLY ONE of the two shapes:
- Single-creative:
headline,body, and one ofimageUrl/video, ORplatformPostId/objectStoryIdto reuse an organic post. On POST /v1/ads/messaging,placementAssetscan replaceimageUrl/video. - Carousel (messaging and CTWA):
bodyandcarouselCards(2-10 image cards) instead ofimageUrl/video, top level or percreatives[]entry. - Multi-creative: a non-empty
creatives[]array. Top-level creative fields must NOT be set on this shape.
Existing post references work on messaging and CTWA only (not call ads).
They cannot be combined with each other or with headline, body, imageUrl
or video. welcomeMessage works on them (it lives on the ad, not the post).
No media is uploaded and the organic post is retained. Fresh creatives still require headline, body, and image or video.
The route enforces this at the Zod boundary; OpenAPI's
required cannot express the OR cleanly.
Campaign, ad set and targeting fields match POST /v1/ads/create on
Meta and run through the same builders: the full targeting set
(including rawTargeting, languages, gender, exclusions and
savedTargetingId), budgetLevel (campaign budget), startDate /
endDate in the ad account timezone, and existingCampaignId to add
a new ad set to a campaign you already have.
Response Body
application/json
application/json
application/json
application/json
application/json
import Zernio from '@zernio/node';const zernio = new Zernio({ apiKey: process.env.ZERNIO_API_KEY });const { data } = await zernio.messagingads.createMessagingAd({ body: { accountId: 'account_abc123', adAccountId: 'adaccount_abc123', name: 'Example', destination: 'whatsapp', destinations: [ 'whatsapp', ], placementAssets: { defaultImageUrl: 'https://example.com/feed-4x5.jpg', rules: [ { imageUrl: 'https://example.com/feed-4x5.jpg', headline: 'Build with Zernio', body: 'Schedule social posts from your app.', description: 'One API for your social content.', placements: { publisherPlatforms: [ 'facebook', ], facebookPositions: [ 'feed', ], }, }, { imageUrl: 'https://example.com/story-9x16.jpg', headline: 'Your next story', body: 'Bring your social workflow to Zernio.', placements: { publisherPlatforms: [ 'facebook', ], facebookPositions: [ 'story', 'facebook_reels', ], }, }, ], }, },});console.log(data);{ "validateOnly": true, "results": [ { "node": "campaign", "status": "validated", "reason": "string" } ], "message": "string"}Create Click-to-Call ad
Creates a Meta ad with a Call Now button that dials your phone number, optimized for quality calls. Same shape and flow as POST /v1/ads/ctwa, but the CTA is CALL_NOW dialing `phoneNumber` via a tel: link. The ad set is destination_type PHONE_CALL optimizing QUALITY_CALL and the campaign objective defaults to OUTCOME_LEADS. Supports the same single-creative and multi-creative shapes as CTWA. **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). If the first request is cut off mid-create (for example by a function timeout), its key can stay in progress and keep returning 409 until it expires; use a new key to retry sooner, after checking whether the ad was created. Keys are scoped to your credential and expire after 24h.
Create reach-frequency prediction
Quotes the reach or budget for a Meta reach and frequency campaign over a date range, without buying anything. This is a QUOTE, nothing is bought and no ad entities are created. Provide a date range plus exactly one of `budgetAmount` (Meta predicts reach) or `reach` (Meta predicts the budget). The response carries the estimate and its allowed bounds (min/max budget and reach). Predictions expire on their own; to buy, reserve one via POST /v1/ads/rf-predictions/{predictionId}/reserve and pass the RESERVED id to POST /v1/ads/create with `buyingType: "RESERVED"`. Reservation campaigns reject automatic placements. Top-level `placements` wins; when it is omitted, `targeting.placements` is used; when neither is set, placements default to Facebook feed (+ Instagram stream when a linked IG professional account resolves). Instagram placements require that IG account.