Image Generation & Editing

May 13, 2026 · View on GitHub

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

Table of Contents


Quick Start

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

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:

{
  "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 IDShorthandPriceMax SizeProvider
google/nano-banananano-banana$0.05/image1024×1024Google Gemini Flash
google/nano-banana-probanana-pro$0.10/image4096×4096Google Gemini Pro
openai/dall-e-3dall-e-3$0.04/image1792×1024OpenAI DALL-E 3
openai/gpt-image-1gpt-image$0.02/image1536×1024OpenAI GPT Image
black-forest/flux-1.1-proflux$0.04/image1024×1024Black 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:

FieldTypeRequiredDescription
modelstringYesModel ID (see table above)
promptstringYesText description of the image to generate
sizestringNoImage dimensions, e.g. "1024x1024" (default)
nnumberNoNumber of images (default: 1)

Response:

{
  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:

FieldTypeRequiredDescription
modelstringNoModel ID (default: openai/gpt-image-1)
promptstringYesText description of the edit to apply
imagestringYesSource image — see Image input formats below
maskstringNoMask image (white = area to edit) — same formats as image
sizestringNoOutput dimensions, e.g. "1024x1024" (default)

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

FormatExampleDescription
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:

{
  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

# 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

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

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)

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:

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

# 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

// 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

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.

FlagDefaultDescription
--modelnano-bananaModel shorthand or ID
--size1024x1024Image 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
FlagDefaultDescription
--image(required)Local image file path (supports ~/)
--mask(none)Mask image (white = area to edit)
--modelgpt-image-1Model to use
--size1024x1024Output size

Model shorthands

ShorthandFull ID
nano-bananagoogle/nano-banana
banana-progoogle/nano-banana-pro
dall-e-3openai/dall-e-3
gpt-imageopenai/gpt-image-1
fluxblack-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.