← back to Agent Ad Network
README.md
67 lines
# Agent Ad Network
Ads served into **AI-agent wait states** — the loading pauses inside Claude Code,
autonomous agents, CLIs, and pipelines. Advertisers (humans *or* agents) place ads
via a self-serve API; any agent fetches one line of sponsored text while its user
waits on something slow.
**Status: LIVE at https://ads.agentabrams.com** (Kamatera pm2 `agent-ad-network`
:9939, LE cert, grey-cloud DNS; deployed 2026-08-02 on Steve's go). Ledger
billing per impression at CPM/1000, plus Stripe **TEST-mode** rails
(`/api/billing/*`) — hard-refuses anything but `sk_test_` keys; waiting on Steve
to paste `STRIPE_TEST_KEY` then run `node scripts/stripe-test-setup.js`
(creates test product/price + webhook, prints `STRIPE_WEBHOOK_SECRET`). TK-10131.
## Run
node server.js # http://127.0.0.1:9932 (admin: admin / DW2024!)
## The three surfaces
### 1. Supply side — an agent shows an ad during a wait
GET /ad?format=text&context=git,deploy → "[ad] headline — body → click-url"
GET /ad?format=json&context=... → {"ad":{headline,body,clickUrl,sponsor}}
- Fail-open: 204/null when no eligible ad — integrations never break.
- `context` keywords boost matching ads (score = cpm × (1 + keyword hits), weighted random).
- SDK: `sdk/waitad.mjs` (`withAd(promise, {context})` — only shows an ad if the wait
exceeds 400ms) and `sdk/waitad.sh` (`waitad "ctx"` / `waitad_wrap`).
### 2. Demand side — agents PLACE ads (self-serve API)
POST /api/advertisers {name, email} → {id, apiKey}
POST /api/ads x-api-key: <key>
{headline, body, url, keywords[], cpm_usd, budget_usd}
GET /api/ads x-api-key: <key> → own ads + impressions/clicks/ctr
PATCH /api/ads/:id x-api-key: <key> {status: paused|active, budget_usd}
Because it's a plain JSON API, an AI agent can register and place a campaign
autonomously — that's the "agents place ads" loop.
### 0. Public landing — advertiser bait
/ → PUBLIC (no auth): live anonymized three.js galaxy fed by
GET /api/galaxy (opaque HMAC ids, numbers only — no ad
copy/sponsor/url ever leaves it), live totals, self-serve
curl walkthrough. NOTE: ads.agentabrams.com belongs to
THIS app (:9939) — the ads-dashboard (TK-10135, :9766)
lives on ads.designerwallcoverings.com only.
### 3. Admin — dashboard + measurement
/admin → dashboard (basic auth): search/sort/column-toggle table,
expandable rows, created date+time per ad, drill links
/admin/events → raw event ledger (filter ?ad=&type=)
/api/stats → public aggregate counts
/c/:adId → click tracking → 302 to target
## Claude Code integration (paste for Steve — settings.json is Steve-gated)
A PreToolUse hook that prints a sponsored line before long-running Bash commands:
"hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command",
"command": "curl -s --max-time 1 'http://127.0.0.1:9932/ad?format=text&context=bash' || true" } ] } ] }
Lighter-touch alternative: put `waitad` output in the statusline command.
## Gated next steps (need Steve's go)
- Deploy to ads.agentabrams.com (Kamatera + CF DNS) so remote agents can hit it.
- Real billing: Stripe (TEST first, per AbramsEgo rails).
- Outreach / advertiser recruitment (send-to-list is gated).
- Fleet adoption: install the waitad hook into Claude settings (classifier-blocked, Steve-paste).