URL: /code/apps/ads · Id: `page:/code/apps/ads`

# Ads

> Audiences, Campaigns and Ads on your own Meta, Google, Bing, LinkedIn, Reddit, X and TikTok ad accounts, spent only under a standing approval.

[Audiences](https://startupbuilder.dev/business/nouns/audience), [Campaigns](https://startupbuilder.dev/business/nouns/campaign) and [Ads](https://startupbuilder.dev/business/nouns/ad) on your own Meta, Google, Bing, LinkedIn, Reddit, X and TikTok ad accounts, spent only under a [standing approval](https://startupbuilder.dev/business/nouns/mandate).

- **System:** [Ads](https://startupbuilder.dev/systems/ads)
- **Nouns:** 4
- **Verbs:** 6
- **Events:** 6

```text title="Run it · Ask"
Add the third ad to the LinkedIn test: the renewal-notice angle, with the checklist image.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Ad.create({ campaign: 'campaign_Li4tX9pQ2w', format: 'image', headline: 'Every renewal notice, 60 days out, for every owner', body: 'Leaseline tracks rent and lease renewals for property managers with 20 to 400 units. $149 a month per portfolio.', media: ['file_Rn6cK2vT8m'], landingUrl: 'https://leaseline.app/renewals', cta: 'learn-more', variant: 'renewal-notice' })
```

```json title="Run it · Response"
{
  "api": "https://api.sb/leaseline.app",
  "ad": {
    "$type": "Ad",
    "campaign": "https://api.sb/leaseline.app/campaigns/campaign_Li4tX9pQ2w",
    "format": "image",
    "variant": "renewal-notice",
    "state": "inReview",
    "landingUrl": "https://leaseline.app/renewals?utm_source=linkedin&utm_medium=paid&utm_campaign=campaign_Li4tX9pQ2w&utm_content=renewal-notice",
    "spend": null,
    "outside": null
  },
  "action": {
    "operation": "Ad.create",
    "mode": "try",
    "status": "done"
  }
}
```

## Nouns
| Noun | What it is |
| --- | --- |
| [Campaign](https://startupbuilder.dev/business/nouns/campaign) | A Campaign is a bounded effort toward a Goal, with a budget, a window, and the Ads or Messages it sends to Audiences. |
| [Ad](https://startupbuilder.dev/business/nouns/ad) | An Ad is one paid or house placement promoting an offer to an Audience, in a format such as search, display, or video. |
| [Audience](https://startupbuilder.dev/business/nouns/audience) | An Audience is a saved query over Visitors, leads, Prospects, or customers, used to target Ads and Messages. |
| [Demand Budget](https://startupbuilder.dev/business/nouns/demand-budget) | A Demand Budget is the money a founder commits each month to demand Plays, recorded as a Decision with a target and a stop rule. |

## Properties
The hub defines each Noun’s core Properties; Ads adds the ones a sale needs.

### Campaign
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `budget` | `money` | What it may spend. | The hub |
| `startsAt` | `datetime` | When it starts. | The hub |
| `endsAt` | `datetime` | When it ends. | The hub |
| `venue` | [`VenueId`](/code/data/types#venueid) | Where it runs, such as `facebook` or `linkedin`. | Ads |
| `state` | `'draft' \| 'live' \| 'paused' \| 'ended'` | Where it stands on the ad account. | Ads |
| `stopRule` | `string` | The reading that pauses it, read each day from your own results. | Ads |
| `bill` | `Bill` | The budget, booked as a Bill you owe the site. | Ads |

### Ad
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `format` | `string` | One of `search`, `display`, `image`, or `video`. | The hub |
| `spend` | `money` | What it has cost so far. | The hub |
| `review` | `'pending' \| 'approved' \| 'rejected'` | The site’s review; a rejected Ad comes back with the site’s reason. | Ads |
| `headline` | `string` | At the site’s length, such as 27 characters on a Meta feed ad. | Ads |
| `landingUrl` | `string` | The page it sends clicks to; it must answer before the Ad is created. | Ads |

### Audience
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `size` | `integer` | How many people match. | The hub |
| `matched` | `integer` | How many members the ad account matched, as it reports. | Ads |

### Demand Budget
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `amount` | `money` | The monthly spend it funds; zero is the default. | The hub |
| `tier` | `string` | One of `$0`, `$100`, `$1k`, `$10k` or `$100k`: the highest tier at or below the amount, whose Play mix it buys. | The hub |
| `target` | `string` | The Metric value the spend must reach, such as cost per first Order under $40. | The hub |
| `stopRule` | `string` | The reading that closes the budget, such as acquisition cost above margin for two weeks. | The hub |
| `until` | `date` | The last day the budget runs before its Decision is read. | The hub |
| `status` | `string` | One of `Open`, `Stopped` or `Closed`. A closed budget is kept with its Evidence. | The hub |

## Lifecycle
Every move names the Verb that makes it, or the Event it waits on.

### Campaign
| State | What it means | Moves to |
| --- | --- | --- |
| `draft` | On the ad account, not serving: its first Ad has not passed review. | `live` on `Ad.updated`; `ended` by [`Campaign.update`](/code/apps/ads/update-campaign) |
| `live` | Serving and spending inside its budget and your standing approval. | `paused` by [`Campaign.update`](/code/apps/ads/update-campaign); `ended` by [`Campaign.update`](/code/apps/ads/update-campaign) |
| `paused` | Stopped by its stop rule or by you; its spend so far is booked. | `live` by [`Campaign.update`](/code/apps/ads/update-campaign); `ended` by [`Campaign.update`](/code/apps/ads/update-campaign) |
| `ended` | Past its end date, or closed; its results stay readable. | None: it ends here. |

## Verbs
| Verb | What it does | Approval | On each site | Limits |
| --- | --- | --- | --- | --- |
| [`Campaign.create`](/code/apps/ads/create-campaign) | Opens a Campaign on your connected ad account, with its budget, window and stop rule, and books the budget as a Bill. | Asks you first above its seat rule | Google Search, Google Display, YouTube, LinkedIn, Facebook, Instagram, Bing, Reddit, TikTok, X | The budget is booked as a Bill; over your approval rule it asks first unless a standing approval covers it. |
| [`Campaign.update`](/code/apps/ads/update-campaign) | Pauses a Campaign, or changes its budget, bid or end date on your ad account, inside your standing approval. | Asks you first above its seat rule | Google Search, Google Display, YouTube, LinkedIn, Facebook, Instagram, Bing, Reddit, TikTok, X | A raise past your standing approval waits with `over-mandate`. |
| [`Ad.create`](/code/apps/ads/create-ad) | Builds one ad in a Campaign on your connected ad account; it runs once the site’s review passes. | Goes out on its own | Google Search, Google Display, YouTube, LinkedIn, Facebook, Instagram, Bing, Reddit, TikTok | Every new Ad passes the site’s own review before it serves. |
| [`Audience.create`](/code/apps/ads/create-audience) | Matches a list from your records, such as your waitlist, to an Audience on your ad account, every email hashed first. | Runs on its own | Google Search, Google Display, YouTube, LinkedIn, Facebook, Instagram | None. |
| [`Mandate.spend`](https://startupbuilder.dev/business/nouns/mandate/spend) | Pays an outside party with a single-use card, inside your standing approval. | Asks you first above its seat rule | None. | None. |
| [`Ad.update`](https://startupbuilder.dev/business/nouns/ad/update) | Changes an Ad’s copy, creative or status; a changed Ad goes back through the site’s review. | Runs on its own | None. | None. |

A Verb that asks you first runs on its own inside a standing approval: a [`Mandate`](https://startupbuilder.dev/business/nouns/mandate) you grant once with [`Mandate.authorize`](/code/integrations/calls/authorize).

## Events
| Event | Emitted by |
| --- | --- |
| `Campaign.created` | [`Campaign.create`](/code/apps/ads/create-campaign) |
| `Campaign.updated` | [`Campaign.update`](/code/apps/ads/update-campaign) |
| `Ad.created` | [`Ad.create`](/code/apps/ads/create-ad) |
| `Ad.updated` | [`Ad.update`](https://startupbuilder.dev/business/nouns/ad/update) |
| `Audience.created` | [`Audience.create`](/code/apps/ads/create-audience) |
| `Mandate.spent` | [`Mandate.spend`](https://startupbuilder.dev/business/nouns/mandate/spend) |

A [Trigger](https://startupbuilder.dev/business/nouns/trigger) can start a Workflow on any of them, and a Watch delivers each to your endpoint, signed ([Events and webhooks](/code/events)).

## Who operates it
| Part | Ads |
| --- | --- |
| Accountable | [Paid Media Manager](https://startupbuilder.dev/agents/marketing/paid-media-manager) |
| Also calls its Verbs | [Marketing lead](https://startupbuilder.dev/agents/marketing/marketing-lead) |
| Seat | An agent holds each seat until you seat a person in it. |
| Asks you first | [`Campaign.create`](/code/apps/ads/create-campaign), [`Campaign.update`](/code/apps/ads/update-campaign), [`Mandate.spend`](https://startupbuilder.dev/business/nouns/mandate/spend), unless a standing approval covers it |

## Integrations
[Meta Ads](/code/integrations/meta-ads) · [Google Ads](/code/integrations/google-ads) · [Microsoft Ads](/code/integrations/microsoft-ads) · [LinkedIn Ads](/code/integrations/linkedin-ads) · [Reddit Ads](/code/integrations/reddit-ads) · [X Ads](/code/integrations/x-ads) · [TikTok Ads](/code/integrations/tiktok-ads)

## Import and export
| Direction | What | How |
| --- | --- | --- |
| Import | Campaigns already on your ad account | `Integration.sync` reads them in as Campaigns and Ads, with their spend to date |
| Export | Spend and results by Ad and day | `Metric.query`, or `Collection.export` to your warehouse |

## Example
**A $100 test that pauses its own losers.**

1. `Campaign.create` opens a one-week Campaign on Meta with $100 and a stop rule: pause an Ad past $15 with no trial.
2. `Ad.create` adds four Ads, one per angle; Meta rejects one for implying a guaranteed result, and it is rewritten.
3. On day 3, two Ads pass $15 with no trial. `Campaign.update` pauses them and moves their $39 to the leaders.
4. `Metric.query` reads trials per dollar from your own account each day until the winner rule names one.