URL: /capabilities/find-customers/buying-trigger-feed · Id: `page:/capabilities/find-customers/buying-trigger-feed`

# Buying-trigger feed

> Each week, the companies in your customer sentence that raised money, posted the role you replace, or dropped the tool you replace.

Each week, the companies in your customer sentence that raised money, posted the role you replace, or dropped the tool you replace.

```text title="Run it · Ask"
Tell me which property managers in my ICP started hiring a leasing coordinator this week, spend up to $2, and show me the plan first.
```

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

const result = await $.try.ICP.track({ goal: 'Tell me which property managers in my ICP started hiring a leasing coordinator this week, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })
```

```json title="Run it · Response"
{
  "mode": "try",
  "stored": false,
  "estimate": "About $1.55 a week",
  "sample": {
    "company": "Cedar Hollow Rentals",
    "trigger": "hiring",
    "detail": "Leasing coordinator, posted 2026-10-24",
    "in your ICP": "yes",
    "added": "account Audience"
  }
}
```

- **Kind:** Workflow
- **Price:** $1.55 per week
- **Your time:** 7 minutes
- **Approval:** Runs on its own
- **Deepens:** [`process:outbound`](https://startupbuilder.dev/workflows/processes/outbound)

## What you get
- **1 week** · Each week, the companies in your customer sentence that raised money, posted the role you replace, or dropped the tool you replace. · [`noun:audience`](https://startupbuilder.dev/business/nouns/audience)
- `added[]` · Each company added this week, with `whyNow`: the Event and its detail, such as “Leasing coordinator, posted 2026-10-24”.
- `updated[]` · Companies already on the Audience with a new trigger; never added twice.
- `skipped` · Trigger hits outside your customer sentence, by reason and count.

**How long:** About 5 minutes.

## Trigger
It runs weekly, as three Monitors with `change` set to funding, hiring and technology.

```ts
every('mon 06:00', ($) => $.ICP.track({ icp: 'icps/icp_4', changes: ['funding', 'hiring', 'technology'] }))
```

## 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 week’s funding rounds | [`Organization.find`](/code/data/organizations/find) | Companies in your sentence that announced a round in the last 7 days | About 80¢ | Runs on its own |
| 2. Read the week’s job posts for your roles | [`Organization.find`](/code/data/organizations/find) | Companies in your sentence that posted the role you replace | About 25¢ | Runs on its own |
| 3. Read what changed at the watched accounts | [`Organization.enrich`](/code/data/organizations/enrich) | Technology adopted or dropped at 50 accounts, oldest-read first | About 50¢ | Runs on its own |
| 4. Keep the hits that match your sentence | `Collection.query` | The hits that pass every filter in your sentence, by a declared rule | Free | Runs on its own |
| 5. Add them to the account Audience with why now | [`Audience.create`](/code/apps/ads/create-audience) | Rows added or updated, each emitting its Event so a Play’s Trigger can act on it | Free | Runs on its own |

Calls: `Organization.find`, then `Organization.enrich`, then `Collection.query`, then `Audience.create`.

About $1.55 a week, each time it runs, capped at $2.00.

## Example: give it a goal
> Tell me which property managers in my ICP started hiring a leasing coordinator this week, spend up to $2, and show me the plan first.

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

## When something fails

| When | Then |
| --- | --- |
| A trigger source fails on every route | That change kind is skipped for the week with a note; the other two still land. |
| A company already on the Audience fires again | Its row is updated with the new `whyNow`, never added twice. |
| Fewer than 10 hiring hits match in goal mode | The agent may widen to adjacent titles, such as “assistant property manager”, as a Decision with its cost; it never widens your sentence. |
| You call it twice in one ISO week | The second call returns the week’s hits at $0: it is idempotent per ICP, change and week. |

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-icp-track-1-<step>-<item>`.

## Change it
| Ask | Compiles to |
| --- | --- |
| Write to the operations lead at each new hit within a day. | [`Prospect.reach`](/capabilities/reach-out/personal-first-touch) |
| Also watch for property managers hiring an assistant property manager. | [`ICP.track`](/capabilities/find-customers/buying-trigger-feed) |
| Only tell me about funding rounds over $1M. | [`ICP.track`](/capabilities/find-customers/buying-trigger-feed) |

```ts
import { $, workflow } from 'startup-builder'

export default workflow({
  name: 'icp-track',
  verb: 'ICP.track',
  input: { icp: 'string', changes: "('funding' | 'hiring' | 'technology')[]", roles: 'string[]' },
  trigger: { schedule: 'mon 06:00' },
  maxCost: 2,
  execute: async (ctx, { icp, changes = ['funding', 'hiring', 'technology'], roles = ['leasing coordinator'], watch = 'audiences/audience_accounts' }) => {
    const hits = []
    if (changes.includes('funding')) hits.push(...(await ctx.step('funding', () => $.Organization.find({ icp, where: { event: { kind: 'funded', within: '7d' } }, maxCost: 1 }))).organizations)
    if (changes.includes('hiring')) hits.push(...(await ctx.step('hiring', () => $.Organization.find({ icp, where: { hiringFor: roles, within: '7d' }, maxCost: 0.3 }))).organizations)
    if (changes.includes('technology')) hits.push(...(await ctx.step('technology', () => $.Organization.enrich({ audience: watch, fields: ['events'], limit: 50, maxCost: 0.6 }))).changed)
    return ctx.step('append', () => $.Audience.create({ name: 'Buying triggers', members: matchIcp(hits, icp), merge: true }))   // emits each Event
  },
})
```

Extend it with `extends: 'ICP.track'` and `base()`, or [compose your own](/code/compose) from these steps.

## Works with
**Plays that order it:** [lead reactivation](/plays/lead-reactivation), [Moment outreach](/plays/moment-outreach), [Objection watch](/plays/objection-watch).

It serves [Find your first customers](/capabilities/find-customers), and runs in [Hands-on](/strategy/hands-on). It deepens [`process:outbound`](https://startupbuilder.dev/workflows/processes/outbound).

## For agents

| Fact | Value |
| --- | --- |
| Guide | `ICP.track` |
| Guide approval | `limits` |
| Goal | Tell me which property managers in my ICP started hiring a leasing coordinator this week, 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 | `every('mon 06:00', ($) => $.ICP.track({ icp: 'icps/icp_4', changes: ['funding', 'hiring', 'technology'] }))` |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | `leaseline-icp-track-1` |
| Lookups | A `find:` input asks unless exactly one record fits |

| Step | Action class | Seat rule that would hold it |
| --- | --- | --- |
| 1. Read the week’s funding rounds | None | None. |
| 2. Read the week’s job posts for your roles | None | None. |
| 3. Read what changed at the watched accounts | None | None. |
| 4. Keep the hits that match your sentence | None | None. |
| 5. Add them to the account Audience with why now | None | None. |

The amounts live once, on the hub ([approvals](https://startupbuilder.dev/agents/approvals)).

| Step | Function | Doer | Checker | Assurance | Inputs |
| --- | --- | --- | --- | --- | --- |
| 1. Read the week’s funding rounds | `code` | Code | Code checks each result against its declared type | `measured` | `$0.icp` |
| 2. Read the week’s job posts for your roles | `code` | Code | Code checks each result against its declared type | `measured` | `$0.icp, $0.roles` |
| 3. Read what changed at the watched accounts | `code` | Code | Code checks each result against its declared type | `measured` | `$0.watch` |
| 4. Keep the hits that match your sentence | `code` | Code | Code checks each result against its declared type | `measured` | `$1, $2, $3, $0.icp` |
| 5. Add them to the account Audience with why now | `code` | Code | Code reads the result back from where it landed | `measured` | `$4.hits` |

The doer is never the checker, and the weakest assurance on the path is the whole call’s.

## Under the hood

| Kind | What it touches |
| --- | --- |
| Systems | [Ads](/code/apps/ads) |
| Nouns | [`noun:organization`](https://startupbuilder.dev/business/nouns/organization), [`noun:collection`](https://startupbuilder.dev/business/nouns/collection), [`noun:audience`](https://startupbuilder.dev/business/nouns/audience) |
| Events | `Collection.queried`, `Audience.created` |

## Reference

| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `icp` | `ICP` | Required | Your saved customer sentence. |
| `changes` | `('funding' \| 'hiring' \| 'technology')[]` | `['funding', 'hiring', 'technology']` | The buying triggers to watch. |
| `roles` | `string[]` | `['leasing coordinator']` | The job titles whose posting means the pain is growing: the role your product saves. |
| `watch` | `Audience` | `your account Audience` | The accounts read for technology changes each week, 50 at a time, oldest-read first. |
| `every` | `string` | `'7d'` | How often the feed reads. |
| `maxCost` | `number` | `2.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 `ICP.tracked`. Refuses with [`max-cost`](/code/refusals#max-cost) before it spends, and with the Data calls and Accounts groups ([refusals](/code/refusals)).