Skip to content

Campaign.create

Open a Campaign on your ad account, such as $100 on LinkedIn for a week.

Action class
spend
Approval
Asks above your rule
Emits
Campaign.created
Price
Free
POST https://api.sb/{your-domain}/campaigns/create

Opens a Campaign on your connected ad account, with its budget, window and stop rule, and books the budget as a Bill. This Verb is a sketch on the hub’s Campaign Noun.

Inputs

NameTypeDefaultMeaning
venuestringRequiredWhere it runs, by id: linkedin, facebook, instagram, google-search, bing, reddit, x or tiktok.
objective'awareness' | 'traffic' | 'leads' | 'conversions'RequiredWhat the ad network delivers for: leads fills its lead form, conversions counts a signup on your site, traffic buys clicks.
budgetmoneyRequiredWhat it may spend, as on the hub’s Campaign: the most it spends over its whole window, never per day.
startsAtdatetimenowWhen it starts. It serves once its first Ad passes the ad network’s review.
endsAtdatetimestartsAt + 7dWhen it ends. Unset: seven days after it starts.
goalGoalnullThe Goal it spends toward, as on the hub’s Campaign.
targetingTargeting{ countries: ['US'] }Who sees it, in account-neutral fields: titles, industries, headcount, countries, interests, communities. The per-account table under Effects lists the fields each account honors.
keywordsstring[][]For a search ad account: the searches to show on, each as an exact match unless it says otherwise.
negativesstring[][]For a search ad account: searches it never shows on, such as free template or jobs.
audiencesAudience[][]Saved Audiences to show it to, as Audience.create made them, on top of targeting.
bid'lowest-cost' | { costPer: money }'lowest-cost'lowest-cost lets the ad network bid to spend the budget; a costPer cap holds each result under it and may underspend.
stopRulestringnullThe reading that pauses it, such as cost per lead above $40 after $60 spent. It is read each day from your own results.
playstringnullThe Play it runs under, so its spend and results count in that Play’s readout.

Outputs

FieldTypeWhat null means
campaignCampaignNever null.
campaign.statestringNever null: draft until its first Ad passes review, then live, paused or ended.
campaign.budgetmoney, in USDNever null: the budget sent.
campaign.startsAtdatetimeNever null: the start sent, or the time of the call.
campaign.endsAtdatetimeNever null: the end sent, or seven days after the start.
campaign.outsidejsonNull in try mode and until the ad account returns its own id for the Campaign.
billBillNull in try mode. Otherwise the Bill for the budget, owed for the ad spend and coded to expense.advertising.

Effects

Action class

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

Modes
  • try: Validates the Campaign against the ad account and returns it as it would be created; nothing is created and no Bill is booked.
  • send: Returns a pending Action at once; Campaign.created fires when the ad account holds the Campaign.
  • do: Waits until the ad account holds the Campaign and returns it with its id on the ad account.
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 opens a second Campaign or books a second Bill.

Saved or shared

The Campaign, its Bill and its id on the ad account are saved. Spend posts to your books as the account reports it, and nothing is shared with other startups.

Emits

Campaign.created with { campaign, venue, objective, budget, bill }

Performed by

Paid Media Manager

Price

Free on your own account. The spend is billed by the site directly: Google, LinkedIn, Meta, Microsoft, Reddit, TikTok, X.

Standing approval

It asks you first above its seat rule, unless a standing approval covers it: a Mandate you grant once with Mandate.authorize.

On each Venue

SiteFieldsFormats
Google Search and Google Display and YouTubeobjective, budget, startsAt, endsAt, keywords, negatives2
LinkedInobjective, budget, startsAt, endsAt, targeting, audiences, bid3
Facebook and Instagramobjective, budget, startsAt, endsAt5
Bingobjective, budget, keywords, negatives, bid, targeting, startsAt1
Redditobjective, targeting, budget, startsAt, endsAt, bid, keywords3
TikTokobjective, budget, startsAt, endsAt, targeting, bid2
Xobjective, targeting, budget, startsAt, endsAt, bid, keywords, gUrl2

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)
Campaign.startsAtcampaign.start_date
Campaign.endsAtcampaign.end_date
Campaign.keywordsad_group_criterion.keyword (match_type EXACT for a test)
Campaign.negativescampaign_criterion.negative (With the wasted search term as its keyword)
  • Smart Bidding. Learns from your conversion volume, and a new account has none, so a first test runs on manual CPC or maximize clicks on exact terms.
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)
Campaign.startsAtadCampaign.runSchedule.start (Epoch milliseconds)
Campaign.endsAtadCampaign.runSchedule.end
Campaign.targetingadCampaign.targetingCriteria (Job titles, industries, staff count ranges and locations, each a LinkedIn targeting facet)
Campaign.audiencestargetingCriteria: audienceMatchingSegments
Campaign.bidadCampaign.costType and adCampaign.unitCost (lowest-cost lets LinkedIn bid to spend the budget; a costPer cap sets unitCost)
  • Budget minimums. $10 a day for any ad format, and a new campaign’s lifetime budget is at least $100.
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)
Campaign.startsAtadset.start_time
Campaign.endsAtadset.end_time
  • Minimum daily budget. Meta sets it per ad account, currency and what the ad set pays for; the agent reads it from your ad account before it builds, and a budget under it refuses with venue-rejected.
  • One ad set. A test under $1,000 runs in one ad set, never split, so every result feeds the same learning.
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
Campaign.keywordskeyword text and match type (Exact unless the term says otherwise; phrase and broad on request)
Campaign.negativescampaign negative keywords
Campaign.bidad group default bid, or the bid strategy (lowest-cost is maximize clicks; a costPer cap is a manual CPC bid)
Campaign.targetinglocation targeting (Countries and regions; Microsoft also shows ads to people searching about a place unless you set people in it)
Campaign.startsAtad group start date
  • What the import copies. Campaigns, ad groups, keywords, ads and negatives as they stand in Google Ads; the agent then sets the budget your standing approval allows.
  • A day’s spend. Microsoft may spend up to twice the daily budget on a busy day, and never more than the daily budget times 30.4 in a month; your standing approval’s cap is set on the month for that 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.targetingad group community targeting (The subreddits, by name)
Campaign.budgetad group budget
Campaign.startsAtad group start date and time
Campaign.endsAtad group end date and time
Campaign.bidad group bid strategy (lowest-cost is automatic bidding; a costPer cap is a cost cap on clicks or conversions)
Campaign.keywordsad group keyword targeting (Shows the ad beside conversations that use the words, across subreddits)
  • Budget floor. The agent reads the minimum Ads Manager shows for your account before it builds, and a budget under it refuses with venue-rejected.
  • 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)
Campaign.startsAtadgroup schedule_start_time
Campaign.endsAtadgroup schedule_end_time (Set with schedule_type SCHEDULE_START_END)
Campaign.targetingadgroup location_ids, age_groups and interest_category_ids (TikTok targets people, not job titles: an interest stands in for a role)
Campaign.bidadgroup bid_type (lowest-cost is BID_TYPE_NO_BID; a costPer cap is BID_TYPE_CUSTOM with bid_price)
  • Budget minimums. An ad group’s daily budget must exceed $20, and a campaign’s daily budget, when you set one, must exceed $50.
Video Spark Ad

X

Through X Ads.

Our fieldX Ads’s field
Campaign.objectivecampaign objective (Picked in X Ads: traffic is website traffic; engagement is post engagement)
Campaign.targetingfollower look-alikes (Of the accounts you name)
Campaign.budgetcampaign total and daily budget
Campaign.startsAtad group start time
Campaign.endsAtad group end time
Campaign.bidad group bid strategy (lowest-cost is autobid; a costPer cap is a target cost)
Campaign.keywordskeyword targeting (Reaches people who searched or posted the words lately, beside the follower look-alikes)
Ad.landingUrlwebsite card URL (Added in the first reply when the post carries no link)
  • Ad review. X reviews every promoted post; a rejection comes back as venue-rejected with the reason.
  • What gets promoted. A post already on your account, as it is; the agent never writes a post only for the ad.
  • Post length. 280 weighted characters on a standard account, a link counting as 23 wherever it points.
Standard post Promoted post

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.
X AdsReturns the promotion as X Ads will hold it, with the steps the browser agent would take; no session opens and nothing is promoted.

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
An ads Play on a site that has not received one test qualified lead from your recordsconversion-sendConnect the ad account and send one test qualified lead; the site stays off until it lands.Integration.connect

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: it waits for an Approval when the spend is over your approval rule and no standing approval covers it, then runs until the ad account holds the Campaign.
Delivered byCampaign.created

Example

Text
Set up a $100 LinkedIn test over one week for owners and property
managers at US firms with 20 to 200 staff, aimed at leads.

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
venue: 'linkedin' under sk_test_{"state":"draft","outside":{"linkedin":"test-campaign-1"}}Opens a Campaign on a test ad account that never serves or spends, and returns the same campaign id every time.
budget: { amount: 999999, currency: 'USD' }Refuses with over-mandateA $999,999 budget is over the Limit of every test standing approval, so the call refuses before a Campaign or a Bill exists.
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