Skip to content

Listing check

Every week, each live listing read back: still up, still linking to you, and still followed, with what changed since you submitted it.

Kind
Workflow
Price
0.8¢ per 40 listings
Your time
2 minutes
Approval
Runs on its own
Deepens
Launches
Post.check
You, to your agent

Check that all my directory listings are still up and followed, spend up to 50¢, and show me the plan first.

curl -X POST "https://api.sb/leaseline.app/posts/check?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"goal":"Check that all my directory listings are still up and followed, spend up to 50¢, and show me the plan first.","budget":{"amount":0.5,"currency":"USD"}}'
npx startup-builder do Post.check --try --data '{"goal":"Check that all my directory listings are still up and followed, spend up to 50¢, and show me the plan first.","budget":{"amount":0.5,"currency":"USD"}}'
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Post.check({ goal: 'Check that all my directory listings are still up and followed, spend up to 50¢, and show me the plan first.', budget: { amount: 0.5, currency: 'USD' } })"
  }
}
import { $ } from 'startup-builder'

const result = await $.try.Post.check({ goal: 'Check that all my directory listings are still up and followed, spend up to 50¢, and show me the plan first.', budget: { amount: 0.5, currency: 'USD' } })
Response
{
  "mode": "try",
  "stored": false,
  "estimate": "About 0.8¢ per 40 listings",
  "sample": {
    "listing": "SaaSHub",
    "field": "rel",
    "was": "follow",
    "now": "nofollow",
    "since": "2026-11-02"
  }
}
Try mode reads, stores nothing and spends nothingAPI referenceIts Nouns

What you get

  • 40 listings · Every week, each live listing read back: still up, still linking to you, and still followed, with what changed since you submitted it. · Post
  • checked · Listings read this week.
  • live · Listings up and linking to you.
  • drifted[] · Each change: the link’s rel (follow to nofollow), the URL, the name, or gone, with what it was and what it is now.

How long: About 30 seconds.

Trigger

It runs weekly, and daily from Directory submissions until each new listing is live.

TypeScript
every.week(($) => $.Post.check({ posts: 'listings' }))

Steps

Your agent does these steps, one row per step; each step’s check is part of its row and its price.

StepVerbReturnsPriceApproval
1. List the live listingsCollection.queryEach listing’s URL and the fields you submittedFreeRuns on its own
2. Read each listing pagePage.auditThe live page: your link, its rel, the name and the descriptionAbout 0.8¢Runs on its own
3. Compare with what you submittedCollection.queryA drift row for each changed field; a listing missing two weeks running becomes rejectedFreeRuns on its own
4. Record each driftPost.updateThe post updated with the change, whose Event any Trigger can act onFreeRuns on its own

Calls: Collection.query, then Page.audit, then Collection.query, then Post.update.

About 0.8¢ per 40 listings, each time it runs, capped at 10¢.

Example: give it a goal

Check that all my directory listings are still up and followed, spend up to 50¢, and show me the plan first.

The plan it shows runs 4 steps, priced before it spends anything; Run it above shows the result it returns.

When something fails

Each failure, and what happens next
WhenThen
A listing page cannot be readIt is retried the next day, not marked gone; one bad read never reports a lost link.
A listing is missing two weeks runningIt becomes rejected with a task to resubmit.
You call it twice in one dayThe second call returns the stored reading at $0: it is idempotent per post and day.

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-post-check-1-<step>-<item>.

Change it

AskCompiles to
Resubmit any listing that has been gone for two weeks.Post.submit
Tell me the day a followed link turns nofollow.Monitor.create
Check my launch posts too, not only directories.Post.check
TypeScript
import { $, workflow } from 'startup-builder'

export default workflow({
  name: 'listing-drift-check',
  verb: 'Post.check',
  input: { posts: "'listings' | string[]" },
  trigger: { schedule: 'weekly' },
  maxCost: 0.1,
  execute: async (ctx, { posts = 'listings' }) => {
    const list = await ctx.step('list', () => $.Collection.query({ from: 'post', where: posts === 'listings' ? "kind = 'listing' and status = 'published'" : `$id in ${posts}` }))
    const read = await ctx.step('read', () => $.Page.audit({ urls: list.map((p) => p.url), checks: ['links'], maxCost: 0.02 }))
    const drifted = diffListings(list, read)                                  // the fields you submitted: a declared rule
    if (drifted.length) await ctx.step('record', () => $.Post.update({ changes: drifted }))
    return { checked: list.length, live: read.filter((r) => r.linksTo).length, drifted }
  },
})

Extend it with extends: 'Post.check' and base(), or compose your own from these steps.

Works with

Plays that order it: Agent registries, Directory listings, Marketplace listings.

It serves Earn first links, and runs in Launch and search, Borrowed. It deepens Launches.

For agents

For agents: the guide, its approvals, retries and lookups
FactValue
GuidePost.check
Guide approvallimits
GoalCheck that all my directory listings are still up and followed, spend up to 50¢, 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.
Triggerevery.week(($) => $.Post.check({ posts: 'listings' }))
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-post-check-1
LookupsA find: input asks unless exactly one record fits
Step
1. List the live listings
2. Read each listing page
3. Compare with what you submitted
4. Record each drift

The amounts live once, on the hub (approvals).

Who does each step, and how it is checked
StepFunctionDoerCheckerAssuranceInputs
1. List the live listingscodeCodeCode checks each result against its declared typemeasured$0.posts
2. Read each listing pagecodeCodeCode checks each result against its declared typemeasured$1.urls
3. Compare with what you submittedcodeCodeCode checks each result against its declared typemeasured$1, $2
4. Record each driftcodeCodeCode reads the result back from where it landedmeasured$3.drifted

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
KindWhat it touches
SystemsSocial
NounsCollection, Page, Post
EventsCollection.queried, Post.updated

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
posts'listings' | string[]'listings'Every live listing, or the posts you name.
secondbooleantrueRead a page that looks gone a second time, on another route, before reporting it.
maxCostnumber0.10The 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 Post.checked. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).

Contents