Skip to content

Ad.create

Add one Ad to a Campaign; it shows once its review passes.

Action class
publish
Approval
Publishes on its own
Emits
Ad.created
Sites
9 sites
Price
Free
POST https://api.sb/{your-domain}/ads/create

Builds one ad in a Campaign on your connected ad account; it runs once the site’s review passes. This Verb is a sketch on the hub’s Ad Noun.

Inputs

NameTypeDefaultMeaning
campaignCampaignRequiredThe Campaign it runs in, sharing its budget and window, as on the hub’s Ad.
format'search' | 'display' | 'image' | 'video'RequiredOne of the hub’s Ad formats. The per-account table under Effects lists the formats each account takes.
headlinestringRequiredThe headline. The per-account table under Effects lists each account’s character limit.
bodystringnullThe text above or beside the media. Unset: the headline stands alone.
mediaFile[][]The image or video Files. An image or video Ad needs one.
landingUrlstringRequiredThe page a click lands on. The platform adds utm_ tags, so each signup credits this Ad.
ctastring'learn-more'The button, such as learn-more, sign-up or book-now, mapped to the nearest button each ad account offers.
offerOffernullThe offer it sells, as on the hub’s Ad.
variantstringnullThe angle it tests, so the readout compares Ads by angle.

Outputs

FieldTypeWhat null means
adAdNever null.
ad.statestringNever null: inReview until the ad network’s review ends, then live or rejected.
ad.formatstringNever null: the format sent.
ad.spendmoney, in USDNull until the ad account reports a first day of spend.
ad.outsidejsonNull in try mode and until the ad account returns its own id for the Ad.

Effects

Action class

publish: an approval rule on publish can make it wait for your OK

Modes
  • try: Checks the words and media against the ad specs of the account and the landing page’s status, and returns the Ad as it would be submitted; nothing is submitted.
  • send: Returns a pending Action; Ad.created fires when the ad account holds the Ad, and Ad.updated when its review ends.
  • do: Waits until the ad account holds the Ad and returns it inReview.
Idempotency

Only with a key: the same Idempotency-Key and input return the first Action, and the same key with other input returns 409. A repeat with the same key never submits a second Ad.

Saved or shared

The Ad is saved with its id on the ad account and, as the account reports it, its spend. A rejected Ad keeps the review’s reason.

Emits

Ad.created with { ad, campaign, venue, format, state }

Performed by

Paid Media Manager

Price

Free on your own account: the platform charges nothing for the call.

Standing approval

It goes out on its own, inside the Limits of your standing approval: a Mandate granted with Mandate.authorize. One approval rule makes it wait for your OK.

On each Venue

SiteFieldsFormats
Google Search and Google Display and YouTubepaign.objective, paign.budget, headline, body, landingUrl2
LinkedInpaign.objective, paign.budget, body, headline, landingUrl3
Facebook and Instagrampaign.objective, paign.budget, body, headline, landingUrl5
Bingpaign.objective, paign.budget, headline, body, landingUrl, path1
Redditpaign.objective, paign.budget, headline, landingUrl, callToAction, media3
TikTokpaign.objective, paign.budget, media, callToAction, landingUrl2

Through Google Ads.

Our fieldGoogle Ads’s field
Campaign.objectivecampaign.advertising_channel_type (With a conversion goal; leads runs a SEARCH campaign bidding toward your signup conversion)
Campaign.budgetcampaign_budget.amount_micros (A daily amount in millionths: $40 is 40000000)
Ad.headlinead_group_ad.​ad.​responsive_search_ad.​headlines (3 to 15, each 30 characters)
Ad.bodyad_group_ad.​ad.​responsive_search_ad.​descriptions (2 to 4, each 90 characters)
Ad.landingUrlad_group_ad.ad.final_urls
  • Ad review. Google reviews every ad; a disapproval comes back as venue-rejected with the policy it names.
Google Search: Responsive search ad YouTube: Video

LinkedIn

Through LinkedIn Ads.

Our fieldLinkedIn Ads’s field
Campaign.objectiveadCampaign.objectiveType (leads is LEAD_GENERATION with LinkedIn’s lead form, traffic WEBSITE_VISIT, conversions WEBSITE_CONVERSION)
Campaign.budgetadCampaign.dailyBudget or adCampaign.totalBudget ({ amount, currencyCode }, the amount as a decimal string; a lifetime budget runs on lifetime pacing, which LinkedIn recommends)
Ad.bodypost commentary (The creative references a post; 3,000 characters, 150 show before it truncates)
Ad.headlinepost content.article.title (200 characters; 70 show before it truncates)
Ad.landingUrlpost content.article.source, or the lead form
  • Ad review. LinkedIn reviews every ad; a rejection comes back as venue-rejected with the reason.
Feed post Single image ad, 1200 × 628, 1200 × 1200 or 720 × 900 Document ad

Facebook and Instagram

Through Meta Ads.

Our fieldMeta Ads’s field
Campaign.objectivecampaign.objective (leads is OUTCOME_LEADS, traffic OUTCOME_TRAFFIC, conversions OUTCOME_SALES)
Campaign.budgetadset.daily_budget or adset.lifetime_budget (In the account currency’s smallest unit: $40.00 is 4000)
Ad.bodyobject_story_spec.link_data.message (The primary text)
Ad.headlineobject_story_spec.link_data.name
Ad.landingUrlobject_story_spec.link_data.link
  • Ad review. Meta reviews every new ad before it runs; a disapproval comes back as venue-rejected with Meta’s reason.
Facebook: Feed image ad, 1440 × 1800, 4:5 Instagram: Feed image, 320 to 1,440 pixels wide, 4:5 to 1.91:1 Instagram: Carousel Instagram: Reel, 9:16 Instagram: Story, 9:16

Bing

Through Microsoft Ads.

Our fieldMicrosoft Ads’s field
Campaign.objectivecampaign goal (Set in Microsoft Ads’ own screens; leads bids toward the signup conversion the import brings over)
Campaign.budgetcampaign daily budget
Ad.headlineresponsive search ad headlines (Up to 15, each 30 characters)
Ad.bodyresponsive search ad descriptions (Up to 4, each 90 characters)
Ad.landingUrlfinal URL
Ad.pathdisplay URL path 1 and path 2 (15 characters each, shown after your domain)
  • Ads per ad group. Three enabled responsive search ads; a fourth waits until one is paused.
  • Ad review. Microsoft reviews every ad; a disapproval comes back as venue-rejected with the reason.
Responsive search ad

Reddit

Through Reddit Ads.

Our fieldReddit Ads’s field
Campaign.objectivecampaign objective (Picked in Ads Manager: traffic sends readers to your page; leads bids toward your signup event)
Campaign.budgetad group budget
Ad.headlinepost title (A promoted post keeps the title it earned replies under)
Ad.landingUrldestination URL
Ad.callToActioncall to action button (Such as Learn More or Sign Up)
Ad.mediathe promoted post’s image or video (As posted; a new image means a new post)
  • Ad review. Reddit reviews every ad; a rejection comes back as venue-rejected with the reason.
  • Comments on a promoted post. Stay open; each one reaches you as a Message, and your agent drafts the answer.
Text or link post Comment Promoted post

TikTok

Through TikTok Ads.

Our fieldTikTok Ads’s field
Campaign.objectivecampaign objective_type (traffic is TRAFFIC; leads is LEAD_GENERATION with TikTok’s instant form; conversions is WEB_CONVERSIONS on your pixel)
Campaign.budgetadgroup budget with budget_mode BUDGET_MODE_DAY (Over $20; the campaign budget stays unset unless you ask, and then it is over $50)
Ad.mediaad tiktok_item_id (A Spark Ad: your own post, authorized for the ad, keeping its likes and comments)
Ad.callToActionad call_to_action (Such as LEARN_MORE or SIGN_UP)
Ad.landingUrlad landing_page_url
  • Spark Ads. Promote a post already on your TikTok account, with its likes and comments kept.
  • Ad review. TikTok reviews every ad; a rejection comes back as venue-rejected with the reason.
Video Spark Ad

What try does

AccountWhat try does
Google AdsSends every change with validate_only set: Google Ads checks the whole build and returns any error, and creates nothing.
LinkedIn AdsReads your ad account, the matched count of each audience and the targeting facets, and checks the campaign against LinkedIn’s budget minimums; LinkedIn has no validate-only call, so nothing is sent to it and nothing is created.
Meta AdsSends the whole build with Meta’s validate_only execution option: Meta runs every check and creates nothing, and the response names any field it would reject.
Microsoft AdsReturns what the import would bring over, term by term with the budget it would set, and the steps the browser agent would take; no session opens and nothing changes.
Reddit AdsReturns the campaign and the promoted post as Ads Manager will hold them, with the steps the browser agent would take; no session opens and nothing is created.
TikTok AdsReturns the Spark Ad and its ad group as Ads Manager will hold them, checked against TikTok’s budget floors, with the steps the browser agent would take; nothing is created.

Refuses when

CheckErrorFixThen call
A landing page fails its status or Price checklandingFix what the reason names, then turn the Play back on.Campaign.update

Every call through an account can also meet the shared account refusals: grant-missing, needs-reauth, account-not-connected, over-mandate, quota-reached, venue-rejected.

The landing payload
JSON
{
  "type": "https://startupbuilder.dev/errors/landing",
  "title": "Landing",
  "status": 422,
  "detail": "A landing Page fails its status or Price check.",
  "fix": "Fix what the reason names, then turn the Play back on.",
  "next": {
    "verb": "Campaign.update",
    "input": {}
  },
  "instance": "https://api.sb/leaseline.app/actions/action_1"
}

When it waits

FactValue
send returnsA pending Action until the ad account holds the Ad; the review outcome arrives later as Ad.updated.
Delivered byAd.created

Example

Text
Add the third ad to the LinkedIn test: the renewal-notice angle, with
the checklist image.

To retry, send the same Idempotency-Key: the retry returns the first answer for $0. A new key reads again.

Test values

A sk_test_ key answers these the same way every time, at $0 (every test value).

Test valueAlways returnsWhat it is for
headline: 'test: disapprove'Refuses with venue-rejectedThe test ad account rejects it at review with the reason Misleading claims, passed through in detail, so your agent’s rewrite path runs.
maxCost: 0.0001Refuses with max-costAny priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap.
rate-limited.testRefuses with rate-limitedAny call naming this hostname answers 429 with Retry-After, so your retry logic runs.
sources-busy.testRefuses with sources-busyAny call naming this hostname answers 503 as if every route were busy, and nothing is spent.

Used by