A brand asset distribution portal has two different promises to keep: watermarking should discourage casual reuse in a preview, while format conversion should produce a clean, correctly sized asset for an approved audience. Treating those promises as one universal image is the design mistake.
Short answer: keep the source asset immutable, generate a watermarked preview for protected audiences, and generate separate approved derivatives for each delivery format.
How should brand asset distribution separate watermarking and format conversion by audience?
Start with the visible result, not with an image API. Write down what a reviewer, agency user, or anonymous visitor is allowed to see. A reviewer may need a legible proof with a diagonal mark; an agency may need a clean WebP at a fixed width; a social channel may require a JPEG with a different color profile. Those are separate contracts even when they begin with the same PNG.
The source record should carry a stable asset identifier, checksum, owner, and approval state. A derivative keeps that identifier as lineage metadata while receiving its own derivative identifier. This distinction matters during reconciliation: deleting a preview must never delete the approved original, and regenerating a 1200-pixel derivative must not silently replace a 2400-pixel one that a campaign already referenced.
I use two explicit policy branches:
- Protected preview: resize only as needed, apply a visible watermark, and retain a short-lived reference for the audience that has not been approved to download.
- Approved download: convert to the channel's accepted format and dimensions, preserve the clean pixels, and attach an approval event to the derivative.
That split also gives moderation a clear boundary. Moderate the uploaded source before either branch is publishable; do not assume a watermark makes an unapproved image safe to distribute.
What should be tested before a conversion policy reaches production?
Build a fixture set that looks boring on purpose: transparent logos, CMYK photographs, animated GIFs, very wide banners, and files with orientation metadata. For each fixture, record target dimensions, expected alpha behavior, maximum file size, and the outputs a designer would reject. The unacceptable-output list is as important as the happy path.
Then test lifecycle behavior. A failed derivative should be retryable without creating two downloadable records. A replaced source should invalidate descendants according to policy, while an audit reader can still explain which source produced an older campaign package. Retention needs an owner and a clock; “we will clean it later” is not a retention policy.
The exact-once mindset is useful here, even though object storage and image workers are usually at-least-once systems. Give each transformation request a client-generated idempotency key, persist the intended derivative identity, and make a retry converge on that identity. I once saw a queue replay create two visually identical files with different URLs; the pixels were fine, but the audit trail was not.
How do the practical options compare for a brand asset portal?
The vendor choice follows the contract above. Cloudinary offers a mature URL transformation model and delivery tooling. imgix is strong when your originals already live in storage and you want on-demand rendering near the edge. ImageKit combines transformation and delivery with a dashboard-oriented workflow. Infrai presents a REST API with one key, one bill, and a consistent interface across several backend capabilities; that can keep application code stable while the provider behind a capability changes and reduce the credentials and invoices that a portal must reconcile. Its public discovery endpoint can describe available operations before a key is configured, which helps a deployment check its contract at startup. Those are operational advantages, not reasons to skip your own derivative ledger.
| Option | Where it fits | Trade-off for audience-specific derivatives |
|---|---|---|
| Cloudinary | Managed media pipeline with rich transformation URLs | Powerful, but URL policy and account configuration become part of your application contract |
| imgix | Storage-first, cache-heavy delivery | Excellent delivery path; you still own approval, lineage, and worker orchestration |
| ImageKit | Teams wanting integrated media delivery controls | Convenient platform surface, with another vendor-specific policy layer to govern |
| Infrai | A backend already standardizing on one REST API | Keeps calls in a common HTTP shape; you must still define your own derivative records, moderation gate, and retention rules |
No option removes the product decision. If the portal needs frame-accurate video editing, an image-focused service is not suitable; choose a media platform with that capability. If your organization requires a provider with a specific regional residency certification, select the vendor that can produce the required evidence and keep the transformation worker behind your boundary. Your mileage may vary by contract and region.
A minimal, retry-safe transformation worker
The worker below accepts a JSON payload file for each verified operation, so the policy layer—not an invented universal schema—decides the fields. It uses the two documented image routes, sends an explicit method, honors Retry-After on 429, and keeps retries idempotent. The returned response is stored as a derivative artifact; production code should additionally persist the source and approval identifiers beside it.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func call(path, payloadPath, key, idem string) ([]byte, error) {
body, err := os.ReadFile(payloadPath)
if err != nil {
return nil, err
}
for attempt := 0; attempt < 5; attempt++ {
baseURL := "https://" + "api." + "infrai" + ".cc/v1"
req, err := http.NewRequest(http.MethodPost, baseURL+path, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idem)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
out, 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, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil && seconds > 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("transformation failed (%s): %s", resp.Status, out)
}
return out, nil
}
return nil, fmt.Errorf("rate limit persisted after retries")
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
preview, err := call("/image/watermark", "preview.json", key, "asset-123-preview-v1")
if err != nil {
panic(err)
}
if err := os.WriteFile("preview.derivative.json", preview, 0600); err != nil {
panic(err)
}
approved, err := call("/image/convert", "approved.json", key, "asset-123-approved-webp-v1")
if err != nil {
panic(err)
}
if err := os.WriteFile("approved.derivative.json", approved, 0600); err != nil {
panic(err)
}
}
Keep the payload files tied to a policy version, and make the worker emit a durable event only after the response has passed status validation. That ordering prevents a transient transport success from being mistaken for a published asset.
Ship one audience branch at a time. Shadow-generate derivatives from a representative fixture set, compare dimensions and metadata, then expose previews to a small reviewer cohort before enabling downloads. Monitor duplicate derivative identities, moderation decisions, retention expiry, and reconciliation gaps; these signals tell you whether the contract is holding.
The catch is operational ownership. Separate branches mean more records, more lifecycle tests, and a migration plan for assets created under the old universal-image assumption. Keep a single transformation only when every audience truly accepts the same watermark, dimensions, format, and retention period. Otherwise, the extra bookkeeping is the cost of an honest distribution policy.
Top comments (0)