# Bullpen API and CLI

Bullpen exposes one endpoint. Every call is a POST with a JSON body naming an `action`. Auth is a workspace API key.

```
POST https://qsxircxaggtazoqrykyz.supabase.co/functions/v1/api
Authorization: Bearer bp_live_...
Content-Type: application/json

{"action": "whoami"}
```

## Quickstart

1. Sign in at https://bullpen.sale/login and open a workspace (you need the manager or admin role).
2. Go to **Settings → API keys** (https://bullpen.sale/app/settings/api), name the key, copy it. It is shown once.
3. Check it:

```bash
export BULLPEN_KEY=bp_live_...
curl -s https://qsxircxaggtazoqrykyz.supabase.co/functions/v1/api \
  -H "Authorization: Bearer $BULLPEN_KEY" -H "Content-Type: application/json" \
  -d '{"action":"whoami"}'
# {"user_id":"…","org_id":"…","workspace":"…","role":"admin","via":"api_key"}
```

4. Ask Herman to do something:

```bash
curl -s https://qsxircxaggtazoqrykyz.supabase.co/functions/v1/api \
  -H "Authorization: Bearer $BULLPEN_KEY" -H "Content-Type: application/json" \
  -d '{"action":"herman_chat","message":"Who is on the floor right now?"}'
```

## CLI

```bash
curl -fsSL https://bullpen.sale/install.sh | sh     # installs ~/.local/bin/bullpen (bash, curl, python3)
bullpen login bp_live_...                           # saves the key to ~/.config/bullpen/key
bullpen whoami
bullpen herman "find 10 HVAC companies in Salt Lake City"
bullpen brain "what does Bullpen cost for a team of five?"
bullpen knock +18015551234 "Acme Electric" "Jane Doe, GM"
bullpen call campaign_create name="Q4 HVAC"            # any action: key=value, key:=<json>
```

## Pricing

Usage-based, prepaid credits: $5 per human reached (bridge or knock), $0.30 per crew minute, $3/month per number, $0.03 per text, $0.25 per fax page. New workspaces get $10. At a zero balance, spending actions return **402**. Check with `credits_status`. Full table: https://bullpen.sale/pricing

## Paying from an agent

An agent can top up its workspace's credits itself, with no checkout page, using the **Machine Payments Protocol** (MPP, through Stripe). It can also attach an **AP2** mandate proving its principal allowed the spend.

```
POST https://bullpen.sale/pay/credits?amount=25
X-Bullpen-Key: bp_live_...
```

1. The first request returns **402** with a `WWW-Authenticate: Payment …` challenge ($5–$500).
2. The agent pays with a card Shared Payment Token (for example `npx @stripe/link-cli mpp pay <url>`) and retries with `Authorization: Payment …`.
3. Bullpen credits the workspace and answers with a payment receipt.

**No account yet?** An agent can create the workspace itself by paying:

```
POST https://bullpen.sale/pay/signup?amount=25
Content-Type: application/json

{"owner_email": "ops@yourco.com", "company": "Your Co", "agent_name": "your agent"}
```

Same 402 → pay → retry flow ($25–$500, all of it becomes credits, plus the $10 welcome credit). The paid response returns `api_key`, `org_id` and `balance_cents`. Research, Herman, the brain, company search and campaigns work right away. Outbound calls and texts unlock after the owner opens the invite email, signs in, accepts the Outbound Compliance Agreement and adds a phone number, because a person has to stand behind outbound.

**AP2 (optional).** Get a challenge first:

```
GET https://bullpen.sale/pay/ap2/challenge?amount=25
X-Bullpen-Key: bp_live_...
# {"aud":"bullpen.sale","nonce":"n_…","transaction_id":"tx_…","payee":{"id":"bullpen.sale","name":"Bullpen"},"payment_amount":{"amount":2500,"currency":"USD"}}
```

Close your open mandate to exactly that (`aud`, `nonce`, `transaction_id`, payee, amount in cents) and send the dSD-JWT chain on both requests as `X-AP2-Mandate`. Bullpen checks the root issuer against the workspace's trusted issuers (Settings → API keys), every hop's signature and hash binding, every hop's amount and payee limits (not just the root's), expiry, and replay. A paid request returns `ap2_receipt`, an ES256 JWT whose `reference` is sha256 of the closing mandate. Verify it with https://bullpen.sale/pay/ap2/jwks.

## Auth

- **API key** (`bp_live_…`): acts as the member who created it, in that workspace. Revoke it any time in Settings → API keys.
- Keys can call the actions listed below. Billing, buying numbers, connecting Salesforce/Google and inviting people need a signed-in person.
- Errors come back as `{"error":"…"}` with an HTTP status: 400 bad input, 401 bad or revoked key, 403 not allowed for this key or role, 402 out of credits, 409 a precondition (e.g. phones not set up).

## Actions

### Account
- `whoami` → user, workspace, role.

### Herman (the chat agent that runs the bullpen)
- `herman_chat` `{message}` → Herman's reply. Outside actions (calls, texts, email) come back as **pending** with an id; nothing goes out until confirmed. Herman must be switched on for the key's user.
- `herman_decide` `{id, decision: "confirm"|"cancel"}` → runs or drops a pending action.

### Company brain
- `brain_ask` `{question}` → answer with sources, gated by the member's role.

### Calls
- `bridge_call` `{to, target?, why?}` → a crew member dials, works the menu / hold / AI receptionist, asks "real person or AI?", and connects the key's user the moment a person is on.
- `knock_call` `{to, company, target?, note?}` → reach a human at a company line and leave "we got to your humans — find us at bullpen.sale". Company lines only, business hours, once a week per company.
- `knock_many` `{targets:[{company, phone, target?}]}` → two at a time.
- `floor_state` → live and recent crew calls.

### Messages
- `send_sms` `{to, body}` → from the workspace's line (E.164 `to`). Opt-outs are honored.
- `mail_send` `{to, subject, text, cc?}` → from the workspace's agent inbox.

### Finding companies
- `company_search` `{query, max?}` → companies with their main line and website.
- `company_research` `{id}` → who to ask for at a found company.
- `twilio_search` `{area_code}` → numbers available to buy (buying needs a person).

### Campaigns
- `campaign_create` `{name, goal?}` · `campaign_import` `{id, csv}` · `campaign_update` · `campaign_cycle_now` `{id}` · `campaign_work` `{id}` · `campaign_chat` `{id, message}`

### Social Intel (watch companies for buying and risk signals)
- `intel_add` `{names}` (comma or newline separated) · `intel_update` · `intel_remove` · `intel_scan_now`

### Meetings
- `book_meeting` · `meeting_status`

## Rules the crew follows
- Cell phones need written consent before an AI calls them; company lines don't.
- Calling hours follow the callee's time zone.
- STOP / opt-outs end texts and calls to that number.
- The crew says it is an AI when asked.

## Contact
office@roseglass.dev · https://bullpen.sale
