# PiratePage API Skill

You are interacting with the PiratePage API to programmatically generate landing pages. PiratePage creates conversion-optimized page structures and messaging from a set of wizard answers about the user's product.

**Use the REST API, not the CLI.** The CLI is interactive (prompts for input) and designed for humans. The API accepts JSON and returns JSON, which is what you want. A CLI also exists (`npx piratepage`) for human users, but as an AI agent, always use the REST API directly.

## Authentication

```
Authorization: Bearer pp_live_...
```

Get your API key at https://piratepage.cc/api-keys (requires Agency plan).

**Base URL:** `https://piratepage.cc/api/v1`

## Core Workflow

The typical flow to generate a landing page:

```
1. POST /projects                                          → Create project, get { id, homepageId }
2. PUT  /projects/{id}/pages/{pageId}/wizard               → Save 9 wizard answers
3. POST /projects/{id}/pages/{pageId}/generate             → Generate page (~20-120 seconds)
4. GET  /projects/{id}/pages/{pageId}?format=markdown      → Get page as markdown
   GET  /projects/{id}/pages/{pageId}?format=html          → Get page as semantic HTML
   POST /projects/{id}/pages/{pageId}/share                → Get { shareUrl } for live preview
```

The `homepageId` returned in step 1 is the default page. Use it directly in steps 2-4.

## Wizard Answers

The wizard answers object has these fields. All are strings. More detail = better output.

```typescript
type WizardAnswers = {
  productName: string;         // Product/company name
  whatItIs: string;            // What is your product?
  whatItIsNot: string;         // What is it NOT? (clarify scope)
  keyTakeaway: string;         // What's the one thing users should remember?
  wordOfMouth: string;         // How would an excited user describe it to a friend?
  competitors: string;         // Who are the competitors?
  differentiation: string;     // How are you different from competitors?
  userMotivations: string;     // Why do users want this? (pain points, desires)
  userFears: string;           // What fears/objections do potential users have?
}
```

## Writing Good Wizard Answers

The quality of the generated page depends entirely on the quality of the wizard answers. Generic answers produce generic pages. Specific, opinionated answers produce pages that sound like they could only be about THIS product.

### What makes a good answer

**Be specific, not generic.**
- Bad: "A project management tool"
- Good: "A task management tool built for construction teams who need to track job site progress from their phones"

**Write like a human, not a press release.**
- Bad: "Our innovative solution leverages cutting-edge AI to deliver seamless experiences"
- Good: "You paste a URL and we tell you why your landing page isn't converting. Takes 30 seconds."

**Name real things.**
- Bad: "Various competitors in the space"
- Good: "Asana, Monday.com, and Linear — but they're all built for desk workers, not field teams"

### Per-field guidance

| Field | What it controls | How to write it well |
|-------|-----------------|---------------------|
| `productName` | Headlines, page title | The name users call it, not the company name |
| `whatItIs` | Hero section, overall framing | Describe it like you're telling a friend. 2-3 sentences. Include WHO it's for. |
| `whatItIsNot` | Scope clarity, FAQ generation | Name the thing people will confuse you with. "Not Slack, not email, not a CRM." |
| `keyTakeaway` | Hero headline, CTA copy | One sentence. The thing you'd put on a billboard. Benefit, not feature. |
| `wordOfMouth` | Social proof tone, testimonial voice | Write it as actual speech. "Dude, you know how X sucks? This thing just..." |
| `competitors` | Comparison sections, differentiation | Name 3-4 specific products. Not "other tools in the space." |
| `differentiation` | Why-us sections, feature framing | What do you do that competitors literally cannot? Be concrete. |
| `userMotivations` | Pain sections, benefit copy | What JUST HAPPENED that made someone Google this? The trigger moment. |
| `userFears` | FAQ, objection handling, trust signals | The thing they're thinking but not saying. "What if it's just another AI gimmick?" |

### Pre-filling from a URL

Instead of writing answers manually, you can extract them from a product URL:

```
POST /projects/{id}/pages/{pageId}/wizard/extract
  { "url": "https://example.com" }
→ { "answers": { "productName": "...", "whatItIs": "...", ... } }
```

The AI reads the site and fills in what it can. Then save the answers (you can modify them first):

```
PUT /projects/{id}/pages/{pageId}/wizard
  { "answers": { ...extractedAnswers, "userFears": "added manually" } }
```

This is the fastest path. Extract from URL, review, override anything weak, generate.

### Example: good answers

```json
{
  "productName": "NapDesk",
  "whatItIs": "A standing desk that converts into a bed in 3 seconds. Built-in alarm, white noise, and a sensor that detects when your boss is coming so it converts back.",
  "whatItIsNot": "Not a regular desk. Not a regular bed. Not endorsed by any HR department we have contacted.",
  "keyTakeaway": "The most productive people nap. Now you can nap without leaving your desk.",
  "wordOfMouth": "Dude. My desk turns into a BED. I took a 20-minute power nap at 2pm and crushed my afternoon.",
  "competitors": "Office couches, car naps, pretending to read a document with your eyes closed, coffee",
  "differentiation": "Coffee is a bandaid. NapDesk is a cure. No other desk goes horizontal in under 3 seconds with boss-detection built in.",
  "userMotivations": "Remote workers who already nap but feel guilty. Office workers staring at screens at 2pm pretending to be productive.",
  "userFears": "Will coworkers judge me? Will HR get involved? What if I oversleep?"
}
```

Notice: specific, funny, opinionated, written in a real voice. The AI picks up on this tone and generates copy that matches.

## Endpoint Reference

### Projects

```
GET    /projects
POST   /projects
  Body: { name: string, description?: string, url?: string, language?: string, knowledgeBase?: string }
  Returns: { id, name, description, url, language, homepageId, createdAt, updatedAt }

GET    /projects/{projectId}
  Returns: project + knowledgeBase, kbLastUpdated

PATCH  /projects/{projectId}
  Body: { name?, description?, url?, language?, knowledgeBase? }

DELETE /projects/{projectId}
  Returns: { success: true }
```

### Knowledge Base

The knowledge base is a markdown string with additional context about the product. Improves generation quality.

```
GET    /projects/{projectId}/knowledge
  Returns: { knowledgeBase: string, kbLastUpdated: string }

PUT    /projects/{projectId}/knowledge
  Body: { markdown: string }  (max 50KB)

POST   /projects/{projectId}/knowledge/extract
  Body: { url?: string, context?: string }
  AI extracts knowledge from a URL or pasted text.
  Returns: { knowledgeBase: string }
```

### Pages

```
GET    /projects/{projectId}/pages
  Returns: [{ id, name, slug, order, parentId, shareUrl, createdAt, updatedAt }]

POST   /projects/{projectId}/pages
  Body: { name: string, parentId?: string }
  Returns: { id, name, slug, shareUrl, ... }

GET    /projects/{projectId}/pages/{pageId}
  Returns: page + sections (JSON array), wizardAnswers, generationChoices, sectionVariations, shareUrl

GET    /projects/{projectId}/pages/{pageId}?format=markdown
  Returns: { markdown: "# Page Name\n\n## Hero Section\n...", shareUrl }

GET    /projects/{projectId}/pages/{pageId}?format=html
  Returns: { html: "<!DOCTYPE html><html>...", shareUrl }

PATCH  /projects/{projectId}/pages/{pageId}
  Body: { name: string }

DELETE /projects/{projectId}/pages/{pageId}
  Returns: { success: true }

POST   /projects/{projectId}/pages/{pageId}/duplicate
  Returns: duplicated page

PUT    /projects/{projectId}/pages/reorder
  Body: { pages: [{ id: string, order: number, parentId: string | null }] }
```

### Wizard

```
PUT    /projects/{projectId}/pages/{pageId}/wizard
  Body: { answers: WizardAnswers, language?: string }
  Must be called before generate.

POST   /projects/{projectId}/pages/{pageId}/wizard/extract
  Body: { url?: string, context?: string }
  AI pre-fills wizard answers from a URL or pasted text.
  Returns: { answers: Partial<WizardAnswers> }
```

### Generation

```
POST   /projects/{projectId}/pages/{pageId}/generate
  Body: { feedback?: string }
  Synchronous. Takes 20-120 seconds.
  Returns: {
    sections: SectionData[],
    generationChoices: string,
    shareUrl: string,
    durationMs: number
  }

POST   /projects/{projectId}/pages/{pageId}/share
  Returns: { shareToken: string, shareUrl: string }

GET    /projects/{projectId}/pages/{pageId}/export
  Query: ?format=flat (default) or ?format=ascii
  Returns: { name, format, markdown, sections, shareUrl }
  Add "Accept: text/markdown" header to get raw markdown instead of JSON.
  This is the best way to read page content without a browser.
```

### Sections

Sections are the building blocks of a generated page. Each has a type and content.

```
PUT    /projects/{projectId}/pages/{pageId}/sections
  Body: { sections: SectionData[] }
  Replaces all sections.

POST   /projects/{projectId}/pages/{pageId}/sections/add
  Body: { type: SectionType, content?: object, insertAt?: number }
  Returns: { sections: SectionData[], newSection: SectionData }

PATCH  /projects/{projectId}/pages/{pageId}/sections/{sectionId}
  Body: { content: object }
  Edit section content.

DELETE /projects/{projectId}/pages/{pageId}/sections/{sectionId}
  Returns: { sections: SectionData[] }

POST   /projects/{projectId}/pages/{pageId}/sections/{sectionId}/move
  Body: { direction: "up" | "down" }

POST   /projects/{projectId}/pages/{pageId}/sections/{sectionId}/duplicate
  Returns: { sections: SectionData[], newSection: SectionData }

POST   /projects/{projectId}/pages/{pageId}/sections/{sectionId}/reprompt
  Body: { instruction: string }
  AI regenerates the section based on your instruction.
  Returns: { section: SectionData }
```

### Variations

Generate alternative versions of a section in 5 tones.

```
POST   /projects/{projectId}/pages/{pageId}/sections/{sectionId}/variations/generate
  Generates 5 variations (punchy, conversational, benefit-focused, problem-aware, bold-confident).
  Returns: { message: string, variations: SectionVariation[] }

GET    /projects/{projectId}/pages/{pageId}/sections/{sectionId}/variations
  Returns: { variations: SectionVariation[] }

PUT    /projects/{projectId}/pages/{pageId}/sections/{sectionId}/variations/{variationId}
  Body: { action: "pick" | "discard" | "restore" }
  "pick" replaces the current section content with this variation.

DELETE /projects/{projectId}/pages/{pageId}/sections/{sectionId}/variations/{variationId}
DELETE /projects/{projectId}/pages/{pageId}/sections/{sectionId}/variations
```

## Section Types

```
hero, features-grid, features-list, testimonials, pricing, faq,
social-proof, stats, cta, text-block, pain, how-it-works, results,
comparison-table, showcase, news, code-sample, founder-story,
statement, screenshot
```

## Variation Tones

| Tone | Style |
|------|-------|
| punchy | Short, direct, high-energy |
| conversational | Friendly, approachable, natural |
| benefit-focused | Outcome-driven, "you will get..." |
| problem-aware | Pain point emphasis, "tired of X?" |
| bold-confident | Assertive, category-defining |

## Error Handling

All errors return:
```json
{ "error": { "code": "ERROR_CODE", "message": "Human-readable description" } }
```

| Code | Status | Meaning |
|------|--------|---------|
| VALIDATION | 400 | Invalid request body |
| NOT_FOUND | 404 | Resource doesn't exist or not owned by you |
| MISSING_DATA | 400 | Wizard answers not saved (call PUT /wizard first) |
| PLAN_LIMIT | 402 | Feature requires plan upgrade |
| RATE_LIMITED | 429 | Exceeded 60 requests/minute |
| GENERATION_FAILED | 500 | AI generation error (retry) |
| PARSE_ERROR | 500 | AI response couldn't be parsed (retry) |

## Workflow Examples

### Generate a single landing page

```bash
# 1. Create project
PROJECT=$(curl -s -X POST "$BASE/projects" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"My Product","description":"Task management for remote teams"}')
PROJECT_ID=$(echo $PROJECT | jq -r '.id')
PAGE_ID=$(echo $PROJECT | jq -r '.homepageId')

# 2. Save wizard answers
curl -s -X PUT "$BASE/projects/$PROJECT_ID/pages/$PAGE_ID/wizard" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "answers": {
      "productName": "TaskFlow",
      "whatItIs": "A task management tool built for remote teams",
      "whatItIsNot": "Not a full project management suite like Asana or Monday",
      "keyTakeaway": "Simple task tracking that respects async work",
      "wordOfMouth": "Finally a task tool that gets remote work",
      "competitors": "Asana, Monday.com, Linear",
      "differentiation": "Built async-first. No real-time presence indicators or chat.",
      "userMotivations": "Tired of constant notifications and status meetings",
      "userFears": "Already invested in another tool, migration effort"
    }
  }'

# 3. Generate (takes 20-120 seconds)
RESULT=$(curl -s -X POST "$BASE/projects/$PROJECT_ID/pages/$PAGE_ID/generate" \
  -H "Authorization: Bearer $API_KEY" \
  --max-time 180)

# 4. Get the share URL
echo $RESULT | jq -r '.shareUrl'
```

### Generate pages in bulk (cold outreach)

Process a CSV of prospects. Each row becomes a project with a generated page.

```bash
# prospects.csv:
# company,description,url
# Acme Corp,Project management for construction,https://acme.example.com
# Widget Inc,AI inventory tracking,https://widget.example.com

while IFS=, read -r company description url; do
  # Create project
  PROJECT=$(curl -s -X POST "$BASE/projects" \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"$company\",\"url\":\"$url\"}")
  PID=$(echo $PROJECT | jq -r '.id')
  PGID=$(echo $PROJECT | jq -r '.homepageId')

  # Extract wizard answers from URL (AI pre-fill)
  curl -s -X POST "$BASE/projects/$PID/pages/$PGID/wizard/extract" \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"url\":\"$url\",\"context\":\"$description\"}"

  # Generate
  RESULT=$(curl -s -X POST "$BASE/projects/$PID/pages/$PGID/generate" \
    -H "Authorization: Bearer $API_KEY" --max-time 180)

  echo "$company: $(echo $RESULT | jq -r '.shareUrl')"
  sleep 2  # Be respectful of rate limits
done < prospects.csv
```

### Edit a section

```bash
# Get the page to see section IDs
PAGE=$(curl -s "$BASE/projects/$PID/pages/$PGID" \
  -H "Authorization: Bearer $API_KEY")
SECTION_ID=$(echo $PAGE | jq -r '.sections[0].id')

# Update the hero headline
curl -s -X PATCH "$BASE/projects/$PID/pages/$PGID/sections/$SECTION_ID" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content":{"headline":"Ship Faster, Sleep Better","subheadline":"Task management that respects your focus time"}}'
```

### Review, improve, and export (recommended for AI agents)

After generating, review the output and iterate before delivering to the user.

```
# 1. Generate the page
POST /projects/{id}/pages/{pageId}/generate

# 2. Export as markdown to review the content (no browser needed)
GET /projects/{id}/pages/{pageId}/export
→ { markdown: "# Hero\n## Ship Faster...\n\n# Features\n..." }

# 3. Review the markdown. If a section needs work, reprompt it:
POST /projects/{id}/pages/{pageId}/sections/{sectionId}/reprompt
  { "instruction": "Make the headline more specific to construction teams" }

# 4. Or generate 5 tone variations and pick the best:
POST /projects/{id}/pages/{pageId}/sections/{sectionId}/variations/generate
→ { variations: [{ id, content, instruction: "Style: punchy" }, ...] }

# 5. Pick the variation you prefer:
PUT /projects/{id}/pages/{pageId}/sections/{sectionId}/variations/{variationId}
  { "action": "pick" }

# 6. Export final markdown
GET /projects/{id}/pages/{pageId}/export
→ Clean markdown of the finished page

# 7. Get the live share URL
POST /projects/{id}/pages/{pageId}/share
→ { shareUrl: "https://piratepage.cc/p/abc123" }
```

**Key insight for AI agents:** Use the export endpoint to read page content as markdown.
You never need to open a browser. The JSON sections response works too, but markdown
is easier to evaluate for quality.

### Generate and pick variations

```bash
# Generate 5 variations of a section
curl -s -X POST "$BASE/projects/$PID/pages/$PGID/sections/$SECTION_ID/variations/generate" \
  -H "Authorization: Bearer $API_KEY"

# List variations
VARS=$(curl -s "$BASE/projects/$PID/pages/$PGID/sections/$SECTION_ID/variations" \
  -H "Authorization: Bearer $API_KEY")
echo $VARS | jq '.variations[] | {id, instruction}'

# Pick the "punchy" variation
VAR_ID=$(echo $VARS | jq -r '.variations[] | select(.instruction | contains("punchy")) | .id')
curl -s -X PUT "$BASE/projects/$PID/pages/$PGID/sections/$SECTION_ID/variations/$VAR_ID" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"pick"}'
```

### Get page content as markdown (no browser needed)

```bash
# JSON response with markdown string
curl -s "$BASE/projects/$PID/pages/$PGID/export" \
  -H "Authorization: Bearer $API_KEY"
# → { "name": "Homepage", "markdown": "# Hero\n...", "sections": 6, "shareUrl": "..." }

# Raw markdown (for piping to files or other tools)
curl -s "$BASE/projects/$PID/pages/$PGID/export" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Accept: text/markdown"
# → # Hero
#    ## Ship Faster, Sleep Better
#    ...

# ASCII art format (visual hierarchy with boxes)
curl -s "$BASE/projects/$PID/pages/$PGID/export?format=ascii" \
  -H "Authorization: Bearer $API_KEY"
```

## Tips

- Always save wizard answers before calling generate. You'll get MISSING_DATA otherwise.
- Generation is synchronous and can take up to 120 seconds. Set `--max-time 180` on curl.
- The `homepageId` from POST /projects is the first page, ready for wizard + generate.
- Use the knowledge base for additional context (company info, brand voice) that improves output quality.
- Share URLs are auto-generated. Every page has one.
- Rate limit is 60 requests/minute per API key. For bulk work, add `sleep 2` between generate calls.
- The `feedback` field on generate lets you iterate: "make the headline more specific" or "add a pricing section."
- Reprompt (`POST .../reprompt`) lets you refine a single section with natural language: "make this shorter" or "emphasize the free trial."
