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- Sites
- 10 sites
- 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
| Name | Type | Default | Meaning |
|---|---|---|---|
venue | string | Required | Where it runs, by id: linkedin, facebook, instagram, google-search, bing, reddit, x or tiktok. |
objective | 'awareness' | 'traffic' | 'leads' | 'conversions' | Required | What the ad network delivers for: leads fills its lead form, conversions counts a signup on your site, traffic buys clicks. |
budget | money | Required | What it may spend, as on the hub’s Campaign: the most it spends over its whole window, never per day. |
startsAt | datetime | now | When it starts. It serves once its first Ad passes the ad network’s review. |
endsAt | datetime | startsAt + 7d | When it ends. Unset: seven days after it starts. |
goal | Goal | null | The Goal it spends toward, as on the hub’s Campaign. |
targeting | Targeting | { countries: | 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. |
keywords | string[] | [] | For a search ad account: the searches to show on, each as an exact match unless it says otherwise. |
negatives | string[] | [] | For a search ad account: searches it never shows on, such as free template or jobs. |
audiences | Audience[] | [] | Saved Audiences to show it to, as Audience.create made them, on top of targeting. |
bid | 'lowest-cost' | { costPer: | 'lowest-cost' | lowest-cost lets the ad network bid to spend the budget; a costPer cap holds each result under it and may underspend. |
stopRule | string | null | The reading that pauses it, such as cost per lead above $40 after $60 spent. It is read each day from your own results. |
play | string | null | The Play it runs under, so its spend and results count in that Play’s readout. |
Outputs
| Field | Type | What null means |
|---|---|---|
campaign | Campaign | Never null. |
campaign.state | string | Never null: draft until its first Ad passes review, then live, paused or ended. |
campaign.budget | money, in USD | Never null: the budget sent. |
campaign.startsAt | datetime | Never null: the start sent, or the time of the call. |
campaign.endsAt | datetime | Never null: the end sent, or seven days after the start. |
campaign.outside | json | Null in try mode and until the ad account returns its own id for the Campaign. |
bill | Bill | Null 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 onspendcan 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.createdfires 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-Keyand input return the first Action, and the same key with other input returns409. 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.createdwith{ campaign,venue, objective, budget, bill } - Performed by
- 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
Mandateyou grant once withMandate.authorize.
On each Venue
| Site | Fields | Formats |
|---|---|---|
| Google Search and Google Display and YouTube | objective, budget, startsAt, endsAt, keywords, negatives | 2 |
objective, budget, startsAt, endsAt, targeting, audiences, bid | 3 | |
| Facebook and Instagram | objective, budget, startsAt, endsAt | 5 |
| Bing | objective, budget, keywords, negatives, bid, targeting, startsAt | 1 |
objective, targeting, budget, startsAt, endsAt, bid, keywords | 3 | |
| TikTok | objective, budget, startsAt, endsAt, targeting, bid | 2 |
| X | objective, targeting, budget, startsAt, endsAt, bid, keywords, gUrl | 2 |
Google Search and Google Display and YouTube
Through Google Ads.
| Our field | Google Ads’s field |
|---|---|
Campaign.objective | campaign.advertising_channel_type (With a conversion goal; leads runs a SEARCH campaign bidding toward your signup conversion) |
Campaign.budget | campaign_budget.amount_micros (A daily amount in millionths: $40 is 40000000) |
Campaign.startsAt | campaign.start_date |
Campaign.endsAt | campaign.end_date |
Campaign.keywords | ad_group_criterion.keyword (match_type EXACT for a test) |
Campaign.negatives | campaign_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.
Through LinkedIn Ads.
| Our field | LinkedIn Ads’s field |
|---|---|
Campaign.objective | adCampaign.objectiveType (leads is LEAD_GENERATION with LinkedIn’s lead form, traffic WEBSITE_VISIT, conversions WEBSITE_CONVERSION) |
Campaign.budget | adCampaign.dailyBudget or adCampaign.totalBudget ({ amount,, the amount as a decimal string; a lifetime budget runs on lifetime pacing, which LinkedIn recommends) |
Campaign.startsAt | adCampaign.runSchedule.start (Epoch milliseconds) |
Campaign.endsAt | adCampaign.runSchedule.end |
Campaign.targeting | adCampaign.targetingCriteria (Job titles, industries, staff count ranges and locations, each a LinkedIn targeting facet) |
Campaign.audiences | targetingCriteria: |
Campaign.bid | adCampaign.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.
Facebook and Instagram
Through Meta Ads.
| Our field | Meta Ads’s field |
|---|---|
Campaign.objective | campaign.objective (leads is OUTCOME_LEADS, traffic OUTCOME_TRAFFIC, conversions OUTCOME_SALES) |
Campaign.budget | adset.daily_budget or adset.lifetime_budget (In the account currency’s smallest unit: $40.00 is 4000) |
Campaign.startsAt | adset.start_time |
Campaign.endsAt | adset.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.
Bing
Through Microsoft Ads.
| Our field | Microsoft Ads’s field |
|---|---|
Campaign.objective | campaign goal (Set in Microsoft Ads’ own screens; leads bids toward the signup conversion the import brings over) |
Campaign.budget | campaign daily budget |
Campaign.keywords | keyword text and match type (Exact unless the term says otherwise; phrase and broad on request) |
Campaign.negatives | campaign negative keywords |
Campaign.bid | ad group default bid, (lowest-cost is maximize clicks; a costPer cap is a manual CPC bid) |
Campaign.targeting | location targeting (Countries and regions; Microsoft also shows ads to people searching about a place unless you set people in it) |
Campaign.startsAt | ad 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.
Through Reddit Ads.
| Our field | Reddit Ads’s field |
|---|---|
Campaign.objective | campaign objective (Picked in Ads Manager: traffic sends readers to your page; leads bids toward your signup event) |
Campaign.targeting | ad group community targeting (The subreddits, by name) |
Campaign.budget | ad group budget |
Campaign.startsAt | ad group start date and time |
Campaign.endsAt | ad group end date and time |
Campaign.bid | ad group bid strategy (lowest-cost is automatic bidding; a costPer cap is a cost cap on clicks or conversions) |
Campaign.keywords | ad 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.
TikTok
Through TikTok Ads.
| Our field | TikTok Ads’s field |
|---|---|
Campaign.objective | campaign objective_type (traffic is TRAFFIC; leads is LEAD_GENERATION with TikTok’s instant form; conversions is WEB_CONVERSIONS on your pixel) |
Campaign.budget | adgroup budget with budget_mode BUDGET_MODE_DAY (Over $20; the campaign budget stays unset unless you ask, and then it is over $50) |
Campaign.startsAt | adgroup schedule_start_time |
Campaign.endsAt | adgroup schedule_end_time (Set with schedule_type SCHEDULE_START_END) |
Campaign.targeting | adgroup location_ids, (TikTok targets people, not job titles: an interest stands in for a role) |
Campaign.bid | adgroup 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.
X
Through X Ads.
| Our field | X Ads’s field |
|---|---|
Campaign.objective | campaign objective (Picked in X Ads: traffic is website traffic; engagement is post engagement) |
Campaign.targeting | follower look-alikes (Of the accounts you name) |
Campaign.budget | campaign total and daily budget |
Campaign.startsAt | ad group start time |
Campaign.endsAt | ad group end time |
Campaign.bid | ad group bid strategy (lowest-cost is autobid; a costPer cap is a target cost) |
Campaign.keywords | keyword targeting (Reaches people who searched or posted the words lately, beside the follower look-alikes) |
Ad.landingUrl | website 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-rejectedwith 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.
What try does
| Account | What try does |
|---|---|
| Google Ads | Sends every change with validate_only set: Google Ads checks the whole build and returns any error, and creates nothing. |
| LinkedIn Ads | Reads 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 Ads | Sends 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 Ads | Returns 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 Ads | Returns 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 Ads | Returns 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 Ads | Returns 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
| Check | Error | Fix | Then call |
|---|---|---|---|
| A landing page fails its status or Price check | landing | Fix 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 records | conversion-send | Connect 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
{
"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
| Fact | Value |
|---|---|
send returns | A 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 by | Campaign.created |
Example
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 value | Always returns | What it is for |
|---|---|---|
venue: | {"state": | Opens a Campaign on a test ad account that never serves or spends, and returns the same campaign id every time. |
budget: | Refuses with over-mandate | A $999,999 budget is over the Limit of every test standing approval, so the call refuses before a Campaign or a Bill exists. |
maxCost: | Refuses with max-cost | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
rate-limited.test | Refuses with rate-limited | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
sources-busy.test | Refuses with sources-busy | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |
Used by
- Workflows: Ad test setup
- Jobs: Run ads
- Integrations: Google Ads · LinkedIn Ads · Meta Ads · Microsoft Ads · Reddit Ads · TikTok Ads · X Ads