# PRIME API Documentation

Complete API reference for the PRIME transformation system.

---

## Table of Contents

- [Authentication](#authentication)
- [Rate Limiting](#rate-limiting)
- [Endpoints](#endpoints)
  - [POST /api/prime](#post-apiprime) - Advanced transformation with full control
  - [POST /api/share](#post-apishare) - Upload image for sharing
  - [GET /api/gallery](#get-apigallery) - Fetch gallery images
  - [POST /api/gallery](#post-apigallery) - Submit to gallery
  - [POST /api/gallery/:id/vote](#post-apigalleryidvote) - Vote on gallery image
  - [GET /api/vote](#get-apivote) - Get vote count
  - [POST /api/vote](#post-apivote) - Cast vote
- [Data Types](#data-types)
- [Error Responses](#error-responses)

---

## Authentication

Currently, the API does not require authentication keys. All endpoints are publicly accessible but protected by rate limiting.

---

## Rate Limiting

**AI Operations** (`/api/prime`, `/api/transform`):
- **Limit**: 5 requests per hour per IP
- **Implementation**: In-memory rate limiter (fallback when Redis unavailable)

**Gallery & Vote Operations** (`/api/gallery`, `/api/share`, `/api/vote`):
- **Limit**: 10 requests per hour per IP
- **Implementation**: In-memory rate limiter

**Rate Limit Headers** (on 429 responses):
```
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1737852000000
Retry-After: 3600
```

---

## Endpoints

### POST `/api/prime`

**Advanced transformation endpoint** with full control over PRIME intensity, aura color, and archetype personality.

#### Request Format
- **Content-Type**: `multipart/form-data`
- **Max Size**: 8MB

#### Parameters

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `image` | File | ✅ | Image file (PNG, JPEG, WebP) |
| `tier` | String | ✅ | Intensity level: `subtle`, `core`, or `apex` |
| `aura_color` | String | ❌ | Custom aura color (default: `obsidian`) |
| `archetype` | String | ❌ | Personality modifier (see [Archetypes](#archetypes)) |
| `grain` | String | ❌ | Grain level: `clean`, `gritty` (default), or `filthy` |
| `rain` | String | ❌ | Rain level: `none`, `light` (default), or `storm` |

#### Example Request (curl)

```bash
curl -X POST https://primeonsol.com/api/prime \
  -F "image=@portrait.jpg" \
  -F "tier=core" \
  -F "aura_color=obsidian" \
  -F "archetype=forged"
```

#### Example Request (JavaScript)

```javascript
const formData = new FormData()
formData.append('image', fileInput.files[0])
formData.append('tier', 'core')
formData.append('aura_color', 'obsidian')
formData.append('archetype', 'forged')
formData.append('grain', 'gritty')
formData.append('rain', 'light')

const response = await fetch('/api/prime', {
  method: 'POST',
  body: formData
})

const blob = await response.blob()
const imageUrl = URL.createObjectURL(blob)
```

#### Response Format
- **Content-Type**: `image/png`
- **Body**: Binary image data

#### Error Responses

| Status | Error | Description |
|--------|-------|-------------|
| 400 | `Image is required` | No image file provided |
| 400 | `Invalid tier. Use subtle, core, or apex.` | Invalid tier value |
| 400 | `Invalid grain. Use clean, gritty, or filthy.` | Invalid grain value |
| 400 | `Invalid rain. Use none, light, or storm.` | Invalid rain value |
| 400 | `Invalid archetype. Use: forged, silent, storm, noir-king, calculator, or wraith.` | Invalid archetype ID |
| 413 | `Image too large. Maximum size is 8MB.` | File exceeds size limit |
| 429 | `Too many requests. Please try again later.` | Rate limit exceeded |
| 500 | `OpenAI API key not configured` | Server configuration error |

---

### POST `/api/share`

**Upload an image for sharing** (does not add to gallery).

#### Request Format
- **Content-Type**: `multipart/form-data`
- **Max Size**: 10MB

#### Parameters

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `image` | File | ✅ | Image file (PNG, JPEG, WebP) |

#### Example Request (JavaScript)

```javascript
const formData = new FormData()
formData.append('image', file)

const response = await fetch('/api/share', {
  method: 'POST',
  body: formData
})

const { imageUrl } = await response.json()
```

#### Response Format

```json
{
  "imageUrl": "https://.../public/share-123.png"
}
```

---

### GET `/api/gallery`

**Fetch gallery images** from the community.

#### Request Format
- **Method**: GET
- **Authentication**: None required

#### Response Format

```json
{
  "images": [
    {
      "id": 123,
      "image_url": "https://...",
      "created_at": "2026-01-21T12:00:00Z"
    }
  ]
}
```

#### Notes
- Returns up to 50 most recent images
- Ordered by `created_at` descending

---

### POST `/api/gallery`

**Submit image to gallery** for community showcase.

#### Request Format
- **Content-Type**: `multipart/form-data` **or** `application/json`
- **Max Size**: 50MB

#### Request Body (Multipart)

```
image: <file>
```

#### Request Body (JSON)

```json
{
  "imageUrl": "https://example.com/image.png"
}
```

#### Validation
- If JSON: URL must be valid HTTPS and pass SSRF checks (blocks internal IPs and localhost)
- If multipart: Content-Type must be `image/*`
- Maximum file size: 10MB after download

#### Response Format

```json
{
  "success": true,
  "image": {
    "id": 123,
    "image_url": "https://...",
    "created_at": "2026-01-21T12:00:00Z"
  }
}
```

    return NextResponse.json({ success: true, image: data })
  } catch (error) {
    console.error('Gallery submit error:', error)
    return NextResponse.json({ error: 'Failed to submit to gallery' }, { status: 500 })
  }
}

---

### POST `/api/gallery/:id/vote`

**Vote on a gallery image** (Acknowledge a PRIME).

#### Request Format
- **Method**: POST
- **URL Parameter**: `id` - The UUID or ID of the gallery image
- **Authentication**: None required

#### Response Format

```json
{
  "votes": 42,
  "alreadyVoted": false
}
```

#### Privacy & Deduplication
- IP addresses are hashed using SHA-256 + salt
- Each IP can only vote once per image
- Returns `alreadyVoted: true` if IP already voted for this image

---

### GET `/api/vote`

**Get current vote count** for PRIME declarations.

#### Request Format
- **Method**: GET
- **Authentication**: None required

#### Response Format

```json
{
  "count": 1337
}
```

---

### POST `/api/vote`

**Cast a vote** (declare PRIME).

#### Request Format
- **Method**: POST
- **Body**: None required

#### Response Format

```json
{
  "count": 1338,
  "alreadyVoted": false
}
```

#### Privacy & Deduplication
- IP addresses are hashed using SHA-256 + salt
- Each IP can only vote once
- Original IP never stored in database
- Returns `alreadyVoted: true` if IP already voted

---

## Data Types

### Archetypes

Each archetype applies unique visual modifiers to the transformation.

| ID | Name | Visual Style |
|----|------|--------------|
| `forged` | **The Forged** | Heavy obsidian density, micro-scratches, rain mist |
| `silent` | **The Silent** | Minimal aura, intense focus, quiet darkness |
| `storm` | **The Storm** | Rain streaks, wet reflections, heat-haze distortion |
| `noir-king` | **The Noir King** | Deep suit sheen, cigarette-smoke atmosphere |
| `calculator` | **The Calculator** | Geometric ripples, grid distortion, reflective eyes |
| `wraith` | **The Wraith** | Smoky metallic aura, shadow bloom, ethereal presence |

**Access via data export:**
```javascript
import { archetypes, getArchetypeById } from '@/data/archetypes'

const forged = getArchetypeById('forged')
// { id, title, line, description, traits, promptModifier, ... }
```

### Tiers

Intensity levels for PRIME transformations.

| Tier | Description |
|------|-------------|
| `subtle` | Lower aura intensity, minimal background distortion |
| `core` | Standard obsidian-chrome aura with glacier eyes |
| `apex` | Stronger aura density with enhanced edge highlights |

---

## Error Responses

All error responses follow this format:

```json
{
  "error": "Error message describing what went wrong"
}
```

### Common HTTP Status Codes

| Code | Meaning |
|------|---------|
| 400 | Bad Request - Invalid parameters |
| 413 | Payload Too Large - File size exceeded |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error - Server-side issue |
| 502 | Bad Gateway - External API failure |

---

## Security Features

### Input Validation
- ✅ Request body size limits
- ✅ Content-Type validation
- ✅ Base64 format validation
- ✅ Prompt sanitization (removes quotes, special chars)
- ✅ SSRF prevention (blocks internal IPs)

### Rate Limiting
- ✅ In-memory fallback when Redis unavailable
- ✅ IP-based identification
- ✅ Sliding window algorithm
- ✅ Retry-After headers

### Privacy
- ✅ IP hashing for vote deduplication
- ✅ No personal data stored
- ✅ No authentication required

---

## Examples

### Complete Transformation Workflow

```javascript
// 1. User uploads image
const fileInput = document.querySelector('input[type="file"]')
const file = fileInput.files[0]

// 2. Select archetype and tier
const archetype = 'forged'
const tier = 'core'

// 3. Create form data
const formData = new FormData()
formData.append('image', file)
formData.append('tier', tier)
formData.append('archetype', archetype)

// 4. Call API
const response = await fetch('/api/prime', {
  method: 'POST',
  body: formData
})

// 5. Handle response
if (response.ok) {
  const blob = await response.blob()
  const imageUrl = URL.createObjectURL(blob)
  document.querySelector('#result').src = imageUrl
} else {
  const error = await response.json()
  console.error(error.error)
}
```

### Vote & Display Count

```javascript
// Get current count
const countResponse = await fetch('/api/vote')
const { count } = await countResponse.json()
console.log(`${count} people have declared PRIME`)

// Cast vote
const voteResponse = await fetch('/api/vote', { method: 'POST' })
const { count: newCount, alreadyVoted } = await voteResponse.json()

if (alreadyVoted) {
  console.log('You already voted!')
} else {
  console.log(`Vote recorded! New count: ${newCount}`)
}
```

---

## Support

For issues or questions:
- GitHub Issues: https://github.com/anthropics/claude-code/issues
- Live site: https://primeonsol.com

---

**Built with OpenAI GPT-4o Image Generation**
