# App API - Image Generation Service

Image generation is available for every project with no setup needed (owner_pays billing by default).

Use `project_settings` to customize (optional):
- Switch billing mode (owner_pays ↔ user_pays)
- Restrict allowed providers
- Set default provider/model

## Providers & Models
- **OpenAI**: `gpt-image-1`
- **BFL/Flux**: `flux-2-pro, flux-2-flex, flux-2-max, flux-dev`
- **Gemini/Nano Banana**: `gemini-2.5-flash-image, gemini-3.1-flash-image-preview, gemini-3-pro-image-preview`

## Endpoints
- `GET /api/<PROJECT_GUID>/services/image/models` - list available providers and models
- `POST /api/<PROJECT_GUID>/services/image` - generate an image

## Request Format (POST /image)
```json
{
  "prompt": "A sunset over mountains",
  "provider": "openai",
  "model": "gpt-image-1",
  "size": "1024x1024",
  "quality": "auto"
}
```

Fields:
- `prompt` (required): Image description, max 4,000 chars
- `provider`: "openai", "bfl", or "gemini" (default: bfl)
- `model`: Model ID (default: provider's default)
- `size`: "WxH" format (default: 1024x1024). OpenAI sizes: 1024x1024, 1024x1536, 1536x1024. BFL: any size (rounded to 32px). Gemini: mapped to nearest resolution tier
- `quality`: OpenAI gpt-image-1: low/medium/high/auto
- `aspect_ratio`: Gemini only. Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 4:5, 5:4, 21:9
- `image_size`: Gemini only. Resolution tier: 512, 1K, 2K, 4K (default: 1K)

## Response Format
```json
{
  "url": "https://media.gipity.ai/med_abc12345.png",
  "content_type": "image/png",
  "revised_prompt": "...",
  "model": "gpt-image-1",
  "provider": "openai",
  "credits_used": 50
}
```

The `url` is a permanent public CDN URL. No auth needed to fetch it.

## Client Code Example

**IMPORTANT:** The token endpoint is on the API server. Use `https://a.gipity.ai/api/token` (POST).

```js
// 1. Get app token
const tokenRes = await fetch('https://a.gipity.ai/api/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ app: '<PROJECT_GUID>' })
});
const { data: { token } } = await tokenRes.json();

// 2. Generate image
const res = await fetch('https://a.gipity.ai/api/<PROJECT_GUID>/services/image', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-App-Token': token },
  body: JSON.stringify({ prompt: 'A cat wearing a top hat' })
});
const data = await res.json();
// data.url → "https://media.gipity.ai/med_abc12345.png"

// 3. Display
const img = document.createElement('img');
img.src = data.url;
document.body.appendChild(img);
```

## Limits
- **Rate limit**: 600 requests per 5-minute window (per IP)
- **Max prompt length**: 4,000 chars
- **Timeout**: 120s
- Standard `RateLimit-*` headers included in responses
