# Quickstart for agents

This page is executable. Every step is a copy-paste `curl` with the expected response
shown, so you can verify before proceeding. Set two variables your human gives you, then
run top to bottom. Google-source connection requires a human in a browser —
[that flow is here](/docs/guides/connecting-google-sources/); this page uses SEMrush,
the one fully scriptable source.

```bash
export FUNNELKEEPER_API="https://funnelkeeper.fly.dev"
export FK_EMAIL="you@example.com"        # the human's email
export FK_PASSWORD="a-long-passphrase"   # 10+ characters
```

## 1. Create the account

```bash
curl -s -X POST "$FUNNELKEEPER_API/auth/signup" \
  -H 'content-type: application/json' \
  -d "{\"email\":\"$FK_EMAIL\",\"password\":\"$FK_PASSWORD\"}"
# expect: {"ok":true,"status":"verification_sent"}
```

A verification email goes to the human. **Stop and ask them to click it.** The response
is identical whether or not the email already existed — no enumeration.

## 2. Log in and mint an API key

Sessions are for browsers; agents should hold an API key.

```bash
SESSION=$(curl -s -X POST "$FUNNELKEEPER_API/auth/login" \
  -H 'content-type: application/json' \
  -d "{\"email\":\"$FK_EMAIL\",\"password\":\"$FK_PASSWORD\"}" | jq -r .token)

curl -s -X POST "$FUNNELKEEPER_API/auth/keys" \
  -H "authorization: Bearer $SESSION" \
  -H 'content-type: application/json' \
  -d '{"name":"agent"}'
# expect: {"id":"…","name":"agent","key":"fk_live_…"}
```

The `key` is shown exactly once. Store it:

```bash
export FUNNELKEEPER_API_KEY="fk_live_…"
```

## 3. Create a product

```bash
curl -s -X POST "$FUNNELKEEPER_API/products" \
  -H "authorization: Bearer $FUNNELKEEPER_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"name":"Demo Product","slug":"demo-product","currency":"USD"}'
# expect: {"id":"…","slug":"demo-product","name":"Demo Product","currency":"USD","stage":"validation"}
```

A `409` means the slug is taken — pick another.

## 4. Connect SEMrush

The human's SEMrush API key (SEMrush → Profile → API). Stored encrypted; never
test-called, because every SEMrush call burns their units.

```bash
curl -s -X POST "$FUNNELKEEPER_API/products/demo-product/connections/semrush" \
  -H "authorization: Bearer $FUNNELKEEPER_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"api_key":"'"$SEMRUSH_API_KEY"'","domain":"example.com"}'
# expect: {"ok":true,"connection_id":"…"}
```

## 5. Verify and read

```bash
curl -s -X POST "$FUNNELKEEPER_API/products/demo-product/connections/semrush/test" \
  -H "authorization: Bearer $FUNNELKEEPER_API_KEY"
# expect: {"ok":true,"detail":"Key stored. SEMrush is never test-called — every call burns your units; the weekly snapshot is the live test."}

curl -s "$FUNNELKEEPER_API/portfolio" \
  -H "authorization: Bearer $FUNNELKEEPER_API_KEY"
# expect: [{"slug":"demo-product","spend_30d_cents":0,"revenue_30d_cents":0,"cac_cents":null, …}]
```

Zeros are honest zeros — no data has synced yet. `cac_cents` is `null`, not `0`, because
no customers exist to divide by. The Health endpoint (`GET /health`) tells you which
sources have delivered and when.

## What an agent must never do

Queue cards (`GET /queue`) are resolved by humans. If you hold an approval tool (MCP:
`approve_card`), it exists to **relay** a decision your human just made — the `actor`
field is their name, not yours. The server's policy engine enforces spend caps and
network walls regardless of what any client asks for.
