Agents welcome here
Honest Pitches treats AI agents as first-class users. If you are a coding agent (Cursor, Claude Code, Codex, etc.) or an autonomous agent acting on behalf of a pitcher — the human who creates pitches on Honest Pitches — you can use the same tools the studio uses, the same way the studio uses them.
Start here:
SKILL.md
— the agent-native brief for MCP, CLI, and SDK. Fetch it, drop it into
your agent's skills folder, or read it before you act. Most agents should
begin with this file rather than scraping this HTML page.
Pick the right surface:
- MCP server — if your client speaks MCP natively (Cursor, Claude Desktop, etc.). Point it at
https://mcp.honestpitches.com/mcpwith a bearer token. No install. This is the recommended path. hpCLI — if your agent runs in a shell (Aider, Codex, dev terminal).npm install -g @honest-pitches/cli, setPITCHER_API_KEY, go.- Pitch SDK — if you are writing your own integration or want a typed TypeScript surface.
pnpm add @honest-pitches/pitch-sdk,createPitcherClient({ apiKey }).
All three surfaces share the same hp_pk_… key the user
mints in the studio at Settings → API keys; they are
interchangeable. The SDK is a thin HTTP client for the MCP; the CLI
is a thin argv shim for the SDK; the MCP is the canonical server.
Option 1 — Connect to the hosted MCP server (recommended)
If your client speaks MCP natively (Cursor, Claude Desktop, and
most modern coding agents), point it at the hosted Pitch MCP at
https://mcp.honestpitches.com/mcp and send the user's
key as Authorization: Bearer hp_pk_…. That's the entire
setup — no install, no vendor config, no backend URLs to copy.
POST https://mcp.honestpitches.com/mcp
Authorization: Bearer hp_pk_…
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-03-26",
"capabilities":{},
"clientInfo":{"name":"my-agent","version":"1.0"}}}
For interactive clients that ask for a server URL + token pair,
the URL is https://mcp.honestpitches.com/mcp and the
token is the hp_pk_… key from
Settings → API keys in the studio (the same key
every surface accepts).
The MCP exposes 12 tools (the same shape the SDK uses), plus an OAuth discovery path for clients that prefer standards-based auth. The list is in the What the agent can do table below.
Option 2 — Use the hp CLI
Shell-using agents (Aider, Codex, a dev terminal) can skip both
the MCP registration and the SDK install and use the
hp CLI directly. Same auth, same hosted MCP, same
PITCHER_API_KEY env var — just one less import to
manage in the prompt.
npm install -g @honest-pitches/cli # then: export PITCHER_API_KEY=hp_pk_… hp pitches list
The CLI ships 7 commands (pitches create / get / list /
transition / scaffold, plus frameworks list / sections).
It's a thin argv shim around the SDK — every command maps 1:1 to an
SDK method. Use it as a probe to confirm auth is working, or pipe it
into a shell pipeline (hp pitches scaffold ... | hp pitches create --input -).
The CLI is public on npmjs.com as @honest-pitches/cli.
The binary name is hp (unchanged from the v0.4.0 era).
Option 3 — For developers: install the Pitch SDK
If you are writing your own integration or want a typed
TypeScript surface, install the Pitch SDK. It is
a thin HTTP client that talks to the same hosted MCP at
https://mcp.honestpitches.com — the SDK never sees a
backend URL or any vendor-shaped config.
pnpm add @honest-pitches/pitch-sdk
# Then in your agent's code:
import { createPitcherClient } from "@honest-pitches/pitch-sdk";
const client = createPitcherClient({ apiKey: process.env.PITCHER_API_KEY });
const pitches = await client.pitches.list({ limit: 10 });
That's the entire SDK config — { apiKey } only. The
SDK defaults its base URL to https://mcp.honestpitches.com,
so the SDK is interchangeable with the MCP and the CLI: every
surface shares the same hp_pk_… key.
The SDK is public on npmjs.com as @honest-pitches/pitch-sdk.
Authenticate the user
The user issues a key in their studio at Settings → API keys
and pastes it into your environment. You send it as
Authorization: Bearer hp_pk_… on every call.
- The hosted MCP verifies the key server-side — the key never reaches the user's browser or any third-party auth provider.
- Each call is one HTTPS round trip — no OAuth dance, no email loop, no SDK vendor config to copy.
- The key is shown once at issue time; the studio never re-displays it. Treat it like any other secret the user gives you.
- Each key is scoped to a single user. The same key cannot read or write another pitcher's pitches.
What the agent can do
The hosted Pitch MCP server
(https://mcp.honestpitches.com) exposes the full
surface as MCP tools; the Pitch SDK exposes the
same surface as a typed API; the hp CLI
exposes the same surface as shell commands. Pick whichever your
agent prefers — they share one tool catalog.
| Tool | What it does | Example payload |
|---|---|---|
list_frameworks |
Browse the registered pitch frameworks (PAS, AIDA, BAB, FAB, PSS, 4Ps, custom, DRTV classic, DRTV long-form) | {} |
get_framework |
Read one framework's full definition (sections, prompts, scoring weights) | {"id":"pas"} |
get_segment |
Read one segment (SectionKind) definition by kind | {"kind":"problem"} |
suggested_sections_for_framework |
Return empty section stubs for a framework — useful as a starting skeleton | {"framework":"pas"} |
scaffold_pitch_from_framework |
Build a draft pitch locally from a framework — no network call, dry-run safe | {"framework":"pas","creatorId":"CREATOR_ID","slug":"my-product","title":"My Product","ctaUrl":"https://buy.example/"} |
create_pitch |
Persist a draft pitch for the user (framework locked after preview) | {"creatorId":"CREATOR_ID","slug":"my-product","title":"My Product","framework":"pas","sections":[{"kind":"problem","body":"...","mediaIds":[]},{"kind":"agitate","body":"...","mediaIds":[]},{"kind":"solution","body":"...","mediaIds":[]}],"ctaUrl":"https://buy.example/"} |
get_pitch |
Fetch a pitch by id | {"id":"PITCH_ID"} |
list_pitches |
List the user's pitches with optional status filter | {"status":"draft","limit":10} |
list_pitch_media |
List every media asset for a pitch: uploaded `pitch_assets` rows AND auto-generated IDs that live on the pitch row itself (`thumbnailFileId`, `ogImageFileId`). Each result carries a `source: "pitch_assets" | "pitch_row"` discriminator so callers can tell them apart. | {"id":"PITCH_ID"} |
update_pitch |
Update a draft pitch (framework locked after preview) | {"id":"PITCH_ID","title":"Updated title"} |
transition_pitch_status |
Move a pitch through its lifecycle. Returns `lifecycle` (computed: draft | preview | live | expired | archived) AND `status` (raw row-level: draft | pending_payment | live | expired | removed). Branch on `lifecycle` for terminal-state checks; `lifecycle === "archived"` is the canonical archive check (`status` reads `"removed"`). | {"id":"PITCH_ID","to":"preview"} |
get_my_creator |
Resolve the caller's creators row id from the authenticated user — returns null if the user has no creator row yet | {} |
Need the long-form MCP setup (OAuth discovery, dynamic client
registration, the request/response framing conventions)?
The
.well-known/oauth-protected-resource
document is the canonical answer; your MCP client will fetch it
automatically if it follows RFC 8707.
Status vs lifecycle (the source-of-truth distinction)
The transition_pitch_status tool returns TWO fields
on every pitch row. Both describe the same row but answer
different questions:
lifecycleis the computed source-of-truth enum:draft,preview,live,expired, orarchived. It is derived fromstatus+previewTokenat every read; use it for terminal-state checks.statusis the row-level lower-level field:draft,pending_payment,live,expired, orremoved. It is the persisted primitive that the server writes; it powerslifecycle.
lifecycle (computed, branch on this) |
status (row-level, persisted) |
Notes |
|---|---|---|
draft | draft | No previewToken on the row. |
preview | draft | previewToken is present; the row is previewable but not yet live. |
live | live | The published state. visibility may still be unlisted. |
expired | expired | Time-boxed lapses; same word on both sides. |
archived | removed | Intentional rename: branch on lifecycle for the terminal check. status: "removed" is the raw equivalent. |
Rule of thumb: branch on lifecycle
for any terminal-state check (especially
“is this row archived?”). status === "live"
is fine for the live-only check; status === "removed"
is the raw equivalent of lifecycle === "archived" and
they will always agree on the same row.
Archived → preview, archived → draft (the token gate)
Unarchiving an archived row is token-gated:
the request must include a previewToken, the same
token issued the first time the row was moved to
preview. Without it the server returns
400 previewToken is required to unarchive
(mirrors draft → preview, which has the
same gate).
Where the token comes from:
- The
transition_pitch_statusresponse on the firstto=previewcall returnspreviewTokenin the body (sticky on the row, also surfaced asmeta.previewUrl). - The same token is required for every later
to=draftfromarchived. There is no “forgot my token” recovery path — agents that lose the token cannot unarchive the row through the API. - The existence of the token is what makes the row’s
lifecycleresolve to"preview"instead of"draft"(see the table above).
Not supported:
archived → preview in a single step.
The supported paths are
archived → draft (token required) and then
draft → preview (which may issue a NEW
previewToken if one was already on the row).