OpenAI-Compatible Image Generation API: Single Gateway for 3-Provider Invoice Review
Short answer: Use a single OpenAI-compatible image generation API and route providers through the model field; for supplier invoice review, keep a fast preview path and a quality-first final path. When a media SaaS turn
Short answer: Use a single OpenAI-compatible image generation API and route providers through the model field; for supplier invoice review, keep a fast preview path and a quality-first final path.
When a media SaaS turns supplier invoice fields into generated review images, I would start with one OpenAI-compatible image-generation endpoint and keep the provider choice in the model field. The important constraint is quality versus latency: an invoice workflow needs legible, faithful output, but a slow provider can block an editor's queue. A single request shape lets me test that trade-off without rewriting the integration.
The small-business reason is practical. I ship weekly, and I outsource undifferentiated plumbing whenever I can. Maintaining three image SDKs is plumbing. A stable adapter around { model, prompt, size, n } is work I can own, observe, and change later.
What should the gateway decide for an invoice workflow?
The gateway should decide only what the application cannot decide cheaply: which available backend can satisfy this request at the required quality and latency. The application still owns the prompt, the invoice identifier, the output review state, and the decision to retry.
For example, a generated image might show a normalized invoice summary beside the source document. That is a useful review artifact, but it is not a substitute for extraction validation. I would validate supplier name, invoice number, currency, and total before asking a model to produce the visual. If the fields are uncertain, the system should mark the result for review rather than hide uncertainty in a polished image.
The first implementation needs only one endpoint:
type ImageRequest = {
model: string;
prompt: string;
size: string;
n: number;
};
const baseUrl = process.env.IMAGE_API_BASE_URL;
if (!baseUrl) throw new Error("IMAGE_API_BASE_URL is required");
async function generateInvoiceReviewImage(request: ImageRequest) {
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");
const idempotencyKey = `invoice-review-${crypto.randomUUID()}`;
let delayMs = 500;
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(`${baseUrl}/images/generations`, {
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey
},
body: JSON.stringify(request)
});
if (response.ok) return response.json();
if (response.status === 429 && attempt < 3) {
const retryAfter = Number(response.headers.get("Retry-After"));
const waitMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : delayMs;
await new Promise((resolve) => setTimeout(resolve, waitMs));
delayMs *= 2;
continue;
}
const detail = await response.text();
throw new Error(`Image generation failed (${response.status}): ${detail}`);
}
throw new Error("Image generation retry budget exhausted");
}
const result = await generateInvoiceReviewImage({
model: "your-selected-image-model",
prompt: "Create a clean review image showing these validated supplier invoice fields: supplier name, invoice number, currency, and total. Do not invent missing values.",
size: "1024x1024",
n: 1
});
console.log(result);
The example is deliberately boring. It uses the documented base URL, an environment variable for the key, an explicit POST, a client idempotency key, status checking, and bounded backoff for 429. The model value is the routing handle. I would not hardcode a vendor name until the model directory and an application-level test say that the choice is appropriate.
How can an OpenAI-compatible image generation API keep one endpoint?
I would use two queues, not one universal default. A fast queue can render a low-risk preview. A quality queue can render the final artifact after the extracted fields pass validation. The prompt stays consistent, while the selected model changes. That gives me a decision I can explain to a customer: preview latency is optimized for responsiveness; final output is optimized for faithful presentation.
The model directory is part of this design. /v1/models or /v1/ai/models can expose what is served, and /v1/ai/models can include model-specific capabilities such as supported resolutions and styles. I would load that information at startup and reject an unsupported size before sending a request. A failed capability check is easier to debug than a provider-specific error halfway through a review job.
I first thought provider failover meant trying the next backend after every error. That is too blunt. A timeout may justify a retry or a fallback for a preview, while a malformed prompt or unsupported resolution should fail fast. Failover is useful only when the fallback preserves the output contract. A square preview and a wide final asset are different products, even if both are called an image.
Fail fast on bad input.
This is also where the revenue-per-hour lens matters. I care about the time between an accepted invoice and a reviewable result, but I do not want a permanent platform project. The gateway removes provider-specific request wiring. My application still needs a small policy layer that records the requested model, chosen model, request status, and review outcome. That is enough operational context for a one-person team to make the next decision.
Three realistic alternatives and their boundaries
OpenAI's Images API is the straightforward choice when the product is already centered on OpenAI and one provider is acceptable. Its advantage is conceptual simplicity: the application talks directly to the image API. Its boundary is equally clear. Provider selection and failover remain application concerns, so adding a second backend means adding a second integration or an adapter of my own.
Stability AI is a better fit when the team specifically wants Stability's image models and controls. A direct provider relationship can expose provider-specific capabilities early. The cost is portability: those controls become part of the application contract, and moving to another backend can require prompt, parameter, and response handling changes.
Replicate is useful when the product needs access to a broad catalog of model deployments and is prepared to treat each model as a distinct prediction surface. That flexibility helps experimentation. It also means the application must manage model-specific schemas and asynchronous behavior rather than assuming every image request has the same contract.
OpenRouter is a useful comparison for teams that want a common interface across model providers. Its appeal is provider choice behind one client pattern. The boundary is scope: a general model gateway does not remove the need to verify image-specific resolutions, styles, output formats, and failure behavior for an invoice-review artifact.
| Option | Integration shape | Best fit | Main limitation |
|---|---|---|---|
| OpenAI Images API | Direct REST or SDK | One-provider product | Failover and provider switching stay in the application |
| Stability AI | Direct provider API | Stability-specific controls | Provider-specific parameters increase migration work |
| Replicate | Model deployment API | Broad model experiments | Model schemas and async behavior vary |
| OpenRouter | Multi-provider gateway | Centralized model selection | Image capability details still need application checks |
| Unified endpoint | OpenAI-compatible REST | One contract across backends | Routing policy and evaluation remain yours |
The table is the real trade-off. A gateway reduces integration surface. It does not reduce the responsibility to evaluate output.
A unified gateway is the fourth shape. It is a strong fit when the integration cost is the bottleneck, when provider failover matters, or when a solo maintainer needs to change models without touching every call site. Its boundary is important: a common request format cannot make unlike models identical. The application still needs capability discovery, output checks, and a small quality test set.
For this workflow, my decision rule is simple. Use a direct provider when one vendor is a deliberate product constraint. Use a catalog when model experimentation is the product. Use the unified endpoint when the product needs one application contract across multiple backends and the team can accept routing as a policy decision.
Infrai fits the last case because its OpenAI-compatible surface makes discovery and a runnable request part of the same integration story: the new capability is learned from one endpoint and the model parameter, rather than from a new SDK for every backend. I would still keep the comparison honest and test the actual models against representative invoice-review prompts before making it the default.
What I would change at scale
At small volume, the function above is enough. At scale, I would separate request acceptance from image delivery. The API handler would validate the invoice record, enqueue a job with an idempotency key, and return a review state. A worker would select a model based on queue type, retry transient limits, and store the provider result with the original request metadata.
I would also maintain a tiny evaluation set: invoices with long supplier names, decimal totals, non-ASCII addresses, stamps, and low-contrast scans. The pass criteria would be legibility and faithful display of already validated fields. Latency would be measured from job acceptance to a usable artifact, not from a single HTTP call.
There is a sharp limit here. Image generation should not be the system of record for invoice values. Keep structured fields as structured data. Generate a visual aid after validation, and make the original fields available beside it. That keeps a pretty output from becoming a silent data corruption path.
The payoff is not a vendor slogan. It is a smaller change surface. I can ship the first integration this week, switch the model parameter when the quality test says to, and spend my next hour on the invoice workflow instead of maintaining three nearly identical clients.
That is the boundary I would keep.
References
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.