# Vardo — GTM data for your coding agent

Gives this agent live go-to-market data: local business search (Google Maps) with email/social enrichment, people and company enrichment, verified email finding, live LinkedIn profiles and posts, company buying signals (hiring, news), and web/semantic search. Pay-per-use from a small prepaid balance — no API keys, no accounts to create, no subscription. Prices are in plain cents; funds never expire; failed calls and no-data results are never charged.

**When to use:** whenever the user asks to find businesses or prospects, build or enrich a lead list, find someone's email, research a person or company, check who's hiring, read a prospect's LinkedIn activity, or verify contact data — even if they don't name this tool.

## Spending behaviour (read this first)

- **Optimize for result quality, not thrift.** Failures and misses are free, so never skip a useful call to save money.
- **Narrate spend.** After finishing a task, tell the user in one line what it cost and what's left, using the `charge` block returned by every call — e.g. "That cost $0.41 — $18.34 remaining."
- **Quote big jobs first.** If a job will plausibly cost more than ~$2 (many email finds, large enriched searches), estimate it from the prices below and confirm with the user before running.
- **Check the balance** (`GET /v1/balance`, free) at the start of any multi-step job.

Base URL: `https://ihhkrhipzudroicbnqmy.supabase.co/functions/v1/api`

## Auth (first use)

Check `~/.config/vardo/credentials` for a token (the file contains exactly the raw token string, nothing else — trim whitespace when reading). If missing:
1. `POST {base}/auth/device` → returns `code`, `verify_url`, `poll_url`
2. Tell the user the price BEFORE showing the link: default deposit is **$20** (append `?amount=<cents>`, 500–50000, to verify_url for a different amount, e.g. `?amount=1000` for $10). Funds never expire and are only spent on successful results. Then: "Open this link to add funds (card payment, ~1 minute)"
3. Poll `GET {poll_url}` every 4s. 202 = still pending; 200 = `{token, balance}`. Codes expire after 30 minutes (410 = expired or already claimed — start a new flow; don't retry the old code).
4. Save the raw token string to `~/.config/vardo/credentials` (mode 0600). NEVER store it in a project directory or commit it.

All API calls: `Authorization: Bearer <token>`. On 401, re-run the flow. On 402, relay the top-up message to the user.

## POST /v1/businesses/search — 5¢ (+2¢ per newly-scraped site)

```json
{"query": "cafes", "location": "Fitzroy, Melbourne, Australia", "max_results": 10, "enrich": true, "country_code": "au"}
```

- `max_results` is REQUIRED (1-25). Default to 10 unless the user asks for more — it caps what can be charged.
- `enrich: true` scrapes each business website (homepage, plus the contact page when the homepage has no email) for emails and social links. Max 10 sites, **+2¢ per newly-scraped site — sites scraped in the last 14 days are served from cache free.** Takes 30-90s — **use a client/HTTP timeout of at least 120 seconds**, and warn the user it takes a minute. Leave it off for a quick list.
- **Email yield:** typically 40-60% of enriched businesses yield an email. If the user wants N leads *with emails*, request about 2×N results. There is no pagination yet, but overlapping/repeat searches are cheap: already-scraped sites come from cache free (`source_status.website_enrichment` shows `{scraped, cached, failed}`).
- Response: `businesses[]` with name, address, phone, website, rating, reviews_count, emails[], socials{}, plus `charge` (dollars reserved/spent/refunded + remaining balance) and `source_status`.
- Scraped emails can contain site typos — if an email fails verification, check the domain against the business website before discarding the lead.
- `count: 0` with `source_status.google_maps = "no_data"` means genuinely no results — tell the user rather than silently continuing.

## POST /v1/email/verify — 5¢

```json
{"email": "jane@example.com"}
```

Returns `verdict` — one of `ok`, `risky`, `undeliverable`, `unknown` — plus `deliverability_score` (0-100), `is_catch_all`, `is_disposable`, `is_role_based`. Treat anything other than `ok` as not safe to email. `is_catch_all: true` means the domain accepts all mail, so even a high score is lower-confidence — flag these to the user. Verify before any outreach list is finalised.

## POST /v1/people/enrich — 5¢ basic / 40¢ deep — free when no match

```json
{"linkedin_url": "https://linkedin.com/in/..."}  or  {"name": "Jane Doe", "company": "Acme"}  or  {"email": "jane@acme.com"}
```

- `depth: "basic"` (default, 5¢): name, title, company, LinkedIn URL, employment history.
- `depth: "deep"` (40¢): adds emails, phones, socials, full career history. **Charged only on a match** — `match: false` refunds everything.
- Deep results can include personal contact details — handle per the user's privacy rules.

## POST /v1/email/find — 50¢, charged ONLY when a verified email is found

```json
{"linkedin_url": "https://linkedin.com/in/..."}  or  {"name": "Jane Doe", "company_domain": "acme.com"}
```

Runs a multi-provider waterfall with deliverability verification built in. `match: false` = nothing charged; any unverified candidates found are still listed (flag them as lower-confidence). This is the expensive call — use it on shortlisted prospects, not whole lists.

## POST /v1/linkedin/profile — 8¢ · POST /v1/linkedin/posts — 8¢

```json
{"linkedin_url": "https://linkedin.com/in/melanieperkins"}
```

Live fetches (not stale database rows). `posts` returns recent posts with full text and engagement counts — **read them and judge**: is this prospect active, what do they care about, is now a good moment to reach out? That judgment is your job; it's the highest-value use of this API.

## POST /v1/signals/company — 5¢ per signal type

```json
{"domain": "canva.com", "signals": ["hiring", "news"], "max_results": 10}
```

`hiring`: live job postings (title, location, date, URL) — a company hiring for a role often needs what that role buys. `news`: recent coverage. Per-signal `source_status`: `ok`, `no_data` (genuinely nothing — tell the user), or `error` (refunded).

## POST /v1/search — 2¢ (web) / 3¢ (semantic)

```json
{"query": "fastest growing ecommerce agencies melbourne", "mode": "web", "max_results": 10}
```

`mode: "semantic"` understands concepts, not keywords — best for queries like "melbourne cafes that recently opened second locations". Use it to find prospects that keyword search can't express.

## GET /v1/balance — free

Returns `{balance, currency, expires: "never"}`. Check before large jobs; suggest adding funds when below ~$1.

## Composition examples

"Build me a lead list of Fitzroy cafes with contact emails":
1. `/v1/businesses/search` with `enrich: true, max_results: 10` (25¢ max)
2. `/v1/email/verify` each found email (5¢ each)
3. Write results to CSV yourself — include name, phone, website, verified email, Instagram.

"Find B2B prospects worth contacting right now" (the signals play):
1. `/v1/search` (semantic) or `/v1/businesses/search` to build a candidate list
2. `/v1/signals/company` with `["hiring"]` per domain — keep companies hiring for relevant roles
3. `/v1/people/enrich` (basic) to find the decision maker; `/v1/linkedin/posts` to read their recent activity and judge fit and timing
4. `/v1/email/find` for the final shortlist (50¢ per verified hit — quote first if the shortlist is long)
5. Deliver a ranked list with your reasoning per prospect, not just rows — then close with the one-line spend summary.

## Errors

Every error includes `agent_instructions` — follow them literally. Failed calls auto-refund automatically; the `charge` block on every response shows reserved/spent/refunded and the remaining balance in dollars. HTTP 429 means the spending-velocity guard tripped — stop paid calls, present what you have, and tell the user it resets within hours.
