Skip to content

Retargeting

Shows Ads only to your own past visitors and signups, stops when they convert, and holds a share back to price every dollar.

Channel
Ads and sponsorships
Run oneMode try
You, to your agent

Hand our latest Visit that ended without a signup to the paid media agent and show me what it chose and why. Don’t spend.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Visit.ended%22%2C%20%22Checkout.abandoned%22%2C%20%22Contact.stalled%22%2C%20%22Registration.missed%22%2C%20%22Watch.replayed%22%2C%20%22Customer.converted%22%2C%20%22Consent.withdrawn%22%2C%20%22Audience.resized%22%2C%20%22Experiment.matured%22)&limit=1" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

# Not live yet (gaps #7)
curl -X POST "https://api.sb/leaseline.app/triggers/fire?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"like":"event_8211"}'

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22visitor_903%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22visitor_903%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
import { $ } from 'startup-builder'

const { events: [last] } = await $.Event.list({ where: 'name in ("Visit.ended", "Checkout.abandoned", "Contact.stalled", "Registration.missed", "Watch.replayed", "Customer.converted", "Consent.withdrawn", "Audience.resized", "Experiment.matured")', limit: 1 })   // { play } compiled to this Play’s Trigger Events
const { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' })   // the same Event again, in try mode · Not live yet, gaps #7
const { decisions: [decision] } = await $.Decision.list({ where: `on = "${subject}"` })   // what it chose, and why · Not live yet, gaps #7
const { actions } = await $.Action.list({ where: `on = "${subject}"` })   // what it did, with its checks · Not live yet, gaps #7
return { decision, actions }
npx startup-builder do Event.list --json '{"where":"name in (\"Visit.ended\", \"Checkout.abandoned\", \"Contact.stalled\", \"Registration.missed\", \"Watch.replayed\", \"Customer.converted\", \"Consent.withdrawn\", \"Audience.resized\", \"Experiment.matured\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_8211"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"visitor_903\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"visitor_903\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Visit.ended\", \"Checkout.abandoned\", \"Contact.stalled\", \"Registration.missed\", \"Watch.replayed\", \"Customer.converted\", \"Consent.withdrawn\", \"Audience.resized\", \"Experiment.matured\")', limit: 1 })   // { play } compiled to this Play’s Trigger Events\nconst { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' })   // the same Event again, in try mode · Not live yet, gaps #7\nconst { decisions: [decision] } = await $.Decision.list({ where: `on = \"${subject}\"` })   // what it chose, and why · Not live yet, gaps #7\nconst { actions } = await $.Action.list({ where: `on = \"${subject}\"` })   // what it did, with its checks · Not live yet, gaps #7\nreturn { decision, actions }",
    "mode": "try"
  }
}
Response200application/json
{
  "decision": {
    "$id": "https://api.sb/leaseline.app/decisions/decision_701",
    "$type": "Decision",
    "choice": "retargeting-audiences",
    "reason": "a Visit that ended at the pricing Page with no signup, from a Visitor with advertising Consent, outside the standing holdout: the 0–7 day window",
    "stakes": "low",
    "options": [
      "retargeting-audiences",
      "audience-split-holdout",
      "ad-tag-consent-gate",
      "retargeting-pacing",
      "retargeting-creative"
    ],
  …
sample · 4 Not live yetPreview

On PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim

action_1 keeps this Visitor out of the 20 % standing holdout. action_2 adds them to the 0–7 day window at the next hourly upload, where the Ads say nothing about the Visit itself.

+6.5 added trials per $100 · ■■□□ Early sign · n 1,840 · path → arr

Try another: the VisitChoiceReason
A Denver Visit to pricing, 2 min, no signup, Consent givenretargeting-audiencesthe 0–7 day window
A returning Visitor drawn into the standing holdout on their first Visitaudience-split-holdoutheld out of every paid Campaign
A Visit with a browser opt-out signalad-tag-consent-gateno Venue tag loads
Started a trial 3 h after an Ad clickretargeting-pacinga converter leaves every window at once

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Retargeting trial lift → Deals and trials opened → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +6.5 trials per $100 × $280 a month × 0.15 trials to paid (your last 90 days) × $347 Pro ARR ≈ +$947. Under First signal: About 7 weeks to tell +5 trials per $100 at 1,000 Visitors a month with 20 % held out; at 1,000 in 90 days, about 14 weeks.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Visit.ended · first Audience member added 3 min after the Visit · 8 keys Not live yet

Start it this week

Terminal
npx startup-builder add play retargeting   # 1 · writes plays/retargeting.mdx (Not live yet, gaps #5)

# 2 · dry run; below, its 422 on 2026-08-24
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"retargeting"}'

# 3 · keep it, at You approve each one
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"retargeting"}'

# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/retargeting \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":null}'

# off: every Campaign pauses through the proxy
curl -X PATCH https://api.sb/leaseline.app/campaigns/retargeting \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
422Refusedretargeting can’t start yet: 1 check fails and 1 warns.
CheckFixThen
use-when pass · Not live yet, gaps #8Keep 1,000 or more own Visitors in 90 days, or run editorial-seo first.POST /{namespace}/metrics/query
baseline pass · Not live yet, gaps #8Read it once; it fills Before in Your number.POST /{namespace}/metrics/query
conversion-send failSend one test trial from your server; each Venue stays off until it lands.POST /{namespace}/integrations/{integration}/call
role-unstaffed warn · Not live yet, gaps #8Staff the Marketing Lead, or leave it: approvals and the kill switch come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "retargeting can’t start yet: 1 check fails and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "1,840 past Visitors and Contacts in 90 days",
      "doc": "/plays/retargeting#check-use-when",
      "fix": "Keep 1,000 or more own Visitors in 90 days, or run editorial-seo first.",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "2.2 trials per 100 own Visitors with no Ads, n 1,610 in 90 days",
      "doc": "/plays/retargeting#check-baseline",
      "fix": "Read it once; it fills Before in Your number.",
      "then": "Metric.query"
    },
    {
      "check": "conversion-send",
      "status": "fail",
      "observed": "no server-side trial has reached Facebook or Google Display",
      "doc": "/plays/retargeting#check-conversion-send",
      "fix": "Send one test trial from your server; each Venue stays off until it lands.",
      "then": "Integration.call"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "marketing-lead"
      ],
      "owned": 0,
      "doc": "/plays/retargeting#check-role-unstaffed",
      "fix": "Staff the Marketing Lead, or leave it: approvals and the kill switch come to you.",
      "then": "Role.staff"
    }
  ]
}

It says no, says why, and names the fix.

CheckFix (one call)Your timeWait
use-when · baseline · pass: 1,840 own Visitors and Contacts; 2.2 per 100, n 1,610 (gaps #8)POST /{namespace}/metrics/query on retargeting-trial-lift, which fills Before in Your number1 minnone
conversion-send · fail: no test conversion on either VenuePOST /{namespace}/integrations/{integration}/call sends one test trial to each Venue10 minup to 24 h for each Venue to report it
role-unstaffed · warn (gaps #8): no Marketing LeadPOST /{namespace}/roles/{role}/staff, or leave it: approvals come to you0 minnone

Your number · Before 2.2 · Now 6.5 added trials per $100 · Target 7 by Dec 31 · ■■□□ Early sign · n 1,840

The Ads cost what the Venues bill, capped at $280 a month by your Limit: about $15 for each added trial. Hourly Audience uploads and the daily drift read cost about $0.20 a day (Worker cost). At 1,000 Visitors a month the same cap holds.

When it starts

WhenTrigger idStarts onWindowHow it gets in
A Visit ends with no signupretargetable-visitevent on Visit.ended where a Visit the Site earned, ended with no conversion, from a Visitor whose tags may firethe 0–7, 8–30 and 31–90 day windowsyour Site’s tag, after the consent gate (POST /{namespace}/visitors/{visitor}/identify)
A member starts a trialconversion-recordedevent on Customer.converted where the Play's conversion Event for a member, which ends retargeting for themout of every window within 1 hyour server sends the conversion (POST /{namespace}/integrations/{integration}/call)
A browser sends an opt-out signal, or Consent is withdrawnopt-out-signalevent on Consent.withdrawn where a browser opt-out signal or a withdrawn advertising Consenttags stop at once; lists update at the next uploadthe consent gate on every Visit
+ 4 more for checkout exits, stalled signups, missed events and Audience size (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_8211",
  "$type": "Event",
  "name": "Visit.ended",
  "at": "2026-10-19T13:48:20Z",
  "seq": 8211,
  "data": {
    "visitor": "visitor_903",
    "lastPage": "/pricing",
    "seconds": 128,
    "converted": false,
    "consent": {
      "advertising": true
    },
    "optOutSignal": false
  }
}
Reproduce it in test
npx startup-builder trigger Visit.ended --like event_8211   # replays the Visit
npx startup-builder clock advance --by 7d   # moves the member from the 0–7 to the 8–30 day window
npx startup-builder listen Visit.ended --forward-to http://localhost:3000/hook

Who runs it

Paid media · your agent (role:paid-media-manager, worker_7) holds the Key Result. The plan, every new concept and every raise come to you (Dana Okafor, person, worker_1) for approval, because the Marketing Lead Role has no person. It writes to no one; it builds Audiences and Ads. Your number links to Did it work.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Add to a window (retargeting-audiences) · Hold out (audience-split-holdout)It does it1,472 · 368——POST /{namespace}/actions/{action}/redirect
No tags (ad-tag-consent-gate) · Remove a converter (retargeting-pacing)It does it214 · 78—a converter still served after 24 h → converter-leakPOST /{namespace}/actions/{action}/redirect
A new concept (retargeting-creative) · a Limit raiseYou approve each one9 of 9 · 0never: the spend is yours—POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
Change the windows or the holdout shareYou approve each one0 mergednever—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: an Ad that mentions a Visit, a bought list, an Audience widened to keep spendingRefused, every time1 concept refusednever—nothing; the Checks hold it

What it reads · What it may call. It reads your own Visits and Contacts after the consent gate, the Venues’ server-side conversions and spend. No Ad mentions or implies a Visit, a Page or anything else the Person did.

The brief (folded): compiled Agent-register Markdown · play:retargeting@1 · each line names its record key
You hold the paid media Key Result: 7 added trials per $100 by 2026-12-31.       ← worker, metric
On each Trigger, pick the Tactic that acts and say why.                          ← triggers, tactics
Your own Visitors and Contacts only; 20 % held out of every Campaign.            ← your file, line 2; first-party
Three windows: 0–7, 8–30 and 31–90 days; a converter leaves all three.           ← your file, line 4
No Ad mentions a Visit, a Page or a cart.                                        ← copyRules 3
A Price in an Ad equals the Offer’s live Price.                                  ← copyRules 2; price

The slow loop. The weekly Decisions kept the windows and the 20 % holdout, and no Change was merged in the window, so there is no replay or read-back to print. One concept retired on day 30 under its window’s median, a Tactic’s own rule.

Bring your own agent (folded) starts at You approve each one for anything that spends; Audience moves it may make at It does it when it matches the seated agent’s converter removal. POST /{namespace}/roles/{role}/staff seats it.

Terminal
npx startup-builder eval --scenario retargeting-1840 --agent "claude -p" --seat paid-media-manager   # seated: +6.5 trials per $100, 0 converters served past 24 h · yours: +5.9, 2 converters served for 30 h

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
retargeting-audiences retargeting-audiencesa Visit ended with no conversion, Consent given, outside the holdoutYour rule (line 4)PATCH /{namespace}/audiences/{id} into its window at the next hourly uploadnobodythe same
audience-split-holdout audience-split-holdoutthe Visitor was drawn into the 20 % on their first VisitYour rule (line 2)POST /{namespace}/flags/{flag}/evaluate: out of every paid Campaign, for goodnobodythe same
ad-tag-consent-gate ad-tag-consent-gateno advertising Consent, or an opt-out signalYour rule (the consent gate)nothing: no Venue tag loadsnobodythe same
retargeting-pacing retargeting-pacinga member converted, or a window passed 10 a week in frequencyYour rule (the pacing line)PATCH /{namespace}/audiences/{id} removes them; PATCH /{namespace}/ads/{id} holds the windownobodythe same
retargeting-creative retargeting-creativea concept falls under its window’s median for 14 daysAgent’s call, with its reasonPATCH /{namespace}/ads/{id} retires it → POST /{namespace}/ads drafts the next, from approved Pagesyou approve the new concept · Take this oneyou
When they write backFixture replyWhat happensWho answersBy whenCall
Interesteda click to the trial Page from the 0–7 day Adthe trial Page; a trial ends retargeting for them within 1 hnobody1 hPATCH /{namespace}/audiences/{id}
Send me pricinga comment: “How much is it?”the Pricing Page’s list Price, as it standsPaid media · your agent1 hPOST /{namespace}/conversations/{conversation}/answer
Not nowa hide on the Adthat Ad stops for them; they age out at day 90nobodyat oncePATCH /{namespace}/audiences/{id}
Wrong persona comment: “I’m a tenant, why am I seeing this?”a plain answer, and they leave every windowPaid media · your agent1 hPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply to a stalled-signup Ad’s lead formnothing sends by email; the window runs onnobody—POST /{namespace}/messages/{message}/schedule
Question it can’t answera comment: “Does it work for HOAs?”To a person; the comment stays upyou1 business dayPOST /{namespace}/conversations/{conversation}/escalate
Stopan opt-out signal, or “stop following me around”tags stop at once and they leave every list at the next upload (opt-out)nobodyat oncePOST /{namespace}/consents/{consent}/withdraw

When it hands to you

This Play hands no subject to a person: the record names no hand-off. What lands in your queue is approvals: the plan, each new concept and any raise, plus a hard comment now and then. As captured (draft and checks are Not live yet, gaps #7).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_121",
    "$type": "Approval",
    "verdict": "approved",
    "deadline": "2026-08-26T17:00:00Z",
    "draft": "Plan: Facebook and Google Display, 3 windows, 20 % held out, $10 a day, trial started as the conversion",
    "route": "retargeting-plan",
    "checks": [
      {
        "check": "conversion-send",
        "status": "pass"
      },
      {
        "check": "first-party",
        "status": "pass"
      }
    ]
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_744",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-09-23T17:00:00Z",
    "on": "ad_31",
    "brief": {
      "who": "a commenter on the 8–30 day Ad",
      "said": "Does it work for HOAs?",
      "chose": "retargeting-creative",
      "why": "a question on a live concept only you can answer; the comment stays up",
      "never": [
        "hide it"
      ]
    }
  }
]

You answer with POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, POST /{namespace}/tasks/{task}/claim (Take this one) or POST /{namespace}/tasks/{task}/return with a reason, each a one-call window. An Approval unanswered by its deadline expires, and nothing new spends. A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate, and person-promise fires.

On time: you, 11 of 11 within 1 business day, median 2 h, n 11, ■□□□ Too early to tell beside Other Startups’ 93 % on time. Your queue: in week 1, 4 approvals (the plan and 3 concepts); then about 1 concept and 1 comment a week.

The fixed line: POST /{namespace}/deals/{deal}/assign sets owner to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.

When it won’t send

Check (refuses one send)RefusesFixThen
conversion-send (the record’s Conversion send first)a Campaign on a Venue that has not received a test conversion from your server in 24 hSend one test trial from your server; each Venue stays off until it lands.POST /{namespace}/integrations/{integration}/call
opt-out (the record’s Opt-out binds)a tag or a list upload for a browser with an opt-out signal or no ConsentNothing to do: tags stop and the member leaves every list.PATCH /{namespace}/audiences/{id}
first-party (the record’s First-party only)a member from a matched, licensed or bought sourceUse only your own Visitors and Contacts.PATCH /{namespace}/audiences/{id}
scale-gate (the record’s Scale gate)a Limit raise or a second Venue with no winning holdout ReadoutWait for the holdout read, then approve the raise.POST /{namespace}/approvals/{approval}/approve
claims-resolve (hub)an Ad sentence no approved Page backs, or one that mentions what the Person didSay what Leaseline does, from an approved Page; never the Visit.POST /{namespace}/content/draft
price (gaps #1)a Price in an Ad that differs from the Offer’s live PriceQuote the Pricing Page’s live Price.POST /{namespace}/approvals/{approval}/approve
Stop (pauses the Play)Pauses whenThen
conversion-feedno conversion on a Venue for 72 h while spend flowschanges freeze there; at 7 days the Venue pauses
consentany Venue tag request from a test browser with no Consent or with an opt-out signalevery Campaign of the Play pauses
converter-leaka converter still in a window 24 h after convertingthat Campaign pauses until the exclusion holds
audience-flooran Audience under its Venue’s minimum sizeits Campaign pauses; it never widens to keep spending
floor-readafter 30 days and 100 click Visits, retargeted conversion under the Site’s ownthe Play pauses
holdout-losera holdout read marks it a loserthe Play pauses until a Decision names a change
frequencya window’s 7-day frequency above 10that window pauses
spend-limitmonth-to-date spend reaches the Limitthe Play pauses until the month turns (POST /{namespace}/limits/{limit}/raise)
kill-switchyou press itevery Campaign pauses through the proxy; write scope is revoked

The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the concept that ran instead is in Example.

422RefusedThe 0–7 day concept did not run: it says the reader looked at the pricing Page.
CheckFixThen
claims-resolve failSay what Leaseline does, from an approved Page; never the Visit.POST /{namespace}/content/draft
price pass · Not live yet, gaps #1—POST /{namespace}/approvals/{approval}/approve
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The 0–7 day concept did not run: it says the reader looked at the pricing Page.",
  "checks": [
    {
      "check": "claims-resolve",
      "status": "fail",
      "doc": "/plays/retargeting#check-claims-resolve",
      "fix": "Say what Leaseline does, from an approved Page; never the Visit.",
      "then": "Content.draft"
    },
    {
      "check": "price",
      "status": "pass"
    }
  ]
}

It says no, says why, and names the fix.

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "converter-leak",
      "observed": "6 converters still served at 26 h",
      "bound": "24h",
      "window": "Google Display, 0–7 days"
    },
    "doc": "/plays/retargeting#stop-converter-leak",
    "task": "task_731"
  }
]

Did it work

A standing 20 % of your own Visitors and Contacts was held out of every Campaign at random on their first Visit (rules m–o); each figure’s call is POST /{namespace}/metrics/query by node and by arm, and every node above the leaf is Not live yet (gaps #4).

+6.5 added trials per $100 · ■■□□ Early sign · n 1,840 · path → arr

Chose
retargeting-audiences
Why
a Visit that ended at the pricing Page with no signup, from a Visitor with advertising Consent, outside the standing holdout: the 0–7 day window
From
retargeting-audiences · audience-split-holdout · ad-tag-consent-gate · retargeting-pacing · retargeting-creative
Expects
a trial started within 30d, or the member ages out at day 90
Reopens when
Customer.converted for visitor_903, or 90d since the last Visit
By
worker_7
JSON
{
  "$id": "decisions/decision_701",
  "$type": "Decision",
  "choice": "retargeting-audiences",
  "reason": "a Visit that ended at the pricing Page with no signup, from a Visitor with advertising Consent, outside the standing holdout: the 0–7 day window",
  "stakes": "low",
  "options": [
    "retargeting-audiences",
    "audience-split-holdout",
    "ad-tag-consent-gate",
    "retargeting-pacing",
    "retargeting-creative"
  ],
  "by": "worker_7",
  "on": "visitor_903",
  "expected": "a trial started within 30d, or the member ages out at day 90",
  "reopensWhen": "Customer.converted for visitor_903, or 90d since the last Visit"
}
NodeAgent half (served, 80 %)Your half (held out, 20 %)AddedConfidenceBasis
retargeting-trial-lift66 trials of 1,472 (4.5 per 100) on $5208 of 368 (2.2 per 100)+6.5 trials per $100 [1.7, 11.4]■■□□ Early signheld back at random · n 1,840 · 212 still in their 30-day window
opened-deals-and-trials5.3 per 100 (78 of 1,472)3.3 (12 of 368)+2.0 per 100 [−0.1, 4.2] · Not carrying up yet■□□□ Too early to tellthe same assignment
new-arr ↑$3,470, 10 Customers on Pro · Booked: credited, not all caused by this Play$347, 1 Customer (a quarter the size)+$2,082 [−$1,379, +$5,543] · Worth about +$950 a month■□□□ Too early to tell (10 vs 1 Customers)the same · 14 trials still deciding
net-new-arr · arr+$3,470+$347+$2,082, carried: it moves no other bridge term■□□□ Too early to tellcarried from new-arr

The leaf counts trials started within 30 days of a member’s last Visit, served against held out, per $100 of spend, over everyone assigned once the consent gate ran (rule m), to 300 matured in the held-out 20 %; at 368 assigned, 156 have matured, so the Experiment runs on.

How it moves it: served members saw a concept from approved Pages while still inside their 0–30 day window, at a 7-day frequency of 4.1, and none after converting past the first 24 h.

Guardrails: served against held out: paid per trial, 0.13 against 0.08 (new-paying-customers per trial), and refunds on retargeted trials, 0 of 10 against 0 of 1.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/retargeting",
  "$type": "Campaign",
  "play": "play:retargeting@1",
  "path": [
    "retargeting-trial-lift",
    "opened-deals-and-trials",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "retargeting-trial-lift",
    "unit": "ratio",
    "heldBack": 368,
    "decision": null
  },
  "readout": {
    "value": 6.5,
    "n": 1840,
    "maturing": 212,
    "interval": [
      1.7,
      11.4
    ],
    "readability": "C",
    "basis": "held-back",
    "pooled": false
  },
  "ledger": {
    "spend": 520,
    "workerCost": 11,
    "hours": 1.8,
    "pipelineArr": 0,
    "wonArr": 3470,
    "projectedArr": 947,
    "paybackDays": 55
  }
}

Outside benchmark: this Play’s catalog record carries no sourced range yet, so Other Startups’ results stand in: +4.8 added trials per $100, ■■■□ Likely, n 22,400 Visitors.

Before it · After it. Before it: programmatic-seo, editorial-seo and promote-winners bring the Visitors. After it: product-led and instant-response take the trials and forms it brings back, and every converter leaves its windows.

Example

Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days) on Facebook and Google Display at $10 a day: 2,054 Visitors and Contacts entered, 214 had no tags (no Consent or an opt-out signal), and 1,840 were assigned, 1,472 served and 368 held out. Read every captured Ad and comment at ?threads=all; three follow the tables.

What happened, by TacticDecisionsPeopleTrials (counts)
Add to a window1,4720–7: 611 · 8–30: 538 · 31–90: 32341 · 19 · 6
Hold out · No tags · Remove a converter368 · 214 · 78—8 in the held-out 20 %
A new concept9 drafted, 1 refused, 8 ran1 retired on day 30—
Served · held out1,472 · 368$520 · $066 · 8
At 1,000 Visitors a month (the Use it when volume)about 1,870 in 56 daysheld back at random, 20 %Other Startups’ results: +4.8 per $100, ■■■□ Likely, n 22,400
DayEventWhat happenedYour time
0event_8002 Campaign.createdthe three steps; the dry run fails conversion-send15 min
1event_8009 Integration.calledone test trial reached both Venues from your server10 min
2event_8014 Approval.approvedyou approved the plan and 3 concepts9 min
3event_8031 Offer.trialedthe first trial from a retargeted click—
19event_8102 Campaign.pausedconverter-leak on Google Display: 6 converters at 26 h; the exclusion fixed in 2 h6 min
30event_8140 Ad.updatedthe 31–90 day concept retired under its window’s median; you approved the next4 min
1–56event_8012 Approval.created, first of 10you approved 9 concepts; claims-resolve refused 1, rewritten once and run; weekly reviews1 h 16 min · total 1.8 h measured, 1.7 h asked

The 0–7 day Ad, Facebook, Sep 3 · Every renewal notice, 60 days out, for every owner you manage. Try Leaseline free for 14 days.¹

A Denver manager, served, Sep 4 · clicked, started a trial, and left every window within the hour. Paid for Pro on Sep 18: +$347 ARR, credited to the Ad’s coded link.²

The 0–7 day concept, 2026-08-26 · the draft claims-resolve refused, then the concept that ran
- Still looking at our pricing page? Your renewals won’t wait.
+ Every renewal notice, 60 days out, for every owner you manage. Try Leaseline free for 14 days.

A commenter on the 8–30 day Ad, 2026-09-22 16:40 · “Does it work for HOAs?” · Leaseline, 16:42 · *Thanks for asking. Dana, our founder, answers this one herself, here, by tomorrow 17:00.*³ The comment stays up; the Brief went to you (task_744), and you replied at 09:15.

¹ What Leaseline does, from an approved Page, and nothing about the Visit. ² Credit by the coded link; the conversion came from your server, never the Venue’s own count. ³ The routed line, compiled from the Role’s Settings.

Your settings

plays/retargeting.mdx
---
holdout: 0.2      # a fifth of your own Visitors and Contacts never see an Ad
budget: $10/day
---
Windows: 0–7, 8–30 and 31–90 days since the last Visit. A trial started ends it.
Text
$ npx startup-builder show plays/retargeting
holdout 0.2 · budget $10/day · windows 0–7, 8–30, 31–90 · trial ends it     your file  plays/retargeting.mdx:2,3,5 (Ask 2026-08-24)
worker role:paid-media-manager, owns never · chooses none (D38): the Decision picks a Tactic · approval every-send · checks conversion-send, opt-out, first-party, scale-gate, claims-resolve, price · stops 9 · placements facebook, instagram, google-display, youtube, linkedin    catalog  play:retargeting@1 (Not live yet, gaps #5)

Every key from the catalog

KeyIdWhat it is
Tacticad-tag-consent-gateAd tag consent gate decides on every Visit which Venue tags load, by the Visitor’s Market, Consent and opt-out signal, before any tag fires. Starts at Sends nothing.
Tacticretargeting-planRetargeting plan fixes the conversion Event, tests each Venue’s conversion send, and queues Venues, member rule, exclusions and budgets for one approval. Starts at You approve each one.
Tacticretargeting-audiencesRetargeting audiences builds three exclusive window Audiences per Venue and first-party Contact lists, and removes converters, opt-outs and aged-out members. Starts at Sends nothing.
Tacticretargeting-creativeRetargeting creative drafts three concepts per window from the claims source, screens them, and retires a concept that falls under its window’s median. Starts at You approve each one.
Tacticretargeting-pacingRetargeting pacing holds spend and frequency inside each window and pauses any Campaign that keeps serving a converter. Starts at Sends nothing.
Tacticaudience-split-holdoutAudience split holdout keeps a standing share of the Startup’s own Visitors and Contacts out of every paid Campaign, and prices the lift per $100. Starts at You approve each one.
Tacticretargeting-floorRetargeting floor pauses the Play when Visits from retargeting clicks convert below the Site’s own rate after 30 days. Starts at Sends nothing.
Tacticad-write-proxyAd write proxy is the only write path to an ads Venue, holding the token outside the model and forcing every create to paused. Starts at You approve each one.
Tacticad-drift-monitorAd drift monitor diffs every live Ad’s rendered creative, text and enhancement settings against the approved source daily, and pauses or resets what drifted. Starts at Sends nothing.
Tacticincrementality-holdoutIncrementality holdout measures the first Transactions a paid arm adds, by a geo holdout or a day switchback, before any scale step. Starts at You approve each one.
Triggerretargetable-visitRetargetable visit: event on Visit.ended, window 90d.
Triggercheckout-exitCheckout exit: state on Checkout.abandoned, window 7d.
Triggerstalled-signupStalled signup: state on Contact.stalled, window 30d.
Triggerevent-no-showEvent no-show: state on Registration.missed, window 30d.
Triggerreplay-watchedReplay watched: threshold on Watch.replayed, window 14d.
Triggerconversion-recordedConversion recorded: event on Customer.converted, window 1d.
Triggeropt-out-signalOpt-out signal: event on Consent.withdrawn, window 1d.
Triggeraudience-floor-crossedAudience floor crossed: threshold on Audience.resized, window 7d.
Triggerholdout-closedHoldout closed: event on Experiment.matured, window 7d.

Compiled, not written: edit the Play record retargeting and its Leaseline run, then regenerate. Every step runs from a sentence or a line of code. In your words: the CRM words you know, and where each lives here.