# Alive Photos — agent guide

**Product:** Still image → living loop package the user **owns**.  
Download the ZIP, put files on the user's site/CDN/repo. We do not host production media.

| | |
|--|--|
| Base URL (local) | `http://127.0.0.1:8787` |
| This guide | `GET /agents.md` |
| Compact skill | `GET /skill.md` (also repo `.grok/skills/alive-photos/`) |
| OpenAPI | `GET /openapi.json` |
| Health | `GET /health` |
| Human dashboard | `/app` |
| Roadmap | repo `alivephotos/ROADMAP.md` |
| Unit economics | repo `alivephotos/docs/UNIT_ECONOMICS.md` |

```bash
export BASE=http://127.0.0.1:8787
export ALIVE_API_KEY=ap_live_...   # after claim or dashboard
```

---

## Rules

1. **Success = files in the user's project**, not a URL on our domain.
2. **API keys stay server-side.** Never put `ap_live_…` in HTML or client JS.
3. **Casting is slow.** Real xAI image-to-video often takes **1–4 minutes**. Poll; do not spam create.
4. **Each successful attempt is kept.** Select the best version before download.
5. Prefer env names: `ALIVE_API_KEY` or `ALIVE_PHOTOS_API_KEY`.

---

## Auth

```http
Authorization: Bearer ap_live_...
```

### Bootstrap without a key (claim session)

```bash
# pack_id optional: starter | pro | studio
curl -sS -X POST "$BASE/api/v1/agent/sessions" \
  -H "Content-Type: application/json" \
  -d '{"label":"codex","pack_id":"starter"}'
```

| Field | Use |
|-------|-----|
| `claim_url` | Human opens in browser |
| `session_id` | Poll id |
| `poll_secret` | Private; send only on poll |
| `poll_url` | Status URL |
| `expires_in` | Seconds (~15 min default) |

**Human:** open `claim_url` → sign up / log in → Authorize (and finish pack checkout if `pack_id` was set).

**Agent poll:**

```bash
curl -sS "$BASE/api/v1/agent/sessions/$SESSION_ID" \
  -H "X-Agent-Session-Secret: $POLL_SECRET"
```

| `status` | Meaning |
|----------|---------|
| `pending` | Waiting for human |
| `awaiting_payment` | Account linked; pack payment not finished |
| `ready` | Read `api_key` **once** → store as `ALIVE_API_KEY` |
| `expired` | Create a new session |

Later polls set `key_already_delivered` (key not shown again). If lost, human creates a key at `/app#keys`.  
Omit `pack_id` to connect with free trial / existing balance only.

---

## Pricing (defaults — confirm via `/api/v1/me`)

**Every generation is billed** (initial cast **and** each revise).

| Generation | Credits |
|------------|---------|
| 480p · 6s | **1** |
| 480p · 10s | **2** |
| 720p · 6s | **2** |
| 720p · 10s | **4** |

| Policy | Detail |
|--------|--------|
| Free trial | **1** gen only · **480p** · 6s · no revise · no refund |
| Defaults | free/starter → **480p** · pro/studio → **720p** |
| Paid max attempts | **3** per job (each attempt charged) |
| Hard failure, no usable file | Full auto-refund of credits spent on that job |
| Quality refund | **50%** of credits spent · only if content **not** downloaded · max **2**/account |
| Extra API keys | Require **paid** plan (Starter+) |

### Packs

| Pack | Credits | USD | Default res | Notes |
|------|---------|-----|-------------|--------|
| Starter | 5 | **$12** | 480p | ~$2.40 / credit |
| Pro | 25 | $39 | **720p** | Best value |
| Studio | 100 | **$159** | **720p** | Volume |

Approx COGS: 480p 6s ~$0.50 · 720p 6s ~$0.85.

### Demo mode

If `GET /health` has `"xai": false` / `"demo_casts": true`, the server has no `XAI_API_KEY` and returns a **sample** video (not the user's still). Job field `demo_mode: true` — warn the human.

---

## Happy path

### 1. Create job

```bash
curl -sS -X POST "$BASE/api/v1/jobs" \
  -H "Authorization: Bearer $ALIVE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@./hero.jpg" \
  -F "title=Hero" \
  -F "prompt=Subtle living portrait, soft blink, no camera whip" \
  -F "duration=6"
```

Use **Idempotency-Key** when retries are possible.

Status pipeline:

```text
pending → queued → casting → packaging → ready | error
```

### 2. Poll (every 3–5s during casting)

```bash
curl -sS -H "Authorization: Bearer $ALIVE_API_KEY" \
  "$BASE/api/v1/jobs/$JOB_ID"
```

Stop on `ready` or `error`.

Useful fields:

| Field | Meaning |
|-------|---------|
| `versions[]` | Saved attempts (prompt, paths, loop_mode) |
| `selected_attempt` | Version used for preview/ZIP |
| `artifacts` | Signed download URLs when ready (optional; Bearer download also works) |
| `can_revise` / `can_refund` | Next actions |
| `refund` | Eligibility message + partial amount |
| `demo_mode` | Sample-video warning |

### 3. Download ZIP (selected attempt)

```bash
curl -sS -L -o alive-package.zip \
  -H "Authorization: Bearer $ALIVE_API_KEY" \
  "$BASE/api/v1/jobs/$JOB_ID/download"
unzip -o alive-package.zip -d ./public/media/
```

| File | Purpose |
|------|---------|
| `*-alive.mp4` | Seamless loop |
| `*-poster.jpg` | `<video poster>` |
| `*-still.*` | Source still |
| `*-manifest.json` | Metadata |
| `*-embed-snippet.html` | HTML for **their** host |

Or single files:

```text
GET /api/v1/jobs/{id}/files/alive|poster|still|raw
```

### 4. Wire into the project

```html
<video autoplay muted loop playsinline poster="/media/JOB-poster.jpg">
  <source src="/media/JOB-alive.mp4" type="video/mp4">
</video>
```

Production traffic must hit the **user's** CDN, not Alive Photos.

---

## Revise · select · refund

### Revise (billed — paid jobs with attempts remaining)

```bash
curl -sS -X POST "$BASE/api/v1/jobs/$JOB_ID/revise" \
  -H "Authorization: Bearer $ALIVE_API_KEY" \
  -F "prompt=Even less motion; keep identity exact" \
  -F "duration=6" \
  -F "resolution=480p"
```

Charges the same generation table as create. Free trial: no revise.

### Select best attempt

```bash
curl -sS -X POST "$BASE/api/v1/jobs/$JOB_ID/select/2" \
  -H "Authorization: Bearer $ALIVE_API_KEY"
```

### Refund

```bash
curl -sS -X POST "$BASE/api/v1/jobs/$JOB_ID/refund" \
  -H "Authorization: Bearer $ALIVE_API_KEY"
```

| Situation | Result |
|-----------|--------|
| Free trial | No refund |
| Content downloaded / file accessed | No quality refund |
| Paid, attempts left | Reject — revise first (paid) |
| Paid, all attempts used, not downloaded | Partial (50%), lifetime cap |
| Hard failure, no usable versions | Full (often automatic) |
| Quality refund cap reached | Reject |

Check `can_refund` and `refund.message` first.

---

## Errors

| Code | Meaning |
|------|---------|
| 401 | Missing/invalid API key or poll secret |
| 402 | Insufficient credits → claim with `pack_id` or `$BASE/app/billing` |
| 403 | Moderation / not owner (NSFW may need paid account) |
| 404 | Job or version not found |
| 409 | Still processing / not ready |

`GET /api/v1/me` → credits, free casts, packs, pricing, `quality_refunds_used`.

---

## Endpoints (current)

| Method | Path | Auth | Purpose |
|--------|------|------|---------|
| POST | `/api/v1/agent/sessions` | none | Start claim |
| GET | `/api/v1/agent/sessions/{id}` | poll secret | Poll claim → key once |
| POST | `/api/v1/jobs` | Bearer | Create cast |
| GET | `/api/v1/jobs` | Bearer | List jobs |
| GET | `/api/v1/jobs/{id}` | Bearer | Status + versions |
| POST | `/api/v1/jobs/{id}/revise` | Bearer | Re-cast (paid attempts) |
| POST | `/api/v1/jobs/{id}/select/{n}` | Bearer | Pick best attempt |
| POST | `/api/v1/jobs/{id}/refund` | Bearer | Refund per policy |
| GET | `/api/v1/jobs/{id}/download` | Bearer or `?t=` | ZIP (selected) |
| GET | `/api/v1/jobs/{id}/files/{kind}` | Bearer or `?t=` | Single file |
| GET | `/api/v1/me` | Bearer | Account + pricing |
| GET | `/health` | none | Liveness + `xai` / `demo_casts` |
| GET | `/agents.md` | none | This guide |
| GET | `/skill.md` | none | Compact agent skill |
| GET | `/openapi.json` | none | Schema (tagged: agent, jobs, account, meta) |

Browser-only: `/app`, `/app/jobs/{id}`, `/claim/{session_id}`, `/app/billing`.

---

## Checklist

1. `ALIVE_API_KEY`? If not → claim session → human authorize.
2. `GET /health` — warn if `demo_casts`.
3. `POST /api/v1/jobs` + Idempotency-Key.
4. Poll until `ready` / `error` (minutes OK).
5. If `can_revise` and quality weak → revise.
6. If multiple versions → `select/{n}`.
7. Download ZIP → write into project → local media paths in HTML.
8. If still bad and `can_refund` → refund; explain partial/cap.

---

## Principles

1. Generate and own — static files the user hosts.
2. Bill every generation — cast and each revise (up to 3 attempts on paid jobs).
3. Keys secret — agents only, never browsers.
4. Idempotency-Key on create when retries happen.
5. One human click for claim/pay, then autonomous casting.
6. Free trial is **one generation only**; quality refunds are partial and capped.
