Gateway API ← Back to UI

Gateway API

OpenAI Responses API–compatible endpoint for PostProdAgent workflows. Workflow list current as of v1.9.2.

The Gateway API exposes PostProdAgent image-generation workflows through an interface compatible with the OpenAI Responses API. Any client that speaks the OpenAI protocol can point its base_url at this server and submit jobs without custom integration.

Jobs are processed asynchronously: the create call returns immediately with a response ID and status: queued; the client polls the GET endpoint until status: completed and then reads the output image(s).

Base URL  —  same host/port as the PostProdAgent UI, e.g. http://<server>:8765

Authentication

All endpoints require HTTP Basic Authentication.

# Header format
Authorization: Basic <base64(username:password)>

# curl shorthand
curl -u admin:admin https://<server>:8765/v1/responses

Models

Each model maps to a PostProdAgent workflow. Pass the model ID in the model field of a create request.

postprod/3d_to_on_model
WKF_IN_3D_TO_ON_MODEL
postprod/accessory_swap_flux
WKF_IN_ACCESSORY_SWAP_FLUX
postprod/bag_video_complex
WKF_IN_BAG_VIDEO_COMPLEX
postprod/breakdance_video
WKF_IN_BREAKDANCE_VIDEO
postprod/dgx_cambio_colore_intimi
WKF_IN_DGX_CAMBIO_COLORE_INTIMI
postprod/dgx_cambio_fondo
WKF_IN_DGX_CAMBIO_FONDO
postprod/dgx_fitting_steso
WKF_IN_DGX_FITTING_STESO
postprod/dgx_post_produzione_gioiello
WKF_IN_DGX_POST_PRODUZIONE_GIOIELLO
postprod/dgx_sam3_01
WKF_IN_DGX_SAM3_01
postprod/dgx_upscaler_svr
WKF_IN_DGX_UPSCALER_SVR
postprod/dgx_vto_01
WKF_IN_DGX_VTO_01
postprod/fabrics_to_mannequin
WKF_IN_FABRICS_TO_MANNEQUIN
postprod/free_style_ai_nano
WKF_IN_FREE_STYLE_AI_NANO
postprod/free_style_ai_op
WKF_IN_FREE_STYLE_AI_OP
postprod/image_ai_postproduction
WKF_IN_IMAGE_AI_POSTPRODUCTION
postprod/outfit_composer
WKF_IN_OUTFIT_COMPOSER
postprod/outfit_composer_with_accessory
WKF_IN_OUTFIT_COMPOSER_WITH_ACCESSORY
postprod/photo_retouch
WKF_IN_PHOTO_RETOUCH
postprod/sketch_acquerello
WKF_IN_SKETCH_ACQUERELLO
postprod/sketches_lp_lavilla_rtw_high
WKF_IN_SKETCHES_LP_LAVILLA_RTW_HIGH
postprod/sketches_lp_lavilla_rtw_medium
WKF_IN_SKETCHES_LP_LAVILLA_RTW_MEDIUM
postprod/sketches_lp_lavilla_shoes_high
WKF_IN_SKETCHES_LP_LAVILLA_SHOES_HIGH
postprod/sketches_lp_lavilla_shoes_medium
WKF_IN_SKETCHES_LP_LAVILLA_SHOES_MEDIUM
postprod/test_kling_vto
WKF_IN_TEST_KLING_VTO
postprod/test_upscale_aura_4x
WKF_IN_TEST_UPSCALE_AURA_4X
postprod/texture_and_sketch_to_ghost
WKF_IN_TEXTURE_AND_SKETCH_TO_GHOST
postprod/video_free_style
WKF_IN_VIDEO_FREE_STYLE
postprod/virtual_try_on
WKF_IN_VIRTUAL_TRY_ON
postprod/auto
explicit workflow field
Use postprod/auto together with the optional workflow field to target any workflow by its internal name (e.g. "workflow": "WKF_IN_CUSTOM_WF").

Image position mapping

Images in content[] are assigned to workflow input slots according to the workflow's image_mapping.json. For virtual_try_on:

PositionSlotDescription
1 (first image)defaultGarment / product photo
2 (second image)_REFModel / avatar reference
3+ (extra images)_REF2, _REF3…Additional references (workflow-dependent)

POST /v1/responses

Create a new job. Returns a response object with status: queued.

POST/v1/responses

Request body

FieldTypeDescription
model requiredstringModel ID (see Models above)
input requiredarrayList of message objects (role + content)
workflow optionalstringExplicit workflow name — used when model=postprod/auto
prompt_append optionalstringExtra text appended to the workflow prompt. Supports SIZE=, QUALITY=, PAD_INPUT= overrides
output_format optionalstringjpeg (default) or png
output_quality optionalintegerJPEG quality 1–100, default 90

Content block types

typeFieldsDescription
input_imageimage_url: data URI or URLImage to process. Data URI format: data:image/jpeg;base64,<b64>
input_texttext: stringText prompt, appended to workflow prompt

Response

{
  "id":         "resp_a3f1c9e20b4d",
  "object":     "response",
  "model":      "postprod/virtual_try_on",
  "status":     "queued",
  "output":     [],
  "usage":      { "input_tokens": 0, "output_tokens": 0, "total_tokens": 0 },
  "created_at": 1718356528
}

GET /v1/responses/{id}

Poll the status of a job. When status is completed, the output array contains the result image(s) as base64 data URIs.

GET/v1/responses/{id}

Status lifecycle

queued  →  in_progress  →  completed  /  failed

Completed response

{
  "id":     "resp_a3f1c9e20b4d",
  "status": "completed",
  "output": [
    {
      "role": "assistant",
      "content": [
        {
          "type":      "image_url",
          "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ..." }
        }
      ]
    }
  ]
}
IDM workflows return only the main result. A workflow using idm_mapper_openai + idm_crop produces several artefacts: the annotated photo, plus one detail crop per detail found. Only the annotated photo comes back here. The crops are written as sibling result folders (JOB_..._IDM_01_<label>), which this API does not walk — it reads *_AI.* inside the job folder alone — so they are reachable from the web UI but not over /v1. The coordinates of every detail are in the job's idm_details.json artifact if you need to reproduce the crops yourself. Exposing derived results over the API is on the roadmap, after the refactoring.

GET /v1/models

List all available models and their availability status. The list is generated dynamically from the workflows currently present on the server (any WKF_IN_* folder is exposed as postprod/<name>) — new, renamed, or removed workflows are reflected automatically. A handful of legacy model-id aliases from before a workflow rename are appended with "legacy_alias": true.

GET/v1/models
{
  "object": "list",
  "data": [
    {
      "id":         "postprod/virtual_try_on",
      "object":     "model",
      "workflow":   "WKF_IN_VIRTUAL_TRY_ON",
      "available":  true
    },
    // ...
  ]
}

Example — Virtual Try-On (2 images)

Send a garment photo (position 1) and a model/avatar photo (position 2) to generate a dressed-on-model result.

# 1. Encode images as base64
GARMENT_B64=$(base64 -i garment.jpg | tr -d '\n')
AVATAR_B64=$(base64 -i avatar.jpg | tr -d '\n')

# 2. Create the job
RESPONSE=$(curl -s -u admin:admin \
  -X POST http://<server>:8765/v1/responses \
  -H "Content-Type: application/json" \
  -d "{
    \"model\": \"postprod/virtual_try_on\",
    \"input\": [{
      \"role\": \"user\",
      \"content\": [
        { \"type\": \"input_image\", \"image_url\": \"data:image/jpeg;base64,${GARMENT_B64}\" },
        { \"type\": \"input_image\", \"image_url\": \"data:image/jpeg;base64,${AVATAR_B64}\" },
        { \"type\": \"input_text\",  \"text\": \"Virtual try-on: indossa il capo su questo avatar\" }
      ]
    }]
  }")

RESP_ID=$(echo $RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Job created: $RESP_ID"

# 3. Poll until completed
while true; do
  POLL=$(curl -s -u admin:admin http://<server>:8765/v1/responses/$RESP_ID)
  STATUS=$(echo $POLL | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  echo "Status: $STATUS"
  [ "$STATUS" = "completed" ] && break
  [ "$STATUS" = "failed" ]    && { echo "Job failed"; exit 1; }
  sleep 5
done

# 4. Extract and save result image
echo $POLL | python3 -c "
import sys, json, base64
data = json.load(sys.stdin)
b64 = data['output'][0]['content'][0]['image_url']['url'].split(',')[1]
with open('result.jpg', 'wb') as f:
    f.write(base64.b64decode(b64))
print('Saved result.jpg')
import base64, time, requests

BASE_URL  = "http://<server>:8765"
AUTH      = ("admin", "admin")
HEADERS   = {"Content-Type": "application/json"}

def encode(path):
    with open(path, "rb") as f:
        return "data:image/jpeg;base64," + base64.b64encode(f.read()).decode()

# 1. Create job
r = requests.post(
    f"{BASE_URL}/v1/responses",
    auth=AUTH, headers=HEADERS,
    json={
        "model": "postprod/virtual_try_on",
        "input": [{
            "role": "user",
            "content": [
                {"type": "input_image", "image_url": encode("garment.jpg")},
                {"type": "input_image", "image_url": encode("avatar.jpg")},
                {"type": "input_text",  "text": "Virtual try-on: indossa il capo su questo avatar"},
            ]
        }]
    }
)
resp_id = r.json()["id"]
print(f"Job created: {resp_id}")

# 2. Poll
while True:
    poll = requests.get(f"{BASE_URL}/v1/responses/{resp_id}", auth=AUTH).json()
    status = poll["status"]
    print(f"Status: {status}")
    if status == "completed": break
    if status == "failed":    raise RuntimeError("Job failed")
    time.sleep(5)

# 3. Save result
url = poll["output"][0]["content"][0]["image_url"]["url"]
img_bytes = base64.b64decode(url.split(",")[1])
with open("result.jpg", "wb") as f:
    f.write(img_bytes)
print("Saved result.jpg")

Example — 3-image workflow

Some workflows accept more than two images (e.g. Outfit Composer: body + item 1 + item 2). Additional images are assigned to slots _REF, _REF2, etc. according to the workflow's image_mapping.json.

BODY_B64=$(base64 -i body.jpg    | tr -d '\n')
ITEM1_B64=$(base64 -i item1.jpg  | tr -d '\n')
ITEM2_B64=$(base64 -i item2.jpg  | tr -d '\n')

curl -s -u admin:admin \
  -X POST http://<server>:8765/v1/responses \
  -H "Content-Type: application/json" \
  -d "{
    \"model\": \"postprod/outfit\",
    \"input\": [{
      \"role\": \"user\",
      \"content\": [
        { \"type\": \"input_image\", \"image_url\": \"data:image/jpeg;base64,${BODY_B64}\" },
        { \"type\": \"input_image\", \"image_url\": \"data:image/jpeg;base64,${ITEM1_B64}\" },
        { \"type\": \"input_image\", \"image_url\": \"data:image/jpeg;base64,${ITEM2_B64}\" },
        { \"type\": \"input_text\",  \"text\": \"Componi l'outfit completo\" }
      ]
    }],
    \"prompt_append\": \"SIZE=1024x1536 QUALITY=high\"
  }
The prompt_append field supports parameter overrides: SIZE=WxH, QUALITY=high|medium|low, PAD_INPUT=white|black|transparent. These take precedence over both engine and workflow defaults.

Example — OpenAI Python SDK

Point the SDK's base_url at the PostProdAgent server. No custom client needed.

from openai import OpenAI
import base64, time

client = OpenAI(
    api_key="admin:admin",          # basic auth as api_key (user:pass)
    base_url="http://<server>:8765",
)

def encode(path):
    with open(path, "rb") as f:
        return "data:image/jpeg;base64," + base64.b64encode(f.read()).decode()

# Create + poll in one call (SDK handles serialization)
response = client.responses.create(
    model="postprod/virtual_try_on",
    input=[{
        "role": "user",
        "content": [
            {"type": "input_image", "image_url": encode("garment.jpg")},
            {"type": "input_image", "image_url": encode("avatar.jpg")},
        ]
    }]
)

resp_id = response.id
while response.status not in ("completed", "failed"):
    time.sleep(5)
    response = client.responses.retrieve(resp_id)
    print(response.status)

url = response.output[0].content[0].image_url.url
img_bytes = base64.b64decode(url.split(",")[1])
open("result.jpg", "wb").write(img_bytes)

Polling pattern reference

Recommended polling parameters:

ParameterRecommended valueNotes
Interval5 secondsMost jobs complete in 20–90 s
Max attempts605 minutes total timeout
BackoffNot requiredServer-side queue is stable
import time, requests

def wait_for_result(base_url, resp_id, auth, interval=5, max_attempts=60):
    for _ in range(max_attempts):
        r = requests.get(f"{base_url}/v1/responses/{resp_id}", auth=auth).json()
        if r["status"] == "completed":
            return r
        if r["status"] == "failed":
            raise RuntimeError(f"Job {resp_id} failed")
        time.sleep(interval)
    raise TimeoutError(f"Job {resp_id} not completed after {max_attempts * interval}s")