API
One call. Same refiner.
POST a prompt, poll the run, and get every pass with critique and prompt-match score. The API uses the same spend rules as the web app: 15cr converged, free-with-cap close/no-lock outcomes, 2cr hedge past cap, and up-front reserve/refund for full renders.
Authentication
For programmatic API use, pass your key as a bearer token or use the X-API-Key header. The same key works across products. Browser session auth is separate; /refine/mine lists runs for the signed-in web session only.
Authorization: Bearer csk_your_key_here # or X-API-Key: csk_your_key_here
One key, every product — the same csk_ key authenticates to both estimate and refine and spends from your one shared codershack wallet. See and manage all your keys at https://wallet.codershack.com/account.
POST /refine
Submit a run. Accepts application/json or multipart/form-data (for image file uploads). The response returns a job_id to poll. Full runs charge on the outcome; preview runs are free.
# −15 cr; close/diverged free (3/day)
curl -X POST https://refine.codershack.com/refine \
-H "Authorization: Bearer csk_your_key_here" \
-H "Content-Type: application/json" \
-d '{"prompt": "A backlit portrait bust with violet rim light"}'
Response:
{ "job_id": "8f3c1e2a-..." }
Request body
| Field | Type | Notes |
|---|---|---|
prompt | string | Required. 1–8000 chars. Prose, or a JSON brief (see JSON mode). |
params | object | Optional generation params (below). Unknown keys ignored. |
max_iters | int | null | Cap the number of passes. Server clamps to max_image_iters (5); default runs to the cap. |
preview | bool | Default false. Preview mode (below). |
input_images | array | Image-to-image / reference input. Up to 8 {url XOR b64, media_type?}. Exactly one of url / b64 per image. |
reference_refine | bool | null | With input_images: true starts pass 1 from the reference; false/omitted uses the reference for critique only. |
prompt_format | string | "auto" (default) · "prose" · "json". See JSON mode. |
params
| Field | Type | Notes |
|---|---|---|
width | int | 256–2560, multiple of 32. Default 1024. |
height | int | 256–2560, multiple of 32. Default 1024. |
seed | int | null | Optional. Fix for reproducible generation. |
width × height must not exceed 4MP.
multipart/form-data
For file uploads, POST multipart/form-data with fields prompt, params (a JSON string), prompt_format, max_iters, preview, reference_refine, and one or more input_images file parts — in addition to the JSON body form above.
Preview mode & unlock
Set "preview": true for a fast, free look: 2 passes, the base arm only, no tournament. Previews are free subject to a daily cap (3/day). A preview that converges is locked — the full render is withheld (preview_locked: true, image_url: null) until you unlock it.
curl -X POST https://refine.codershack.com/refine/{job_id}/unlock \
-H "Authorization: Bearer csk_your_key_here"
Unlock spends 15 credits and returns the job status with the full render now available. Close/diverged previews are not locked — their best pass is downloadable for free.
JSON structured prompt mode
When prompt_format is "json" — or "auto" and the prompt parses as a top-level JSON object — the prompt is treated as a structured brief. The refiner pins your leaf values (yours always win), deep-merges its own enrichment under them, sends compact JSON to JSON-capable arms and a deterministic prose flattening to the others, all within an 8000-char budget. Unparseable JSON (or "auto" with a non-JSON prompt) degrades to prose.
The request field prompt is still a string, so send the brief as serialized JSON:
{
"prompt": "{\"subject\":\"a backlit portrait bust\",\"style\":\"editorial studio photography\",\"lighting\":\"violet rim light, soft key\",\"camera\":{\"lens\":\"85mm\",\"aperture\":\"f/1.8\"}}",
"prompt_format": "json"
}
Every leaf you set (subject, lighting, camera.lens, …) is preserved verbatim; enrichment only adds keys you left out. It never overwrites what you specified.
GET /refine/{job_id}
Poll until status is done or failed. Ownership-enforced: you can only poll your own jobs (403 otherwise). Close and diverged runs still expose the best pass via image_url; locked previews return image_url: null until unlocked.
{
"status": "done",
"verdict": "converged",
"prompt_match": 0.94,
"passes": 3,
"max_passes": 5,
"mode": "full",
"preview_locked": false,
"unlocked": false,
"image_url": "https://refine.codershack.com/refine/images/8f3c.../3.png",
"input_image_urls": [],
"error": null,
"history": [
{
"pass": 3,
"match": 0.94,
"note": "",
"render_call": "flux.2-pro",
"critic": "claude-opus-4-8",
"tokens": 4820,
"latency_ms": 7310,
"verdict": "CONVERGED",
"image_url": "https://refine.codershack.com/refine/images/8f3c.../3.png",
"prompt": "A backlit portrait bust with violet rim light, ..."
}
]
}
| Field | Type | Notes |
|---|---|---|
status | string | queued · running · done · failed. |
verdict | string | null | converged · close · diverged. Null until the run finishes. |
prompt_match | float | null | Best pass's critic score, 0–1. |
passes | int | Passes run. |
max_passes | int | null | Cap for this run. |
mode | string | full · preview. |
preview_locked | bool | A converged preview awaiting unlock. |
unlocked | bool | Whether a preview has been unlocked. |
image_url | string | null | Final or best render. Null while no render exists, or while a converged preview is locked. |
input_image_urls | array | Stored refs for any input images you sent. |
error | string | null | Set when status is failed. |
history[] | array | Per pass: pass, match, note, render_call, critic, tokens (per-call token usage), latency_ms, verdict, image_url, prompt. |
GET /refine/mine
List the signed-in web session's runs. This endpoint uses the browser session and returns 401 when there is no session; API keys are not accepted here. Query params limit (1–100, default 20) and offset. Returns { "jobs": [...], "limit", "offset" }; each job carries id, created_at, prompt, verdict, prompt_match, passes, mode, preview_locked, unlocked, and a thumbnail_ref (null while a preview is locked).
GET /refine/images/{job_id}/{image_name}
Fetch a stored render or input image (PNG). Ownership-enforced (403 otherwise). A locked preview's renders return 402 until you unlock — input images stay accessible. Use the image_url / input_image_urls values from the poll payload directly.
Credits
// −15 cr; close/diverged free (3/day)
Converged full runs charge 15 credits. Close and diverged runs return the pass history and the best pass for free up to the daily cap; past the cap, API hedges settle 2 credits. Full runs reserve 15 credits up front and refund when the outcome is free. Previews are free (3/day); unlocking a converged preview spends 15 credits.