I used to debug a bad AI product image by rewriting the prompt. Now I check
the request contract first.
A GPT Image 2 API request can be valid JSON and still be wrong for the selected
model. A generic UI can leak a field from another provider. Two dimension
controls can conflict. A timeout can turn one intended image into two
generations. A technically successful output can still change the product.
This is the seven-check preflight I put in front of the generation call.
Disclosure: I work on XPLA. The workflow below is an engineering pattern,
not a promise about price, access, speed or image quality.
1. Select the contract before rendering the form
I do not let one universal form submit every visible field to every image
model. The application selects a model contract first:
const imageContracts = {
"gpt-image-2": {
endpoint: "/v1/images/generations",
allowed: new Set([
"model", "prompt", "image", "images",
"size", "aspectRatio", "quality", "replyType", "n"
]),
forbidden: new Set(["imageSize"])
}
};
The UI should be generated from that record. A control the selected model does
not support should not appear.
2. Reject unknown fields instead of dropping them
Silently removing an unsupported field creates plausible but uncontrolled
outputs. The user chooses a setting, the backend drops it, a default is used,
and everyone blames the model.
function rejectUnknownFields(body, contract) {
const errors = [];
for (const key of Object.keys(body)) {
if (!contract.allowed.has(key)) {
errors.push(`Unsupported field for ${body.model}: ${key}`);
}
}
for (const key of contract.forbidden) {
if (body[key] !== undefined) {
errors.push(`Forbidden field for ${body.model}: ${key}`);
}
}
return errors;
}
An explicit local error is easier to fix than an attractive image produced
from the wrong request.
3. Make one dimension decision
An image form may expose a ratio, a pixel size, a provider-specific size field
and an orientation inferred from the prompt. I allow one supported decision:
function validateDimensions(body) {
if (body.imageSize !== undefined) {
return ["imageSize is not valid for this standard contract"];
}
const supplied = [
body.size !== undefined,
body.aspectRatio !== undefined
].filter(Boolean).length;
return supplied > 1
? ["Choose size or aspectRatio, not both"]
: [];
}
I store the requested orientation beside the output so QA can verify it.
4. Treat a reference image as a rights record
A reference is more than a URL. I store the product identity and the allowed
transformation:
{
"source_type": "merchant_upload",
"rights_state": "confirmed_for_internal_calibration",
"product_id": "merchant-sku-104",
"variant": "matte-black-500ml",
"must_preserve": [
"single bottle",
"matte black body",
"silver cap",
"existing label geometry"
],
"allowed_changes": [
"background",
"surface",
"lighting direction"
]
}
A marketplace image being public does not automatically grant permission to
download, transform or use it in advertising.
5. Separate retries from new generations
This is the operational rule I care about most.
If a client times out, it may not know whether the server failed before or
after generation. Repeating the request can create another billable task.
Before submission, I persist:
{
"generation_id": "img-job-20260902-001",
"intent": "new_calibration",
"request_hash": "sha256-of-normalized-request",
"state": "submitted",
"attempt": 1,
"result_state": "unknown",
"next_action": "check_before-repeating"
}
My state transition is:
draft
-> approved
-> submitted
-> completed
-> accepted | repair | rejected
submitted
-> transport_unknown
-> reconcile before another generation
A new creative candidate receives a new generation ID. An uncertain transport
result enters reconciliation. I do not hide both actions behind one “Try
again” button.
6. Generate one calibration image
For the first run I use:
- one approved source;
- one product truth sheet;
- one background change;
- one orientation;
- one output;
- one named reviewer.
The question is narrow: can the workflow preserve the product facts that
matter? More outputs do not improve the evidence if no one reviews them.
7. Review product truth, not only aesthetics
My QA table looks like this:
| Check | Expected | Decision |
|---|---|---|
| Product count | one unit | accept / repair / reject |
| Body color | matte black | accept / repair / reject |
| Cap | silver | accept / repair / reject |
| Label geometry | unchanged | accept / repair / reject |
| Invented claims | none | accept / repair / reject |
| Orientation | requested ratio | accept / repair / reject |
| Crop safety | fully visible | accept / repair / reject |
“Looks good” is not a release decision. An attractive output can still invent
a bundle, accessory, certification or quantity.
Complete preflight
function preflightGPTImage2(body, rightsRecord) {
const contract = imageContracts[body.model];
const errors = [];
if (!contract) errors.push(`Unknown model contract: ${body.model}`);
if (!body.prompt?.trim()) errors.push("prompt is required");
if (body.n !== undefined && body.n !== 1) {
errors.push("Use n: 1 and create separate intentional requests");
}
if (body.replyType !== undefined && body.replyType !== "json") {
errors.push('replyType must be "json"');
}
if (contract) {
errors.push(...rejectUnknownFields(body, contract));
errors.push(...validateDimensions(body));
}
if ((body.image || body.images) &&
rightsRecord?.rights_state !== "confirmed_for_internal_calibration") {
errors.push("Reference-image rights are not confirmed");
}
return { ok: errors.length === 0, errors };
}
This is only the request boundary. A production implementation still needs
media-type, file-size, URL, privacy, account, policy and storage checks.
Failure classes need different actions
| Failure class | Example | Default action |
|---|---|---|
| Client | empty prompt | fix locally |
| Contract | unsupported field | reject before submission |
| Access | unauthorized key | stop and fix access |
| Transport | lost response | reconcile before repeating |
| Provider | transient failure | bounded retry policy |
| Safety | risky request | stop or revise |
| Output QA | altered product | repair or reject |
I keep the final order simple:
contract -> preflight -> approval -> one calibration
-> product QA -> intentional scale
The current XPLA-specific request shape and model-name boundaries are
documented in the GPT Image 2 API guide.
Recheck the live contract before production integration.
Top comments (0)