{
  "name": "FireRed LoRA Image Edit API",
  "version": "1.0.0",
  "base_url": "https://firered-lora-api.vercel.app",
  "description": "AI image editing API powered by FireRed-Edit-1.1 + Lightning LoRA. Send an image and a text prompt, get an edited image back in ~5 seconds.",
  "authentication": {
    "type": "API Key",
    "header": "X-API-Key",
    "key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5",
    "note": "Include this header in ALL requests to /api/* endpoints."
  },
  "endpoints": [
    {
      "method": "POST",
      "path": "/api/edit",
      "description": "Edit an image using AI. Synchronous — returns the edited image in the response (~5-13s depending on network).",
      "headers": {
        "Content-Type": "application/json",
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "request_body": {
        "image": {
          "type": "string",
          "required": true,
          "description": "Base64-encoded input image (JPEG or PNG). Max recommended size: 1024x1024."
        },
        "prompt": {
          "type": "string",
          "required": true,
          "description": "Edit instruction in plain English. Examples: 'Change the shirt to red', 'Add sunglasses', 'Make the background a beach'."
        },
        "steps": {
          "type": "integer",
          "required": false,
          "default": 6,
          "description": "Inference steps. 6 is optimal. Range: 4-20. Higher = slower but potentially better quality."
        },
        "cfg": {
          "type": "float",
          "required": false,
          "default": 1.0,
          "description": "Classifier-free guidance scale. 1.0 = fastest (single pass). >1.0 doubles latency. Recommended: 1.0."
        },
        "seed": {
          "type": "integer",
          "required": false,
          "default": 42,
          "description": "Random seed for reproducibility. Same seed + same input = same output."
        },
        "priority": {
          "type": "integer",
          "required": false,
          "default": 1,
          "description": "Job priority. 0 = highest, 1 = normal, 2 = low."
        },
        "client_id": {
          "type": "string",
          "required": false,
          "default": null,
          "description": "Optional identifier for the client/user submitting the job. Used for tracking."
        }
      },
      "response_200": {
        "job_id": "uuid — unique job identifier",
        "status": "completed",
        "actual_time_s": 3.48,
        "output_image_url": "URL if stored on CDN, empty string otherwise",
        "output_b64": "base64-encoded PNG of the edited image (if no CDN URL)",
        "config": {
          "steps": 6,
          "cfg": 1.0,
          "seed": 42
        },
        "input_size": "512x512",
        "output_size": "1024x1024"
      },
      "errors": {
        "401": "Invalid or missing API key",
        "500": "Edit failed (model error)",
        "502": "GPU backend error (Baseten unreachable or timeout)",
        "503": "No GPU backend configured"
      },
      "example_curl": "curl -X POST https://firered-lora-api.vercel.app/api/edit -H 'Content-Type: application/json' -H 'X-API-Key: fr-live-k8x9mP2qR7vN4wJ6tB3nF5' -d '{\"image\": \"<base64>\", \"prompt\": \"Change shirt to blue\"}'",
      "example_python": "import requests, base64\n\nwith open('photo.jpg', 'rb') as f:\n    img_b64 = base64.b64encode(f.read()).decode()\n\nresp = requests.post(\n    'https://firered-lora-api.vercel.app/api/edit',\n    headers={'X-API-Key': 'fr-live-k8x9mP2qR7vN4wJ6tB3nF5'},\n    json={'image': img_b64, 'prompt': 'Add sunglasses'},\n    timeout=60\n)\nresult = resp.json()\nif result['output_b64']:\n    with open('edited.png', 'wb') as f:\n        f.write(base64.b64decode(result['output_b64']))",
      "example_javascript": "async function editImage(imageBase64, prompt) {\n  const resp = await fetch('https://firered-lora-api.vercel.app/api/edit', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-API-Key': 'fr-live-k8x9mP2qR7vN4wJ6tB3nF5'\n    },\n    body: JSON.stringify({ image: imageBase64, prompt })\n  });\n  return resp.json();\n}"
    },
    {
      "method": "GET",
      "path": "/api/status/{job_id}",
      "description": "Get details of a specific job, including the output image.",
      "headers": {
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "path_params": {
        "job_id": "UUID of the job returned from /api/edit"
      },
      "response_200": {
        "job_id": "uuid",
        "status": "completed | processing | queued | failed | cancelled",
        "created_at": "ISO 8601 timestamp",
        "prompt": "the edit prompt",
        "steps": 6,
        "cfg": 1.0,
        "seed": 42,
        "client_id": "optional client identifier",
        "output_image_url": "CDN URL (if available)",
        "output_b64": "base64 PNG (if no CDN URL)",
        "actual_time_s": 3.48,
        "completed_at": "ISO 8601 timestamp"
      },
      "errors": {
        "401": "Invalid API key",
        "404": "Job not found"
      }
    },
    {
      "method": "GET",
      "path": "/api/history",
      "description": "Paginated list of all jobs. Returns metadata only (no images). Click into /api/status/{id} for full details.",
      "headers": {
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "query_params": {
        "page": {"type": "integer", "default": 1, "description": "Page number (1-indexed)"},
        "limit": {"type": "integer", "default": 20, "description": "Jobs per page (max 100)"},
        "status": {"type": "string", "required": false, "description": "Filter by status: completed, processing, queued, failed, cancelled"}
      },
      "response_200": {
        "jobs": ["array of job objects (without base64 images)"],
        "total": 42,
        "page": 1,
        "limit": 20,
        "pages": 3
      }
    },
    {
      "method": "GET",
      "path": "/api/queue",
      "description": "Queue statistics: counts by status, average processing time, throughput, GPU status.",
      "headers": {
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "response_200": {
        "queued": 0,
        "processing": 0,
        "completed": 3,
        "failed": 0,
        "cancelled": 0,
        "avg_time_s": 3.51,
        "throughput_per_min": 17.1,
        "total_jobs": 3,
        "gpu": {
          "status": "ACTIVE",
          "active_replicas": 1,
          "min_replicas": 0,
          "max_replicas": 1,
          "instance_type": "H100 - 1 H100 GPU, 80 GiB VRAM"
        }
      }
    },
    {
      "method": "POST",
      "path": "/api/gpu/scale",
      "description": "Scale the number of GPU replicas. More replicas = more concurrent edits.",
      "headers": {
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "query_params": {
        "replicas": {"type": "integer", "default": 1, "description": "Minimum number of GPU replicas to keep running"}
      },
      "response_200": {
        "status": "scaled",
        "min_replicas": 2,
        "max_replicas": 4
      },
      "note": "Each replica is 1x H100 GPU (~$2.50/hr). Scale to 0 to stop billing (cold start on next request ~2 min)."
    },
    {
      "method": "POST",
      "path": "/api/job/{job_id}/cancel",
      "description": "Cancel a queued or processing job.",
      "headers": {
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "response_200": {
        "status": "cancelled",
        "job_id": "uuid"
      }
    },
    {
      "method": "POST",
      "path": "/api/job/{job_id}/retry",
      "description": "Retry a failed job (max 3 retries).",
      "headers": {
        "X-API-Key": "fr-live-k8x9mP2qR7vN4wJ6tB3nF5"
      },
      "response_200": {
        "status": "requeued",
        "job_id": "uuid",
        "retry_count": 1
      }
    }
  ],
  "model_details": {
    "name": "FireRed-Edit-1.1 + Lightning LoRA",
    "base_model": "FireRedTeam/FireRed-Image-Edit-1.1",
    "lora": "FireRedTeam/FireRed-Image-Edit-LoRA-Zoo (Lightning 8-step)",
    "inference_time": "~3.5s on H100",
    "output_resolution": "1024x1024 (upscaled from input)",
    "quality_score": "9.33/10 (Gemini 3.1 Pro eval)",
    "identity_preservation": "10.0/10"
  },
  "rate_limits": {
    "per_gpu": "~17 edits/minute",
    "scaling": "Linear — 2 GPUs = 34/min, 8 GPUs = 136/min",
    "max_payload_size": "10MB (base64 image)"
  }
}
