# Image Generation & Editing

Generate and edit images via BlockRun's image API with x402 micropayments — no API keys, pay per image.

## Table of Contents

- [Quick Start](#quick-start)
- [Models & Pricing](#models--pricing)
- [API Reference](#api-reference)
  - [POST /v1/images/generations](#post-v1imagesgenerations)
  - [POST /v1/images/image2image](#post-v1imagesimage2image)
- [Code Examples](#code-examples)
  - [Image Generation](#image-generation-examples)
  - [Image Editing (img2img)](#image-editing-examples)
- [In-Chat Commands](#in-chat-commands)
- [Notes](#notes)

---

## Quick Start

ClawRouter runs a local proxy on port `8402` that handles x402 payments automatically. Point any OpenAI-compatible client at it:

```bash
curl -X POST http://localhost:8402/v1/images/generations \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/nano-banana",
    "prompt": "a golden retriever surfing on a wave",
    "size": "1024x1024",
    "n": 1
  }'
```

Response:

```json
{
  "created": 1741460000,
  "data": [
    {
      "url": "https://files.catbox.moe/abc123.png"
    }
  ]
}
```

The returned URL is a publicly hosted image, ready to use in Telegram, Discord, or any client.

---

## Models & Pricing

| Model ID                    | Shorthand     | Price       | Max Size  | Provider            |
| --------------------------- | ------------- | ----------- | --------- | ------------------- |
| `google/nano-banana`        | `nano-banana` | $0.05/image | 1024×1024 | Google Gemini Flash |
| `google/nano-banana-pro`    | `banana-pro`  | $0.10/image | 4096×4096 | Google Gemini Pro   |
| `openai/dall-e-3`           | `dall-e-3`    | $0.04/image | 1792×1024 | OpenAI DALL-E 3     |
| `openai/gpt-image-1`        | `gpt-image`   | $0.02/image | 1536×1024 | OpenAI GPT Image    |
| `black-forest/flux-1.1-pro` | `flux`        | $0.04/image | 1024×1024 | Black Forest Labs   |

Default model: `google/nano-banana`.

---

## API Reference

### `POST /v1/images/generations`

OpenAI-compatible endpoint. Route via ClawRouter proxy (`http://localhost:8402`) for automatic x402 payment handling.

**Request body:**

| Field    | Type     | Required | Description                                    |
| -------- | -------- | -------- | ---------------------------------------------- |
| `model`  | `string` | Yes      | Model ID (see table above)                     |
| `prompt` | `string` | Yes      | Text description of the image to generate      |
| `size`   | `string` | No       | Image dimensions, e.g. `"1024x1024"` (default) |
| `n`      | `number` | No       | Number of images (default: `1`)                |

**Response:**

```typescript
{
  created: number; // Unix timestamp
  data: Array<{
    url: string; // Publicly hosted image URL
    revised_prompt?: string; // Model's rewritten prompt (dall-e-3 only)
  }>;
}
```

### `POST /v1/images/image2image`

Edit an existing image using AI. Route via ClawRouter proxy (`http://localhost:8402`) for automatic x402 payment handling.

**Request body:**

| Field    | Type     | Required | Description                                                 |
| -------- | -------- | -------- | ----------------------------------------------------------- |
| `model`  | `string` | No       | Model ID (default: `openai/gpt-image-1`)                    |
| `prompt` | `string` | Yes      | Text description of the edit to apply                       |
| `image`  | `string` | Yes      | Source image — see **Image input formats** below            |
| `mask`   | `string` | No       | Mask image (white = area to edit) — same formats as `image` |
| `size`   | `string` | No       | Output dimensions, e.g. `"1024x1024"` (default)             |

**Image input formats** — the `image` and `mask` fields accept any of:

| Format             | Example                            | Description                                    |
| ------------------ | ---------------------------------- | ---------------------------------------------- |
| Local file path    | `"/Users/me/photo.png"`            | Absolute path — ClawRouter reads the file      |
| Home-relative path | `"~/photo.png"`                    | Expands `~` to home directory                  |
| HTTP/HTTPS URL     | `"https://example.com/photo.png"`  | ClawRouter downloads the image automatically   |
| Base64 data URI    | `"data:image/png;base64,iVBOR..."` | Passed through directly (no conversion needed) |

Supported image formats: **PNG**, **JPG/JPEG**, **WebP**.

**Response:**

```typescript
{
  created: number; // Unix timestamp
  data: Array<{
    url: string; // Locally cached image URL (http://localhost:8402/images/...)
    revised_prompt?: string; // Model's rewritten prompt
  }>;
}
```

---

## Code Examples

### Image Generation Examples {#image-generation-examples}

### curl

```bash
# Default model (nano-banana, $0.05)
curl -X POST http://localhost:8402/v1/images/generations \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/nano-banana",
    "prompt": "a futuristic city at sunset, cyberpunk style",
    "size": "1024x1024",
    "n": 1
  }'

# DALL-E 3 with landscape size ($0.04)
curl -X POST http://localhost:8402/v1/images/generations \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/dall-e-3",
    "prompt": "a serene Japanese garden in autumn",
    "size": "1792x1024",
    "n": 1
  }'
```

### TypeScript / Node.js

```typescript
const response = await fetch("http://localhost:8402/v1/images/generations", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "google/nano-banana",
    prompt: "a golden retriever surfing on a wave",
    size: "1024x1024",
    n: 1,
  }),
});

const result = (await response.json()) as {
  created: number;
  data: Array<{ url: string; revised_prompt?: string }>;
};

const imageUrl = result.data[0].url;
console.log(imageUrl); // https://files.catbox.moe/xxx.png
```

### Python

```python
import requests

response = requests.post(
    "http://localhost:8402/v1/images/generations",
    json={
        "model": "google/nano-banana",
        "prompt": "a golden retriever surfing on a wave",
        "size": "1024x1024",
        "n": 1,
    }
)

result = response.json()
image_url = result["data"][0]["url"]
print(image_url)
```

### OpenAI SDK (drop-in)

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "blockrun", // any non-empty string
  baseURL: "http://localhost:8402/v1",
});

const response = await client.images.generate({
  model: "google/nano-banana",
  prompt: "a golden retriever surfing on a wave",
  size: "1024x1024",
  n: 1,
});

console.log(response.data[0].url);
```

### startProxy (programmatic)

If you're using ClawRouter as a library:

```typescript
import { startProxy } from "@blockrun/clawrouter";

const proxy = await startProxy({ walletKey: process.env.BLOCKRUN_WALLET_KEY! });

const response = await fetch(`${proxy.baseUrl}/v1/images/generations`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "openai/dall-e-3",
    prompt: "a serene Japanese garden in autumn",
    size: "1792x1024",
    n: 1,
  }),
});

const { data } = await response.json();
console.log(data[0].url);

await proxy.close();
```

### Image Editing Examples {#image-editing-examples}

### curl

```bash
# Using a local file path (simplest)
curl -X POST http://localhost:8402/v1/images/image2image \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "add sunglasses to the person",
    "image": "~/photo.png"
  }'

# Using an image URL
curl -X POST http://localhost:8402/v1/images/image2image \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "change the background to a sunset beach",
    "image": "https://example.com/photo.png"
  }'

# With a mask (inpainting — white = area to edit)
curl -X POST http://localhost:8402/v1/images/image2image \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "replace the background with a starry sky",
    "image": "~/photo.png",
    "mask": "~/mask.png"
  }'

# With explicit model, size, and base64 data URI
curl -X POST http://localhost:8402/v1/images/image2image \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-image-1",
    "prompt": "add a crown",
    "image": "data:image/png;base64,iVBOR...",
    "size": "1536x1024"
  }'
```

### TypeScript / Node.js

```typescript
// ClawRouter reads the file for you — no base64 encoding needed
const response = await fetch("http://localhost:8402/v1/images/image2image", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    prompt: "change the background to a starry sky",
    image: "/Users/me/photo.png", // or "~/photo.png" or an HTTPS URL
  }),
});

const result = (await response.json()) as {
  created: number;
  data: Array<{ url: string; revised_prompt?: string }>;
};

console.log(result.data[0].url); // http://localhost:8402/images/xxx.png
```

### Python

```python
import requests

response = requests.post(
    "http://localhost:8402/v1/images/image2image",
    json={
        "prompt": "add a hat to the person",
        "image": "~/photo.png",  # or an absolute path or HTTPS URL
    },
)

result = response.json()
print(result["data"][0]["url"])
```

---

## In-Chat Commands

When using ClawRouter with OpenClaw, generate and edit images directly from any conversation:

### `/cr-imagegen` — Generate images

```
/cr-imagegen a dog dancing on the beach
/cr-imagegen --model dall-e-3 a futuristic city at sunset
/cr-imagegen --model banana-pro --size 2048x2048 mountain landscape
```

> Registered slash command: `/cr-imagegen`. The `cr-` prefix avoids colliding with Telegram channels that reserve `/imagegen` for their own image-gen bots. Typing the legacy `/imagegen` in chat still works for backward compatibility.

| Flag      | Default       | Description           |
| --------- | ------------- | --------------------- |
| `--model` | `nano-banana` | Model shorthand or ID |
| `--size`  | `1024x1024`   | Image dimensions      |

### `/img2img` — Edit images

```
/img2img --image ~/photo.png change the background to a starry sky
/img2img --image ./cat.jpg --mask ./mask.png remove the background
/img2img --image /tmp/portrait.png --size 1536x1024 add a hat
```

| Flag      | Default       | Description                           |
| --------- | ------------- | ------------------------------------- |
| `--image` | _(required)_  | Local image file path (supports `~/`) |
| `--mask`  | _(none)_      | Mask image (white = area to edit)     |
| `--model` | `gpt-image-1` | Model to use                          |
| `--size`  | `1024x1024`   | Output size                           |

### Model shorthands

| Shorthand     | Full ID                     |
| ------------- | --------------------------- |
| `nano-banana` | `google/nano-banana`        |
| `banana-pro`  | `google/nano-banana-pro`    |
| `dall-e-3`    | `openai/dall-e-3`           |
| `gpt-image`   | `openai/gpt-image-1`        |
| `flux`        | `black-forest/flux-1.1-pro` |

---

## Notes

- **Local image caching** — All images (generated and edited) are cached locally at `~/.openclaw/blockrun/images/` and served via `http://localhost:8402/images/`. Both base64 data URIs and HTTP URLs from upstream are downloaded and replaced with localhost URLs.
- **Payment** — Each image costs the listed price in USDC, deducted from your wallet via x402. Make sure your wallet is funded before generating or editing.
- **No DALL-E content policy bypass** — DALL-E 3 and GPT Image 1 still apply OpenAI's content policy. Use `flux` or `nano-banana` for more flexibility with generation.
- **Size limits** — Requesting a size larger than the model's max will return an error. Check the table above before setting `--size`.
- **Image editing** — The `/v1/images/image2image` endpoint currently supports `openai/gpt-image-1` (default). The `image` and `mask` fields accept local file paths (`~/photo.png`, `/abs/path.png`), HTTP/HTTPS URLs, or base64 data URIs. ClawRouter handles file reading and URL downloading automatically. Supported formats: PNG, JPG/JPEG, WebP.
