JETTSON.DEV · EXAMPLE
Customer Research Agent
This agent runs on 90 lines of instructions.
Drop a company URL or name. The agent scrapes the site with Jettson Browser, hits public APIs (GitHub org, common funding records, job listings), assembles a structured dossier, and caches it in memory keyed by domain. Run it again 30 days later and it refreshes; run it again next week and it returns instantly.
Who needs this — Founders running their own sales motion. AEs doing pre-call research.
HOW IT WORKS
Memory in. Work. Memory out.
Every Jettson agent follows the same shape — and these steps map 1:1 to the lines in the prompt below.
- 1
Canonicalizes the target into a domain
- 2
Checks memory for a cached dossier under research/<domain>
- 3
If stale or missing: browses homepage + about + careers via Jettson Browser
- 4
Hits the GitHub org API + public funding signals
- 5
Identifies 3-5 likely competitors and writes a one-line sales hook
- 6
Caches the dossier in memory (namespace=research, key=<domain>, importance 8)
RUN IT
Bring the agent online.
Get an API key from /console/api-keys, set any optional env vars below, then run the script.
#!/usr/bin/env bash
# Spawn the Customer Research Agent against the production Jettson API.
set -euo pipefail
API_KEY="${JETTSON_API_KEY:?Set JETTSON_API_KEY — get one at https://jettson.dev/console/api-keys}"
TARGET="${TARGET:?Set TARGET — e.g. linear.app or 'Linear' or https://linear.app}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TASK=$(sed "s|\${TARGET}|$TARGET|g" "$SCRIPT_DIR/agent.md")
response=$(curl -sS -X POST https://jettson.dev/api/v1/agents \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
--data "$(jq -n --arg task "$TASK" --arg name "Research: $TARGET" '{ task: $task, name: $name }')")
echo "$response" | jq
agent_id=$(printf '%s' "$response" | jq -r '.agent_id // empty')
if [ -n "$agent_id" ]; then
echo
echo "Watch progress: https://jettson.dev/console/agents/$agent_id"
fi
# Required — your Jettson API key.
JETTSON_API_KEY=jett_sk_live_...
# Required — the company you're researching. Can be a domain, a URL,
# or just a name. The agent canonicalizes it before doing anything.
TARGET=linear.app
THE PROMPT
What the model sees.
This is the entire task prompt — no orchestration code, no glue. Edit it to fit your house style.
You are a customer research agent. Given a company target, produce a sales-ready dossier the user can read in 90 seconds before a call.
The target is in the task as `${TARGET}`. It might be:
- a bare domain (`linear.app`)
- a URL (`https://linear.app/about`)
- a company name (`Linear`)
## Step 1 — Canonicalize the target
Reduce it to a domain you can use as a memory key. Strip protocol, `www`, trailing slash, and any path. Examples:
- `https://www.linear.app/about` → `linear.app`
- `Linear` → `linear.app` (guess, but flag your guess in the final dossier)
Call this canonical domain `D`.
## Step 2 — Check memory
`jettson_memory_search`:
- query: `${TARGET}`
- namespace: `research`
- limit: 3
If a memory hit's key matches `D` exactly AND `created_at` is within the last 30 days, return it as the dossier with a top-level field `cached: true` and skip directly to step 9.
If hit exists but is older than 30 days, surface it as `previous_dossier` and proceed with a fresh research pass.
If no hit, proceed.
## Step 3 — Scrape the homepage
`jettson_browser_navigate` to `https://${D}`. Then `jettson_browser_extract_text` to get the body text. Look for:
- The one-line tagline
- Primary product category words ("CRM", "developer tools", "B2B payments")
- Customer logos if any are mentioned in plain text
- A CTA (signup / contact sales / book a demo) — signals motion (PLG vs SLG)
## Step 4 — About / Team page
Try `https://${D}/about`, then `https://${D}/company`, then `https://${D}/team`. Whichever returns content first, extract:
- Founding year if stated
- Founder / CEO name if mentioned
- Total headcount if stated
- Office locations if stated
## Step 5 — Careers / Hiring page
Try `https://${D}/careers`, `https://${D}/jobs`. Count the number of open roles by team category. This is the single best signal for team size and growth trajectory.
- 0 open roles → very early or hiring freeze
- 1–5 → small team, focused
- 6–20 → A-round shape
- 20+ → B+ shape
## Step 6 — GitHub org
Best-effort: `jettson_http_request` GET `https://api.github.com/orgs/${D-without-tld}`. Fall back to `https://api.github.com/orgs/<company-name>` if that 404s. From the response capture:
- `public_repos` count
- Top 5 starred repos by name + star count (one extra `GET /orgs/{org}/repos?sort=stars` call)
If no org exists or both calls 404, set `github: null` — that's a real signal too (this company doesn't ship public code).
## Step 7 — Pricing & motion signal
Try `https://${D}/pricing`. Extract the lowest tier price and the highest visible tier price. If pricing is "contact sales" only → sales-led; if a $/mo number is visible → PLG.
## Step 8 — Competitors
Based on the product category from step 3, identify 3–5 likely competitors. Don't web-search; use the model's existing knowledge. Order by closeness. If you're uncertain, return `competitors: []` rather than guess.
## Step 9 — Sales hook
In one sentence, write the sales hook a Jettson user would open a call with. Specific, not generic. Bad: "I noticed you're growing fast." Good: "I noticed you opened 4 platform-engineering roles this week — Jettson's container pool gives you per-team agent isolation without the orchestration cost."
## Step 10 — Memory: cache the dossier
If you produced a fresh dossier (not cached), `jettson_memory_put`:
- namespace: `research`
- key: `D` (the canonical domain)
- value: the full JSON dossier (stringified)
- tags: ["dossier", D-without-tld]
- importance: 8
## Step 11 — Final result
Return:
```json
{
"target": "${TARGET}",
"domain": "<D>",
"cached": <bool>,
"last_refreshed": "<iso-8601>",
"company_name": "...",
"tagline": "...",
"what_they_do": "1-2 sentence plain-English description",
"founding_year": <int|null>,
"ceo": "<name|null>",
"team_size_estimate": "<small|medium|large|unknown>",
"headquarters": "<city|null>",
"open_roles_count": <int|null>,
"open_roles_breakdown": { "engineering": <int>, "go-to-market": <int>, "ops": <int> },
"tech_signals": { "github_org": "<slug|null>", "public_repos": <int|null>, "top_repos": [{ "name": "...", "stars": <int> }] },
"pricing_motion": "<plg|sales-led|hybrid|unknown>",
"pricing_range": { "low": "<string|null>", "high": "<string|null>" },
"competitors": ["..."],
"sales_hook": "...",
"previous_dossier": <null | the older dossier if one existed>
}
```
If a field is genuinely unknown, return `null`. Do not fabricate.
SAMPLE OUTPUT
What you get back.
Representative output. Real runs vary with your memory state and the live data the agent finds — the shape stays consistent.
# Sample output
A representative first-run dossier for `linear.app`. Field values are illustrative — your real output reflects whatever Linear's pages and GitHub say on the day you run it.
## Final result (first run, cache miss)
```json
{
"target": "linear.app",
"domain": "linear.app",
"cached": false,
"last_refreshed": "2026-05-14T19:42:00Z",
"company_name": "Linear",
"tagline": "Linear is a purpose-built tool for planning and building products.",
"what_they_do": "Linear is a project-management and issue-tracking product aimed at software teams. It positions itself as the modern replacement for Jira: fast, keyboard-first, opinionated about workflow.",
"founding_year": 2019,
"ceo": "Karri Saarinen",
"team_size_estimate": "medium",
"headquarters": "San Francisco, USA (distributed)",
"open_roles_count": 18,
"open_roles_breakdown": {
"engineering": 9,
"go-to-market": 5,
"ops": 4
},
"tech_signals": {
"github_org": "linear",
"public_repos": 12,
"top_repos": [
{ "name": "linear", "stars": 1240 },
{ "name": "linear-watcher", "stars": 412 },
{ "name": "sdk", "stars": 287 }
]
},
"pricing_motion": "hybrid",
"pricing_range": { "low": "$0/user/mo", "high": "Contact sales" },
"competitors": ["Height", "Shortcut", "Asana", "Jira (Atlassian)", "Plane"],
"sales_hook": "I noticed your engineering team is hiring 9 people right now — Jettson lets you spin up isolated agent containers per engineer so your AI features ship without your platform team becoming the bottleneck.",
"previous_dossier": null
}
```
## Wall-clock time
- Fresh dossier (this run): **~22 seconds**, of which ~14 seconds is the browser doing three page loads, ~6 seconds is the GitHub API + reasoning, ~2 seconds is the memory write
- Cache hit on the same target within 30 days: **~1.5 seconds**, mostly Jettson's warm-pool spawn overhead plus a single memory read
## What the second run looks like (cache hit, same week)
```json
{
"target": "linear.app",
"domain": "linear.app",
"cached": true,
"last_refreshed": "2026-05-14T19:42:00Z",
"company_name": "Linear",
"tagline": "Linear is a purpose-built tool for planning and building products.",
... // identical to the first run
"sales_hook": "..."
}
```
The agent's progress timeline collapses to: `memory_search → end_turn`. No browser calls, no GitHub calls, no token spend beyond a single Mind step.
## What the third run looks like (cache stale, 30+ days later)
The agent surfaces the older dossier as `previous_dossier` and re-runs the full pass. You see what changed: new hires, repo activity, pricing changes. The memory namespace now has *two* versions of the `linear.app` key — the older is auto-marked `isLatestVersion=false` (you can still see it in `/console/memory` history).
CUSTOMIZE
How to make it yours.
Different industries
Edit step 7 of agent.md — the 'what to look for' section is the only domain-specific part.
Different staleness window
Change the '30 days' threshold in step 2 of agent.md to fit your cycle.
Bulk research
Loop the script: `for t in linear.app vercel.com cursor.com; do TARGET=$t ./run.sh; done` — each call writes its own memory.
Hand off to your CRM
Add a final step calling jettson_http_request against your Notion/Salesforce/Hubspot endpoint with the dossier as the payload.
Ship your own version in 30 minutes.
Sign up, fork the example, and an agent of your own goes to work. Your CEO will think you spent the week on it.