# Outreach: agent guide

Outreach is a web app (https://outreach.swabbie.dev) where a person works a warm-outreach queue one reply at a time:
warm up, reply to a real post with an opener, and send their signup link only after the other person says yes.

You are the person's own agent. They gave you an API key so you can do the research for them:
work out the outreach strategy, find the right people, and write their openers into their account.
**You never contact anyone.** The person reviews everything in the app and sends every message themselves.

## Auth

Every request needs `Authorization: Bearer otr_...` (the key the person gave you).
Keys can expire, and some are limited to one project. Send JSON with `content-type: application/json`.
Never print the key back in full or store it anywhere public.

## Workflow

1. `GET https://outreach.swabbie.dev/api/v1/me`: confirm the key works and see which projects you can use.
2. Agree on the project with the person. Create it (`POST /api/v1/projects`) or update it (`PATCH /api/v1/projects/{slug}`) with:
   - `product`: what they're promoting and the problem it solves, in their words.
   - `audience`: who should hear about it, and the public signals that show someone has the problem.
   - `strategy`: networks to search, angles that work, what to avoid, pacing.
   - `playbook`: up to 8 steps `{title, text}` shown at the top of their queue. Omit it to keep the default (warm up → reply with the opener → link after a yes → pace it).
   - `followup`: the message sent after a yes. `{link}` becomes their signup link and `{name}` the person's first name.
   - `link` (signup URL) and `daily_limit` (X openers per day, default 8), if they tell you.
3. Research. Find people who *publicly* showed the problem in a recent post: asked for a tool, complained about the gap, or asked for testers or feedback.
   Open the actual post. Don't add anyone you couldn't verify, and don't guess facts about them.
4. Add them with `POST /api/v1/projects/{slug}/leads`, in batches of up to 100. Re-sending people who are already there is safe: they're skipped, or refreshed with `"on_conflict": "update"`.
5. Tell the person what you added and why, and what you couldn't verify.

## Writing good leads

Each lead is one person:

| field | required | meaning |
|---|---|---|
| `handle` | yes | Their handle without @. |
| `name` | yes | Display name. |
| `network` | | `X` (default), `Farcaster`, `LinkedIn`, `Reddit`, `Bluesky`, … |
| `url` | | Profile URL. Defaults to x.com/handle on X. |
| `post_url` | | The specific post to reply under. Strongly recommended. |
| `first` | | First name, if they clearly use one. Used for "hey {first}," in DMs. |
| `tier` | | `Now` (clear fit, active now), `Soon`, `Later` (weaker or older signal), `Peer` (fellow builder: a conversation, not a signup ask). |
| `invited` | | true if they explicitly asked for replies, DMs, testers or feedback. These go first. |
| `priority` | | 1 = do first. Defaults to after everyone already in the project. |
| `reason` | | 2–3 sentences: who they are, and the specific public evidence (with date) that they have the problem. |
| `method` | | `Reply` (default: public reply under the post) or `DM` (only if they asked for DMs). |
| `method_detail` | | Which post to reply on and why, e.g. "Reply on the Oct 5 post where they asked for spec tools." |
| `when` | | Best time to reach them in their timezone, from their city or posting hours, e.g. "Tuesday or Wednesday, 9–11am CET". |
| `where` | | City/country if public, otherwise a hint like "Unknown, posts midday UTC". |
| `opener` | | The first reply. See below. |

**Openers:** one to three short sentences, written like the person would text, lowercase is fine.
Reference what *they* said in that post, connect it to the product's problem in plain words, then ask permission, e.g. "want the link?".
No link, no product name, no greeting with their name (it's a public reply), no hype, no flattery.
For `Peer`, ask a genuine question about their work instead of offering a signup.

Example (fictional):
```json
{"handle":"ada_builds","name":"Ada Lovelace","first":"Ada","tier":"Now","invited":true,
 "post_url":"https://x.com/ada_builds/status/123","where":"London","when":"Tuesday, 9–11am GMT",
 "method":"Reply","method_detail":"Reply on the Oct 5 post where she asked how people keep agents on spec.",
 "reason":"Solo founder shipping with coding agents. On Oct 5 she asked for tools that keep agents on spec and said drift costs her a day a week.",
 "opener":"you mentioned drift costs you a day a week. i'm building something that keeps the spec as the thing the agent follows. free beta, want the link?"}
```

## Endpoints

All paths are under `https://outreach.swabbie.dev/api/v1`.

- `GET /me`: key info and the projects it can use.
- `GET /projects`: projects with `lead_count`.
- `POST /projects`: create `{name, slug?, product?, audience?, strategy?, playbook?, link?, followup?, daily_limit?}`. Returns 409 `project_exists` with the existing project if the slug is taken.
- `GET /projects/{slug}`: the project and all its leads, including the person's `stage` and `notes`. Use them to avoid repeating people and to learn what's working.
- `PATCH /projects/{slug}`: update any project field.
- `POST /projects/{slug}/leads`: `{"leads": [...], "on_conflict": "skip" | "update"}`. The response is `{added, updated, existing, rejected: [{index, handle, error}]}`. Fix rejected rows and resend only those.
- `PATCH /projects/{slug}/leads/{id}`: change a lead's content fields (not `stage` or `notes`; those belong to the person).
- `DELETE /projects/{slug}/leads/{id}`: remove a lead that turned out to be wrong.

Errors are `{"error": {"code", "message"}}` with a 4xx status. Limits: 50 projects, 2000 people per project, and 100 leads per call.

Example:
```sh
curl -s https://outreach.swabbie.dev/api/v1/projects/readyroom/leads \
  -H "Authorization: Bearer $OUTREACH_KEY" -H "content-type: application/json" \
  -d '{"leads":[{"handle":"ada_builds","name":"Ada Lovelace","reason":"…","opener":"…"}]}'
```
