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.
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
import Zernio from '@zernio/node';const zernio = new Zernio({ apiKey: process.env.ZERNIO_API_KEY });const { data } = await zernio.messagingads.createCallAd({ body: { accountId: 'account_abc123', adAccountId: 'adaccount_abc123', name: 'Example', phoneNumber: 'string', linkUrl: 'https://example.com', },});console.log(data);{ "adType": "single", "ad": {}, "message": "string", "warnings": [ "string" ]}Send conversion events
Sends conversion events, such as purchases or leads, from your server to an ad platform so it can attribute and optimize ads. Relay one or more conversion events to the target ad platform's native Conversions API. Platform is inferred from the provided `accountId`. Requires the Ads add-on. Supported platforms: - Meta (`metaads`) via Graph API - Google Ads (`googleads`) via Data Manager API `ingestEvents` - LinkedIn (`linkedinads`) via `/rest/conversionEvents` - TikTok (`tiktokads`) via the Offline Events API `/offline/batch/` (OFFLINE conversions only) - OpenAI Ads (`openaiads`) via its Conversions API (a separate host, `bzr.openai.com`) - Pinterest (`pinterestads`) via `POST /v5/ad_accounts/{id}/events` - Reddit (`redditads`) via the Reddit Conversions API (`POST /api/v3/pixels/{pixelId}/conversion_events`) `destinationId` semantics differ per platform: - Meta: pixel (dataset) ID, e.g. `123456789012345` - Google: conversion action resource name, e.g. `customers/1234567890/conversionActions/987654321` - LinkedIn: conversion rule ID or URN, e.g. `104012` or `urn:lla:llaPartnerConversion:104012` - TikTok: Offline Event Set ID, e.g. `7057103914977558530` - OpenAI Ads: pixel wire id (numeric `pixel_id`, distinct from the internal pixel id), as returned by `GET /v1/accounts/{accountId}/conversion-destinations` - Pinterest: numeric ad account id, e.g. `549755885175` (Pinterest attributes to the ad account, not to a tag) - Reddit: pixel id, e.g. `a2_abc123`, as returned by `GET /v1/accounts/{accountId}/conversion-destinations` (any other shape returns 400) Pinterest notes: each event needs an `email`, or `ipAddress` plus `userAgent`; others are listed in `failures` with code `INVALID_EVENT` and the rest are still sent. `clickIds.epik` (the `_epik` cookie) is sent as `click_id`. `actionSource` `web` stays `web`, offline sources become `offline`, and app events need `platformData.action_source` `app_android` or `app_ios`. Pinterest answers per event, so one bad event does not fail its chunk (failures carry code `PINTEREST_EVENT_FAILED`). `consent.adUserData: DENIED` sets `opt_out`. The connected user needs a Business Access role on the ad account (403 otherwise). Reddit notes: each event needs a match key: `clickIds.rdt_cid`, `clickIds.rdt_uuid`, `email`, `phone`, `externalId`, or `ipAddress` plus `userAgent`. Reddit accepts events up to 7 days old, and `eventTime` is unix SECONDS (a value more than an hour in the future is refused as milliseconds). Events failing these checks are listed in `failures` with code `INVALID_EVENT` and the rest are still sent. Email (lowercased, dots and `+tags` stripped from the local part), phone (E.164 with the leading `+`) and `externalId` are SHA-256 hashed; IP and user agent go plaintext. `consent.adUserData: DENIED` sends no user fields. `actionSource` maps to WEBSITE (`web`), APP (`app`) or OTHER, overridable with `platformData.action_source`; `platformData.order_id` is forwarded. `testCode` is sent as Reddit's `test_id`. The connection must hold the `adsconversions` permission: one authorized without it returns 403 `reconnect_required` (reconnect via GET /v1/connect/redditads/ads), and a Reddit user with no role on the ad account that owns the pixel returns 403 `insufficient_permissions`. Reddit throttling and other rejections are reported per chunk in `failures` with code `REDDIT_REJECTED`. TikTok notes: this path sends OFFLINE conversions (in-store / CRM / call-center), not web-pixel events. Each event must carry an email or phone (TikTok requires at least one). The connected TikTok ads account must have granted the Offline Events permission; older grants must reconnect. OpenAI Ads notes: requires a tracking tag (pixel) to already exist on the account. Returns 422 with code `TRACKING_TAG_REQUIRED` if `POST /v1/accounts/{accountId}/tracking-tags` hasn't been called yet. Callers can list valid destinations via `GET /v1/accounts/{accountId}/conversion-destinations`. All PII (email, phone, names, external IDs) is hashed with SHA-256 server-side per each platform's normalization spec, including Google's Gmail-specific dot/plus-suffix stripping. Send plaintext. LinkedIn `externalIds` are passed through as plaintext per LinkedIn's spec; only emails and phones are hashed. For LinkedIn, the connected account must have been authorized after the Conversions API rollout (i.e. the OAuth grant must include `rw_conversions`). Older accounts must reconnect. Batching is handled automatically. Meta caps at 1000 events per request and rejects the entire batch if any event is malformed. Google caps at 2000. LinkedIn caps at 5000 and is also all-or-nothing per chunk. OpenAI Ads caps at 1000 per request; larger submissions are split into 1000-event chunks, each all-or-nothing (a malformed event fails every event in that chunk, not the whole request). Reddit works the same way, in chunks of 1000. Dedup: pass a stable `eventId` on every event. Meta and LinkedIn use it to dedupe against browser-side pixel/Insight Tag events; Google maps it to `transactionId`; Reddit receives it as `conversion_id`, its dedup key against Reddit Pixel events. Per-platform `eventName` semantics: - Meta: free-form. Standard names (Purchase, Lead, ...) match Meta's built-in events; custom strings are accepted. - Google: ignored. The conversion action's category determines the event type. Send the standard name closest to your action for documentation, but the platform will not branch on it. - LinkedIn: ignored. The conversion rule's `type` (LEAD, PURCHASE, etc.) is locked to the destination at rule-creation time. Send the standard name for documentation; LinkedIn does not branch on it. - OpenAI Ads: a fixed subset of standard names (Purchase, Lead, AddToCart, ViewContent, InitiateCheckout, CompleteRegistration, Subscribe, StartTrial, Schedule) maps 1:1 onto OpenAI's own event-type enum; any other standard name or custom string is sent as `type: custom` with the name preserved. - Pinterest: standard names map onto Pinterest's (Purchase = `checkout`, AddToCart = `add_to_cart`, InitiateCheckout = `initiate_checkout`, AddPaymentInfo = `add_payment_info`, Lead = `lead`, CompleteRegistration = `signup`, Subscribe = `subscribe`, ViewContent = `view_content`, Search = `search`, PageView = `page_visit`); any other string is sent as is, so it matches an advertiser defined event of that name.
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 of `destination` / `destinations`. - `budgetAmount` and `budgetType` unless `adSetId` or `existingCampaignId` is set, plus `endDate` for a lifetime budget. - Single image or video: `headline`, `body`, and exactly one of `imageUrl` / `video`. With `placementAssets`, `headline` and `body` only (the media comes from `placementAssets`). - `carouselCards`: `body` and the 2-10 cards; `headline` is optional, `imageUrl` / `video` / top-level `description` are a 400. - Existing post (`platformPostId` or `objectStoryId`): no copy or media fields. - `creatives[]`: the same rules per entry, with no top-level creative fields. - `welcomeMessage`, when sent: `text` plus exactly one of `prefillText` / `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.