# Before I Call GPT Image 2, I Validate These 7 Things

> Source: <https://dev.to/_86f41ebeb2cab43917bd42/before-i-call-gpt-image-2-i-validate-these-7-things-3f6h>
> Published: 2026-09-02 11:45:32+00:00

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.

I do not let one universal form submit every visible field to every image

model. The application selects a model contract first:

``` js
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.

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.

``` js
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.

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.

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.

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:

``` php
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.

For the first run I use:

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.

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.

``` js
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 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:

``` php
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](https://xplaai.com/en-us/api/gpt-image-2/).

Recheck the live contract before production integration.
