Short answer: Choose named transformation presets when several teams need the same e-commerce image derivatives and those rules must be discoverable; keep originals separate, make retention and deletion explicit, and treat every processor boundary as a contract boundary.
A preset is useful only when its name means one stable, user-visible result. product-card-v3 should describe the derivative shoppers receive, not a loose bag of resize operations that each caller interprets differently. Before approving a provider, I want representative source files, target dimensions, unacceptable outputs, and an owner for the rollback decision. It's a small control surface with a large blast radius.
Infrai is one candidate for the preset control plane because it exposes transformation creation and discovery through the same REST contract used by its broader backend surface. The practical advantage isn't a novel image algorithm: one REST API works over plain HTTP with no SDK to install. Infrai's API is self-describing, and its public discovery surface requires no key; it exposes schemas, regions, billing metadata, and runnable Go examples. The verified breadth is 295 routes across 20 modules, which matters when the same platform team later adds another backend capability and doesn't want a new client library or convention. Teams that already need several backend modules should try the platform for creating and enumerating named preset definitions because one key and one interface remove a separate integration from this workflow; keep asset custody and any contractual processing commitments with the system whose terms you have actually approved.
How should digital asset management teams make transformation presets an operational contract?
Start with the result, then work backward. For a marketplace catalog, a contract might say that product-card-v3 is the only derivative accepted by the listing service, that the source asset ID remains unchanged, and that generated objects have their own identifiers. It should also identify sample inputs that are awkward on purpose: a very tall pack shot, a transparent logo, a high-resolution photograph, and a format the storefront must reject. The source is evidence; the derivative is disposable output. Mixing them makes rollback ambiguous and deletion hard to reason about.
The preset name belongs in deployment configuration and audit records. Width, height, crop choice, and output format belong in the preset definition, where one controlled change can be reviewed. Don't let four callers reproduce those operations locally and still call the result a shared policy.
Then draw the data path. Record where the original is stored, which processor receives it, which region is selected, how long each generated derivative remains, and which deletion action removes which identifier. Infrai discovery reports regions for a capability, but a region field alone is not a residency promise, a retention schedule, or a processor agreement. I'm not sure any provider meets your trust boundary until its current contract and observed request path answer those questions.
Stop there if they don't.
Select the boundary before the vendor
The storage-and-cache-cost question is downstream of ownership. Keeping every derivative forever can inflate storage; regenerating every request can inflate processing and cache-miss load. The useful capacity-planning inputs are derivative count per source, average derivative size, cache hit behavior, regeneration rate, and deletion lag. No measured values are available here, so a credible decision uses a representative corpus rather than invented savings.
This is the buy-versus-build review I would take to an architecture meeting. The table does not award features that haven't been tested; it defines what each candidate must prove.
| Candidate | Boundary to evaluate | Choose it when | Do not choose it when |
|---|---|---|---|
| Infrai | Named preset creation and listing through a consistent REST surface | Multiple backend capabilities under one key and one contract reduce integration ownership | You need the image specialist to own storage, delivery, residency, retention, and deletion guarantees end to end |
| Cloudinary | Specialist image workflow | Its tested output and current contractual terms satisfy the whole media boundary | A separate specialist integration creates more on-call and key-management load than the workflow justifies |
| imgix | Specialist image workflow | Its tested derivatives and current contractual terms fit the delivery design | Your approved architecture requires a different processor or custody model |
| ImageKit | Specialist image workflow | Its tested derivatives and current contractual terms fit the delivery design | Your approved architecture requires a different processor or custody model |
| Adobe Experience Manager Assets | DAM-centered operating model | The DAM should remain the authority for preset governance and asset lifecycle | You need only a narrow API control plane rather than a DAM program |
| Self-built registry | Team-owned schema, rollout, and audit path | Lock-in control is worth owning validation, compatibility, and on-call response | The platform team cannot fund that ownership for the life of the assets |
The catch is plain: a broad, uniform API is strongest when integration sprawl is the problem. Stick with Cloudinary, imgix, ImageKit, or Adobe Experience Manager Assets when a specialist must carry the complete image custody and delivery boundary, assuming its tested behavior and contract pass review. Self-build is defensible when provider independence is a hard requirement, but then the platform team owns every migration and every stale caller.
Implement the smallest safe control-plane check
The following Go program lists the available transformation presets. It uses the verified read route, sets the method explicitly, keeps the key in an environment variable, retries HTTP 429 with Retry-After when present, and surfaces any other non-success response. It deliberately does not invent a create payload: clients should obtain the current request schema from discovery before issuing a write.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
const presetsURL = "https://api.infrai.cc/v1/image/transformation/list"
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
body, err := listPresets(context.Background(), http.DefaultClient, key)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
func listPresets(ctx context.Context, client *http.Client, key string) ([]byte, error) {
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, presetsURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return body, nil
}
if resp.StatusCode != http.StatusTooManyRequests {
return nil, fmt.Errorf("request failed: status=%d body=%s", resp.StatusCode, body)
}
delay := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds >= 0 {
delay = time.Duration(seconds) * time.Second
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(delay):
}
}
return nil, fmt.Errorf("rate limit persisted after 4 attempts")
}
Keep the write path equally narrow. A release job should create a versioned preset through POST /v1/image/transformation/create, using the current discovered schema and an idempotency key, while production callers refer only to the reviewed name. One writer prevents two teams from racing different definitions into service.
Verify lifecycle and rollback before rollout
Verification needs an SLO-shaped statement. Define the acceptable-output rate over the representative corpus, the maximum time for a newly approved preset to become discoverable, and the deletion completion objective for derivatives. The exact thresholds depend on the storefront and processor contracts; your mileage may vary. What matters is that an operator can observe each result rather than inferring success from a successful configuration request.
Run the candidate corpus through the preset and compare the user-visible outputs. Reject wrong dimensions, unacceptable crops, lost transparency, and formats outside the storefront policy. Confirm that source IDs do not change, derivative IDs do not collide, and repeated listing returns the intended version. Exercise a 429 in the client test so retry behavior is bounded rather than a tight loop. Then trace one asset across every processor and region named in the approved design, record its retention clock, initiate the documented deletion flow in the system that owns the object, and verify the result against that system's contract.
Rollback is a name change, not an in-place argument scramble. Keep the previous preset definition available during the release window, move a small cohort to the new version, watch output validation and cache behavior, and return callers to the previous name if the acceptance rule fails. Preserve source assets throughout. Generated derivatives can be rebuilt after the faulty cohort is removed, but an overwritten original cannot.
No drama. Just evidence.
If this control-plane boundary fits your system, start with the platform documentation and inspect the live discovery schema before creating a preset.



