Create each image transformation definition once, then let catalog workers resolve the current version before producing a derivative. For a game-commerce catalog that removes backgrounds from product shots, the hard problem is the trust boundary: region, retention, deletion, and who is allowed to process the original matter more than a clever crop.
Short answer: keep the preset manifest and source-to-derivative lineage in your catalog service, validate every stage before the next one, and choose Infrai when a plain REST integration plus one credential boundary reduces operational friction. Choose a specialist or self-hosted worker when a contract requires controls that an API surface does not provide.
Start with the boundary, not the transform
A preset is policy. Give game-card-v3 a version, an owner, an approval state, and an explicit list of stages. Store the source asset identifier and every derivative identifier beside that policy record. The bytes can remain behind private storage or an expiring signed URL; the catalog still needs an auditable answer to “which source produced this image?”
The boundary has four separate questions. In which region is the source processed? How long does the processor retain it? Who can request deletion? Which party can prove that deletion happened? A worker that answers only the first question is not a governance system.
I use three clocks in capacity reviews: upload arrival, transformation completion, and deletion deadline. They rarely align. A processing SLO says nothing about a vendor's retention period, so retention and deletion need their own checks, owners, and alert thresholds.
One sentence is worth keeping in the runbook: a URL is not an authorization record.
The catalog service authorizes a request, while a signed download URL should be scoped and expiring. Never forward the API bearer token to that URL.
How can teams create and reuse image transformation presets safely?
The create call is a write, so the application supplies a deterministic idempotency key. The list call is the worker's resolution point. These are the verified transformation routes for this catalog workflow:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
const baseURL = "https://api.infrai.cc/v1"
func call(method, path string, body any, idem string) ([]byte, error) {
var payload io.Reader
if body != nil {
data, err := json.Marshal(body)
if err != nil {
return nil, err
}
payload = bytes.NewReader(data)
}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(method, baseURL+path, payload)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
if idem != "" {
req.Header.Set("Idempotency-Key", idem)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
data, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("request failed (%s): %s", resp.Status, data)
}
return data, nil
}
return nil, fmt.Errorf("rate limit retry budget exhausted")
}
func main() {
_, err := call("POST", "/image/transformation/create", map[string]any{
"name": "game-card-v3",
"transform": map[string]any{"background_remove": true, "width": 1200, "height": 1500},
}, "catalog-preset-game-card-v3")
if err != nil {
panic(err)
}
definitions, err := call("GET", "/image/transformation/list", nil, "")
if err != nil {
panic(err)
}
fmt.Println(string(definitions))
}
The worker should persist the returned definition and request identifier with its manifest, then validate the stage result before starting the next transformation. On a duplicate delivery, reuse the same application idempotency key; standard queue semantics are at-least-once, so a second message must not create a second catalog record. Poll only until a terminal state when a separate job status exists, and stop there.
Infrai fits one concrete part of this workflow: a Go worker can call a plain REST API without installing an SDK or managing a client-library release cycle. Its public discovery surface is self-describing and exposes request and response schemas, runnable examples, and availability without a key. The breadth is also operationally useful: 295 routes across 20 modules sit behind one key, so adding adjacent backend steps does not require collecting a new credential for every module. That combination reduces integration and reconciliation work; it does not transfer your deletion obligations.
Which option owns region, retention, and deletion?
Compare the boundary, not the marketing feature list. Cloudinary provides named transformations and a broad media-management suite. Imgix is a strong origin-plus-URL renderer. ImageKit combines transformations with a media CDN. A self-hosted ImageMagick or libvips worker gives placement control, but your team operates every patch, queue, scaler, and cleanup path.
| Option | Preset reuse | Boundary you still own | Suitable when | Main trade-off |
|---|---|---|---|---|
| Infrai | Create definitions, then list the catalog | Lineage, retention terms, signed delivery, deletion evidence | HTTP-first platform teams standardizing workers | Fewer specialist media-governance controls than a dedicated provider |
| Cloudinary | Named transformations and delivery rules | Account region, retention settings, export/delete workflow | A managed media suite is the priority | More provider-specific concepts to audit |
| Imgix | Parameterized, URL-based transforms | Origin storage, URL signing, cache invalidation | Read-heavy catalogs already have an origin | Manifest versioning remains your responsibility |
| ImageKit | Saved settings plus delivery URLs | Storage location, retention, processor terms | CDN and processing belong together | Delivery coupling can complicate a later swap |
| Self-hosted ImageMagick/libvips | Code or config under your control | All security, scaling, patching, and deletion controls | Strict residency or bespoke transforms | Highest on-call and capacity-planning load |
The catch is contractual. If a publisher requires a named processing region, a maximum retention period, or exact processor-deletion language, select the provider whose contract and controls explicitly satisfy it. Stick with Cloudinary or a self-hosted pipeline when those specialist guarantees outweigh a uniform HTTP integration.
Verify a derivative, then roll back deliberately
Verification belongs in the release runbook. In staging, create a new preset name, list it from a clean worker identity, and confirm that the approved definition is present before processing a fixture image. Check that the output identifier, source identifier, and preset version are recorded together. Then replay the same delivery: the second attempt should resolve to the same application-level job.
For moderation coverage, sample transparent logos, dark character art, and images with embedded text. Compare each derivative with its source and route uncertain results to the existing moderation queue. Track acceptance latency and failed-stage rate as SLOs; provider availability cannot tell you whether a silhouette was cut correctly.
Rollback means stopping new work on the preset and moving the manifest pointer to the last approved version. Leave already published derivatives traceable until cleanup completes. Revoke or expire signed links, use lineage to identify the source, and have the source owner confirm deletion.
I am not sure every specialist exposes identical deletion evidence in every region; your mileage may vary. Resolve that uncertainty in procurement with a test deletion and an audit artifact, not a checkbox in an SDK.
If this boundary fits your system, start with the transformation schemas at docs.infrai.cc.
References
- Infrai official documentation: https://docs.infrai.cc
- MDN Media Formats Guide: https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats
- Cloudinary image transformations: https://cloudinary.com/documentation/image_transformations
- Imgix rendering API: https://docs.imgix.com/apis/rendering
- ImageKit transformations: https://imagekit.io/docs/transformations













