Deepline SDK Search Discipline
This skill teaches GTM search as paid uncertainty reduction. The goal is not fewer provider calls; the goal is making every useful paid call reusable, inspectable, and tied to a final row the user can trust.
A good Deepline search run has a shape: discover the live provider contract, put the first useful provider call into a scratchpad play, pilot a few rows, inspect declared getters, narrow with evidence, enrich only survivors, validate known channels, and export flat rows with provenance and miss reasons.
The play is the notebook. Stable step ids are the cache. Getter contracts are the interface. The CSV is only the final surface.
Build a notebook, not a masterpiece. Start with a probe. Checkpoint what costs money. Add gates before fanout. Climb the ladder only when the receipts look good.
Command Discipline
Run Deepline commands as direct commands. Do not add shell helpers around them.
Do not search parent repos or unrelated worktrees for old *.play.ts scratch files.
Good:
deepline health --json && deepline auth status --json && deepline billing balance --json
deepline plays list --origin prebuilt --json
deepline plays grep email --origin prebuilt --json
deepline tools list --categories email_finder --json
deepline tools describe dropleads_email_finder --json
Bad:
deepline health --json 2>&1
deepline plays search email --json | head
deepline tools describe dropleads_email_finder --json | jq '.cost'
deepline tools search "email finder" --json &
If you need structured data, request Deepline JSON and read that command's output directly. Do not pipe, redirect, slice, parse with Python, background, or wrap commands in shell fallbacks. A short && preflight is fine for cheap status checks; do not chain paid executes, play runs, or discovery commands whose output you need to inspect. Shell parsing turns a typed contract into disposable rendered text and slows the run.
Vocabulary
Use these terms consistently.
- Scratchpad play — the local
.play.tsfile where paid provider work becomes replayable state, not disposable console output. A scratchpad is always a play, not a loose CLI transcript or notes file. - Notebook — the play as a learning surface. It can contain tiny runs, inspection comments, pure scoring steps, gates, and export projections because each rerun preserves useful knowledge.
- Probe — the smallest useful run: usually 1-2 rows, one source, one getter, one hypothesis. A probe is allowed to be incomplete; its job is to make the next edit less speculative.
- Checkpoint — a stable
ctx.tools.execute,ctx.runPlay, orctx.map(...).step(...)id that turns paid or slow work into reusable state. If a rerun should not rebuy it, checkpoint it. - Gate — a pure step that decides whether paid fanout is allowed: domain match, title fit, category evidence, placeholder filter, max rows, max fallback legs, or balance floor.
- Ladder — the scale path: 1 row -> 5 rows -> 25 rows -> full. Do not jump from unknown shape to full fanout unless a prebuilt play exactly covers a tiny input.
- Receipt — row-level evidence that explains why a result is trustworthy: source, provider/play, getter used, fit evidence, status, and miss reason.
- Source call — a provider call that creates candidate companies, people, domains, or accounts.
- Pilot — a tiny run that proves row shape, getter availability, null behavior, relevance, and cost before scale.
- Getter contract — the semantic output interface declared by
deepline tools describe, such asextractedLists.people.get()orextractedValues.email_status.get(). - Evidence column — a field that explains why the row belongs: category, domain source, title match, company fit, signal text.
- Fanout — row-level enrichment where provider calls multiply by row count.
- Branch — a meaningfully different source hypothesis, not another tiny edit to the same provider payload.
- Validation — checking an already found email or phone, not discovering one.
- Miss reason — a deliberate output explaining why a row lacks a desired field.
Operating Model
Search is an economic narrowing loop:
- Run one compact preflight command for health, auth, and balance.
- List and describe prebuilt plays before provider tools; plays are waterfalls and often already encode the right route.
- If a play fits, run it directly or wrap it in a scratchpad with
ctx.runPlay(...). - Drop to provider tools only after naming the play mismatch: missing input, wrong output, wrong scale, or custom gating/validation needed.
- Discover fallback tools by capability tags/categories, then describe exact contracts, getters, cost, and failure modes.
- Put useful custom provider work into a scratchpad play with stable ids.
- Probe 1-2 rows first, then ladder up only after receipts prove shape and fit.
- Add gates before paid fanout; enrich only survivors.
- Export flat user-facing rows with provenance and miss reasons.
Put any provider call that might contribute final rows into a .play.ts file before continuing exploration. The scratchpad play is the artifact; rendered CLI output is only a transient inspection aid.
Cost Checkpoint Before Scale
Borrow the GTM execution-plan habit: separate the pilot from the full run, then pause before paid scale with a concrete plan. Real cost hides in multiplication. Seven final rows can still burn a full budget if the play bought hundreds of people candidates and discarded most of them downstream.
Get cost from the live contract, not memory:
deepline plays describe prebuilt/<play-name> --json
deepline tools describe <fallback-source-tool-id> --json
deepline tools describe <fallback-people-search-tool-id> --json
deepline tools describe <fallback-email-finder-tool-id> --json
deepline billing balance --json
For tools, read cost.pricingModel, deeplineCreditsPerPricingUnit, deeplineUsdPerPricingUnit, billingMode, inputSchema, and declared getters. For plays, read the described inputs/outputs and pricing estimate if present; if the play is a waterfall, estimate the expected range by its stop condition: best case pays the first successful leg, worst case pays every fallback leg that can run for a row. If pricing is missing, ambiguous, or per-result with unclear result count, treat it as cost-unknown and ask before scaling.
Before any mapped paid enrichment beyond the pilot, show the user:
Plan before paid scale:
- Goal: <target rows and required fields>
- Pilot result: <rows inspected, useful rows, declared getters, miss reasons>
- Source size: <candidate companies/people available after cheap filters>
- Limits: <max source rows, max people per account, max fallback legs>
- Route: <prebuilt play used/wrapped, or exact mismatch that forced custom tools>
- Paid fanout estimate: <rows> * <play/waterfall calls per row> * <fallback legs that can run>
- Expected credit range: <low-high credits>, based on `plays describe` and fallback `tools describe`
- Stop conditions: <balance floor, max misses, max spend, max rows>
- Visual inspection: open <DEEPLINE_HOST_URL>/dashboard/plays/<play-name> or use `deepline runs get <run-id> --json --full`
Then ask for approval to continue unless the user already gave a budget or the run stays within a tiny pilot. This is not ceremony: it prevents a broad people search from buying candidates that never survive title, domain, or email checks.
Inside the play, put response-size gates before ctx.map. First source calls can return far more rows than the user asked for. After the pilot reveals shape and counts, add cheap pure steps or source limit/size/numResults fields so mapped enrichment only sees a bounded candidate set.
const source = await ctx.tools.execute({
id: 'company_seed',
tool: '<company-search-tool-id>',
input: {
query: input.query,
numResults: Math.min(Number(input.sour