Platform / 09 img-forge API

img-forge API

img-forge is StackBilt’s AI image generation service. Submit a text prompt, get back a generated image. Supports multiple quality tiers (Stable Diffusion XL through Gemini), async job queuing, and content-addressed image storage on R2.

Gateway: https://imgforge.stackbilt.dev MCP Server: https://img-forge-mcp.blue-pine-edf6.workers.dev/mcp

Authentication

img-forge supports three auth paths, checked in order by the gateway middleware.

API Key

Include your key in the Authorization header or X-API-Key header:

curl -X POST https://imgforge.stackbilt.dev/v2/generate \
  -H "Authorization: Bearer imgf_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A mountain landscape at sunset"}'

API keys use the imgf_ prefix followed by 64 hex characters. You receive the raw key once at creation — store it securely.

OAuth 2.1 (MCP Clients)

The MCP server acts as both Authorization Server and Resource Server using @cloudflare/workers-oauth-provider. MCP clients follow the standard OAuth 2.1 + PKCE flow:

  1. Discover endpoints via /.well-known/oauth-authorization-server
  2. Register dynamically at /register (RFC 7591)
  3. Redirect to /authorize with PKCE challenge
  4. User logs in via Better Auth and grants consent
  5. Exchange auth code for access token at /token

Token lifetimes: Access token 1 hour, refresh token 30 days. Scopes: generate, read

First-time users are auto-provisioned with a free-tier tenant and 100 images/month entitlement on consent approval.

Anonymous

No credentials required. Rate-limited to 100 images/month per IP address.

REST API

Generate an Image

POST /v2/generate

Submit a generation request. Returns immediately with a job ID (async) or waits for completion (sync).

Request body:

FieldTypeRequiredDefaultDescription
promptstringYesText description, 1–2000 characters
negative_promptstringNoThings to exclude (effective on draft tier only)
quality_tierstringNostandarddraft, standard, premium, ultra, ultra_plus
syncbooleanNofalseWait for completion before responding
idempotency_keystringNoDeduplication key (24h TTL)

Example (async):

curl -X POST https://imgforge.stackbilt.dev/v2/generate \
  -H "Authorization: Bearer imgf_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Isometric pixel art of a cloud server room",
    "quality_tier": "premium"
  }'

Response (202 Accepted):

{
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "state": "queued",
  "original_prompt": "Isometric pixel art of a cloud server room",
  "final_prompt": "...",
  "enhancement_logic": "...",
  "asset_url": null,
  "error": null,
  "created_at": "2026-03-04T12:00:00.000Z",
  "completed_at": null
}

When sync: true, the response is 201 Created with state: "completed" and asset_url populated.

Poll Job Status

GET /v2/jobs/:id

Check the state of a generation job. Jobs are scoped to the authenticated tenant.

Response:

{
  "job_id": "a1b2c3d4-...",
  "state": "completed",
  "original_prompt": "...",
  "final_prompt": "...",
  "enhancement_logic": "...",
  "asset_url": "/v2/assets/sha256hash",
  "error": null,
  "created_at": "2026-03-04T12:00:00.000Z",
  "completed_at": "2026-03-04T12:00:08.000Z"
}

Job states: queuedprocessingcompleted | failed

Jobs that remain in processing for more than 60 seconds are automatically marked failed with a timeout error.

Retrieve an Image

GET /v2/assets/:id

Stream the generated image from R2. Images are content-addressed by SHA-256 hash.

Returns image/png with Cache-Control: public, max-age=3600. Returns 404 if the asset does not exist.

Health Check

GET /v2/health

Returns { "status": "ok", "version": "0.2.0" }.

Quality Tiers

TierProviderModelNegative PromptDefault Size
draftCloudflare AIStable Diffusion XL LightningYes1024×1024
standardCloudflare AIFLUX.2 Klein 4BNo1024×768
premiumCloudflare AIFLUX.2 DevNo1024×768
ultraGeminiGemini 2.5 Flash ImageNo1024×1024
ultra_plusGeminiGemini 3.1 Flash Image PreviewNo1024×1024

MCP Tools

Connect MCP-compatible agents to img-forge for programmatic image generation.

Endpoint: https://img-forge-mcp.blue-pine-edf6.workers.dev/mcp

Claude Code Configuration

Add to your MCP settings:

{
  "mcpServers": {
    "img-forge": {
      "url": "https://img-forge-mcp.blue-pine-edf6.workers.dev/mcp"
    }
  }
}

generate_image

Generate an image from a text prompt. Requires generate scope.

ParameterTypeRequiredDefaultDescription
promptstringYesText description, 1–2000 characters
quality_tierenumNostandarddraft, standard, premium, ultra, ultra_plus
negative_promptstringNoExclusions (effective on draft tier only)

The MCP tool always uses sync mode — it returns the completed image URL directly.

list_models

List all available quality tiers with their providers, models, and default sizes. Requires read scope. Takes no parameters.

check_job

Check the status of a generation job. Requires read scope.

ParameterTypeRequiredDescription
job_idstring (UUID)YesThe job ID to check

Rate Limits

Auth MethodQuotaPeriodEnforcement
Anonymous100 imagesCalendar monthPer IP, via KV
API key (free tier)100 imagesCalendar monthPer tenant, via D1 entitlements
OAuth / MCP (free tier)100 imagesCalendar monthPer tenant, via D1 entitlements

When quota is exceeded, the API returns 429 with error code QUOTA_EXCEEDED (authenticated) or RATE_LIMITED (anonymous).

Tenant Management

Authenticated users can manage their API keys through tenant endpoints.

Create Tenant

POST /v2/tenants

Requires a Better Auth session. Returns the raw API key once — it cannot be retrieved again.

{
  "tenant_id": "uuid",
  "api_key": "imgf_...",
  "api_key_prefix": "imgf_abcd1234",
  "scopes": ["generate", "read"],
  "tier": "free"
}

List Tenants

GET /v2/tenants

Returns all tenants for the authenticated user. Does not include raw API keys, only prefixes.

Rotate API Key

POST /v2/tenants/:id/rotate

Invalidates the current key and returns a new one.

Check Usage

GET /v2/tenants/:id/usage

Returns active entitlements and total job count:

{
  "tenant_id": "...",
  "tier": "free",
  "total_jobs": 12,
  "entitlements": [
    {
      "type": "standard",
      "quota_limit": 100,
      "quota_used": 12,
      "remaining": 88,
      "period_start": "2026-03-01T00:00:00Z",
      "period_end": "2026-03-31T23:59:59Z",
      "source": "img-forge-free"
    }
  ]
}

TypeScript Example

const GATEWAY = "https://imgforge.stackbilt.dev";
const API_KEY = "imgf_your_key_here";

// Generate (async)
const genRes = await fetch(`${GATEWAY}/v2/generate`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    prompt: "A neon-lit cyberpunk alleyway",
    quality_tier: "premium",
  }),
});
const job = await genRes.json();
console.log("Job ID:", job.job_id);

// Poll until complete
let result = job;
while (result.state !== "completed" && result.state !== "failed") {
  await new Promise((r) => setTimeout(r, 2000));
  const pollRes = await fetch(`${GATEWAY}/v2/jobs/${job.job_id}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
  });
  result = await pollRes.json();
}

if (result.state === "completed") {
  console.log("Image:", `${GATEWAY}${result.asset_url}`);
}