Skip to content

Who is visiting

A visit to your pricing page in, the company behind it out, checked against your customer sentence and added to your accounts when it fits.

Kind
Workflow
Price
1.2¢ per visit
Your time
4 minutes
Approval
Runs on its own
Visitor.match
You, to your agent

Tell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, and show me the plan first.

curl -X POST "https://api.sb/leaseline.app/visitors/match?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"goal":"Tell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, and show me the plan first.","budget":{"amount":1,"currency":"USD"}}'
npx startup-builder do Visitor.match --try --data '{"goal":"Tell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, and show me the plan first.","budget":{"amount":1,"currency":"USD"}}'
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Visitor.match({ goal: 'Tell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })"
  }
}
import { $ } from 'startup-builder'

const result = await $.try.Visitor.match({ goal: 'Tell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })
Response
{
  "mode": "try",
  "stored": false,
  "estimate": "About 1.2¢ a visit",
  "sample": {
    "visit": "/pricing, twice",
    "company": "Harbor Point Residential",
    "fit": "strong",
    "added": "account Audience",
    "why now": "visited /pricing twice this week"
  }
}
Try mode reads, stores nothing and spends nothingAPI referenceIts Nouns

What you get

  • 1 visit · A visit to your pricing page in, the company behind it out, checked against your customer sentence and added to your accounts when it fits. · Visitor
  • organization · The company behind the visit: hostname, name and fit (strong, possible or none), or nothing when the visit came from a home or mobile network.
  • added · The account Audience it joined, with whyNow, such as “visited /pricing twice this week”.
  • person · Empty until the visitor signs up; Visitor.identify joins them then.

Connect once: Google Analytics. How long: Under 5 seconds.

Trigger

It runs on each new session on a buying page, from a visitor who is not a customer and not a bot.

TypeScript
on.Visit.created((visit, $) => ['/pricing', '/compare', '/demo'].includes(visit.path) && $.Visitor.match({ visitor: visit.visitor }))

Steps

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

StepVerbReturnsPriceApproval
1. Find the company behind the visitOrganization.resolveThe company’s hostname, or no match; a miss is freeAbout 1¢Runs on its own
2. Read the company’s profileOrganization.enrichHeadcount, location and industryAbout 0.2¢Runs on its own
3. Check it against your customer sentenceCollection.querystrong, possible or none, by the declared ruleFreeRuns on its own
4. Add a strong fit to your accountsAudience.updateThe company on the account Audience, with why nowFreeRuns on its own

Calls: Organization.resolve, then Organization.enrich, then Collection.query, then Audience.update.

About 1.2¢ a visit, each time it runs, capped at 5¢.

Example: give it a goal

Tell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, 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
The visit came from a home, mobile or VPN networkNo company resolves, the visit keeps no row, and the miss costs nothing.
The company is already a customerIt is skipped before any read: the trigger ignores customers.
One visitor comes back the same dayThe stored match is returned at $0: it is idempotent per visitor 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-visitor-match-1-<step>-<item>.

Change it

AskCompiles to
Write to the operations lead at each strong fit within the hour.Prospect.reach
Show my ads to the companies that visited pricing.Integration.sync
Resolve visits to my blog posts too.Visitor.match
TypeScript
import { $, workflow } from 'startup-builder'

export default workflow({
  name: 'visitor-match',
  verb: 'Visitor.match',
  input: { visitor: 'string' },
  trigger: { event: 'Visit.created' },
  maxCost: 0.05,
  execute: async (ctx, { visitor, icp = 'icps/icp_4' }) => {
    const org = await ctx.step('resolve', () => $.Organization.resolve({ visitor, maxCost: 0.01 }))
    if (!org.match) return { visitor }
    const profile = await ctx.step('profile', () => $.Organization.enrich({ hostname: org.match.hostname, fields: ['profile'], maxCost: 0.01 }))
    const fit = fitOf(profile, icp)                                                          // the declared ICP rule
    if (fit !== 'strong') return { visitor, organization: { ...org.match, fit } }
    return ctx.step('add', () => $.Audience.update({ id: 'audiences/audience_accounts', add: [org.match], whyNow: 'visited /pricing' }))
  },
})

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

Works with

Plays that order it: Account visits, Retargeting.

It serves Convert visitors, and runs in Paid. It deepens Inbound sales.

For agents

For agents: the guide, its approvals, retries and lookups
FactValue
GuideVisitor.match
Guide approvallimits
GoalTell me which companies visited my pricing page twice this week and fit my ICP, spend up to $1, 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.
Triggeron.Visit.created((visit, $) => ['/pricing', '/compare', '/demo'].includes(visit.path) && $.Visitor.match({ visitor: visit.visitor }))
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-visitor-match-1
LookupsA find: input asks unless exactly one record fits
Step
1. Find the company behind the visit
2. Read the company’s profile
3. Check it against your customer sentence
4. Add a strong fit to your accounts

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

Who does each step, and how it is checked
StepFunctionDoerCheckerAssuranceInputs
1. Find the company behind the visitcodeCodeCode checks each result against its declared typemeasured$0.visitor
2. Read the company’s profilecodeCodeCode checks each result against its declared typemeasured$1.hostname
3. Check it against your customer sentencecodeCodeCode checks each result against its declared typemeasured$2, $0.icp
4. Add a strong fit to your accountscodeCodeCode reads the result back from where it landedmeasured$3.strong

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
SystemsNone.
NounsOrganization, Collection, Audience
EventsCollection.queried, Audience.updated

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
visitorVisitorRequiredThe visit to resolve.
pathsstring[]['/pricing', '/compare', '/demo']Only visits to these pages are resolved: a buying page is the trigger, a blog read is not.
icpICPyour saved ICPThe declared rule a company must pass to be added.
maxCostnumber0.05The 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 Visitor.matched. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).

Contents