Ad kit
One angle in, five headlines and texts, five images at each site’s sizes and a 6-second clip out, every claim checked, saved as Variants.
- Kind
- Agent task
- Price
- 38¢ per angle
- Your time
- 10 minutes
- Approval
- Runs on its own
- Deepens
- Experimentation
Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first.
curl -X POST "https: //api.sb/ leaseline.app/ ads/ make? mode= try" \
-H "Authorization: Bearer $SB_TOKEN" \
-H "Content-Type: application/ json" \
-d '{"goal": "Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first.", "budget": {"amount": 2, "currency": "USD"}}'
npx startup-builder do Ad.make --try --data '{"goal": "Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first.", "budget": {"amount": 2, "currency": "USD"}}'
{
"name": "do",
"arguments": {
"mode": "try",
"code": "return await $.Ad.make({ goal: 'Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })"
}
}
import { $ } from 'startup-builder'
const result = await $.try.Ad.make({ goal: 'Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })
{
"mode": "try",
"stored": false,
"estimate": "About 38¢ an angle",
"sample": {
"variant": "v_5",
"headline": "Never miss a lease renewal again",
"text": "Leaseline reminds you 90, 60 and 30 days out.",
"format": "feed 1080×1080",
"review": "passed"
}
}
What you get
- 1 angle · One angle in, five headlines and texts, five images at each site’s sizes and a 6-second clip out, every claim checked, saved as Variants. · Ad
variants[]· Each Variant: headline, primary text, image and format, ready for Ad test setup.clip· The 6-second clip with its voiceover, made only from an image that passed.review· Assets passed, and each cut with the reason, such as “the #1 rent app” with no source.
How long: About 3 minutes.
Trigger
It runs when you call it, and when message test names a winner.
on.Experiment.concluded((exp, $) => exp.decision.choice === 'ship' && $.Ad.make({ offer: exp.offer, angle: exp.winner.angle }))Steps
Your agent does these steps, one row per step; each step’s check is part of its row and its price.
| Step | Verb | Returns | Price | Approval |
|---|---|---|---|---|
| 1. Read the angle and the offer | Collection.query | The angle, the buyer phrase behind it and what the offer can claim | Free | Runs on its own |
| 2. Write five headlines and texts | Content.draft | Five pairs at the site’s lengths: 40-character headlines, 125-character primary text | About 1¢ | Runs on its own |
| 3. Make the images | File.generate | Five images at each format’s size, in your brand | About 8¢ | Runs on its own |
| 4. Make the clip from the best image | Video.draft | A 6-second clip from the image that passed with the highest score | About 28¢ | Runs on its own |
| 5. Record the voiceover | File.generate | The headline read over the clip | About 1¢ | Runs on its own |
| 6. Save them as Variants | Content.draft | Variants ready for Ad test setup | Free | Runs on its own |
Calls: Collection.query, then Content.draft, then File.generate, then Video.draft, then File.generate, then Content.draft.
About 38¢ an angle, each time it runs, capped at $1.00.
Example: give it a goal
Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first.
The plan it shows runs 6 steps, priced before it spends anything; Run it above shows the result it returns.
When something fails
Each failure, and what happens next
| When | Then |
|---|---|
| An image fails the brand check | It is made again once, then dropped; the kit ships with the images that passed. |
| No image passes | No clip is made, and the kit returns the copy with the findings; the clip is not billed. |
| You call it twice for the same angle and formats | The same Variants return: it is idempotent per offer, angle and formats. |
Each step retries three times, then falls through to the next route; one item's failure lands in skipped with its reason, never failing the call. Before a step would cross maxCost it stops and returns what it has, with a resume link. Each step's idempotency key is the call's key plus the step and the item: leaseline-ad-make-1-<step>-<item>.
How it is graded
A second model checks each asset against the site’s specs, your brand and the angle. It passes when each asset meets its format’s size and length, matches the brand, carries the angle, and claims only what the offer does. A miss is redone at no charge.
Change it
| Ask | Compiles to |
|---|---|
| Make the story format only, with three images. | Ad.make |
| Launch the five Variants on Meta at $20 a day. | Campaign.launch |
| Make a kit for the second-best angle too. | Ad.make |
import { $, workflow } from 'startup-builder'
export default workflow({
name: 'ad-make',
verb: 'Ad.make',
input: { offer: 'string', angle: 'string', formats: "('feed' | 'story' | 'search')[]", images: 'integer', clip: 'boolean' },
maxCost: 1,
grade: { evaluator: 'Venue specs, Brand, angle' , passWhen: 'size and length met; on Brand; carries the angle; no claim past the Offer' , remedy: 'redo' },
execute: async (ctx, { offer, angle, formats = ['feed', 'story'], images = 5, clip = true }) => {
const copy = await ctx.step('copy', () => $.Content.draft({ kind: 'ad-copy', offer, angle, count: images, maxCost: 0.02 }))
const pics = await ctx.step('images', () => $.File.generate({ kind: 'image', from: copy, formats, count: images, maxCost: 0.08 }))
const best = pics.filter((p) => p.grade.passed).sort((a, b) => b.grade.score - a.grade.score)[0]
const video = clip && best ? await ctx.step('clip', () => $.Video.draft({ from: best.$id, seconds: 6, voiceover: copy[0].headline, maxCost: 0.3 })) : null
return { variants: pairUp(copy, pics), clip: video }
},
})Extend it with extends: and base(), or compose your own from these steps.
Works with
Plays that order it: Account ads, Creator ads, House ads, Paid search, Paid social, Promote winners, Retargeting, Sponsorships, Video ladder.
It serves Make content, and runs in Paid. It deepens Experimentation.
For agents
For agents: the guide, its approvals, retries and lookups
| Fact | Value |
|---|---|
| Guide | Ad.make |
| Guide approval | none |
| Goal | Make five ad images and one short clip for the renewals angle, spend up to $2, and show me the plan first. It may change counts, the order of steps, soft filters and the route a read takes, and it stops early when the goal is met. It never relaxes your words, raises its own budget, answers its own approval, calls a Play or acts outside your standing approval. |
| Trigger | on.Experiment.concluded((exp, |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-ad-make-1 |
| Lookups | A find: input asks unless exactly one record fits |
| Step |
|---|
| 1. Read the angle and the offer |
| 2. Write five headlines and texts |
| 3. Make the images |
| 4. Make the clip from the best image |
| 5. Record the voiceover |
| 6. Save them as Variants |
The amounts live once, on the hub (approvals).
Who does each step, and how it is checked
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. Read the angle and the offer | code | Code | Code checks each result against its declared type | measured | $0.angle, |
| 2. Write five headlines and texts | agentic | The Paid media agent | A second model grades the output | graded | $1, |
| 3. Make the images | code | Code | Adversarial review by a second model: Pass, or cut: an unsourced claim, an off-Brand image, text over the site’s limit | measured | $2, |
| 4. Make the clip from the best image | code | Code | Code reads the result back from where it landed | measured | $3.passed |
| 5. Record the voiceover | code | Code | Code reads the result back from where it landed | measured | $4.clip, |
| 6. Save them as Variants | code | Code | Code reads the result back from where it landed | measured | $3.passed, |
The doer is never the checker, and the weakest assurance on the path is the whole call’s.
Under the hood
Under the hood: the Systems, Nouns and Events it touches
| Kind | What it touches |
|---|---|
| Systems | Media, sites and content |
| Nouns | Collection, Content, File, Action, Video |
| Events | Collection.queried, Content.drafted, File.generated, Action.evaluated, Video.drafted |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
offer | Offer | Required | The offer the ads sell. |
angle | string | Required | The angle, from message test’s winner or in your words: “renewals you never miss”. |
formats | ('feed' | 'story' | 'search')[] | ['feed', | The placements to size for: 1080×1080 feed, 1080×1920 story, and search text only. |
images | integer | 5 | Images made; each must pass the brand check. |
clip | boolean | true | Make one 6-second clip from the best image that passed. |
maxCost | number | 1.00 | The most this call spends each time it runs, in USD. It stops before a step would cross it and returns what it has, with a resume link. |
Emits Ad.made. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).