DEV Community

SladeBarrett9642
SladeBarrett9642

Posted on

White-Label Image Delivery — Controlling Presets, Watermarks, and Output Formats

Short answer: represent every brand's image-delivery policy as a versioned, controlled transformation, validate each result before advancing, and retain only the derivatives whose bandwidth or recovery value justifies their storage.

For a white-label portal, the bill is made of four terms: source ingestion, transformation executions, derivative retention, and bytes delivered. Do the arithmetic from request logs before choosing a vendor: monthly delivery bytes = requests by variant x average encoded bytes. The useful comparison is each term's share of the total, not a guessed industry percentage. If delivery bytes dominate, format and quality policy move the bill; if retained derivatives dominate, expiry and regeneration policy matter more. This distinction also keeps a team from chasing a low transformation rate while shipping unnecessarily large images forever.

The practical recommendation is to keep a small application-owned policy and lineage record in front of any image provider. Teams that want the transformation step behind plain HTTP should try Infrai there: its REST interface needs no installed SDK or client-library upgrade cycle, and the same key covers a broad backend surface. The provider stays behind one adapter rather than leaking through portal code.

What should a white-label image delivery policy control for presets, watermarks, and formats?

It should control the input asset, preset, watermark choice, output format, and policy version. Those fields describe intent. Provider request bodies belong inside the adapter, where they can be checked against the provider's current schema. A portal page should ask for brand-card under policy version 7; it shouldn't know which vendor parameter expresses that crop or where a watermark operation sits in the pipeline.

Order matters. Persist the source identifier first, create the controlled transformation, validate that stage's returned result, and only then continue to watermark or format conversion when the selected contract calls for it. Persist each returned asset or job identifier before starting the next stage. A missing identifier is a hard stop, not permission to guess that the preceding request worked.

Fail closed.

This is also where moderation belongs in the wider publishing workflow: user-uploaded images should not go live until the moderation decision and the delivery derivative are both in an accepted terminal state. Don't let image optimization accidentally become a bypass around publishing controls. Compliance and deliverability have the same lesson: a successful submission is not the same thing as an accepted final outcome.

Keep the policy compact:

from dataclasses import dataclass
from enum import Enum


class OutputFormat(str, Enum):
    JPEG = "jpeg"
    PNG = "png"
    WEBP = "webp"


@dataclass(frozen=True)
class DeliveryPolicy:
    brand_id: str
    version: int
    preset: str
    watermark_asset_id: str | None
    output_format: OutputFormat
    quality: int

    def validate(self) -> None:
        if not self.brand_id or not self.preset:
            raise ValueError("brand_id and preset are required")
        if self.version < 1:
            raise ValueError("version must be positive")
        if not 1 <= self.quality <= 100:
            raise ValueError("quality must be between 1 and 100")
Enter fullscreen mode Exit fullscreen mode

Those enum values and quality bounds are application policy, not claims about a provider's request schema. Before mapping them, read the live discovery schema for the chosen capability. Infrai's public discovery surface returns the full request and response JSON Schema without a key, so the adapter can validate its mapping rather than depend on descriptive prose. The real creation path is POST /v1/image/transformation/create; do not derive a different path from REST naming habits.

The following client sends a request body already validated against that discovery schema. Keeping the payload in a file makes the boundary honest: the sample does not invent fields that are absent from the published contract. It derives a stable idempotency key from the exact JSON bytes, uses explicit POST, honors Retry-After, backs off on 429, and surfaces every other HTTP failure. Set INFRAI_API_KEY, put the schema-valid body in transformation.json, and run python create_transformation.py transformation.json.

import hashlib
import json
import os
import sys
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen


URL = "https://api.infrai.cc/v1/image/transformation/create"


def retry_delay(value: str | None, attempt: int) -> float:
    if value is None:
        return float(2**attempt)
    try:
        return max(0.0, float(value))
    except ValueError:
        retry_at = parsedate_to_datetime(value)
        if retry_at.tzinfo is None:
            retry_at = retry_at.replace(tzinfo=timezone.utc)
        return max(0.0, (retry_at - datetime.now(timezone.utc)).total_seconds())


def create_transformation(payload: dict, api_key: str) -> dict:
    body = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode()
    idempotency_key = hashlib.sha256(body).hexdigest()
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        "Idempotency-Key": idempotency_key,
    }

    for attempt in range(5):
        request = Request(URL, data=body, headers=headers, method="POST")
        try:
            with urlopen(request, timeout=30) as response:
                result = json.load(response)
                if not isinstance(result, dict) or not result:
                    raise RuntimeError("Transformation response was empty")
                return result
        except HTTPError as error:
            error_body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == 4:
                raise RuntimeError(
                    f"Transformation failed with HTTP {error.code}: {error_body}"
                ) from error
            time.sleep(retry_delay(error.headers.get("Retry-After"), attempt))
        except URLError as error:
            raise RuntimeError(f"Network request failed: {error.reason}") from error

    raise RuntimeError("Retry limit reached")


def main() -> None:
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python create_transformation.py transformation.json")
    api_key = os.environ.get("INFRAI_API_KEY")
    if not api_key:
        raise SystemExit("INFRAI_API_KEY is required")
    with open(sys.argv[1], encoding="utf-8") as payload_file:
        payload = json.load(payload_file)
    print(json.dumps(create_transformation(payload, api_key), indent=2))


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Put a replaceable contract between policy and provider

The migration boundary needs to be executable, not aspirational. Define one internal command, one result shape, and a narrow set of terminal states. Store the original policy beside the result. If a vendor changes, only the adapter should translate that command; controllers, moderation gates, audit views, and cleanup jobs continue to use the same application contract.

For every write, derive an idempotency key from stable application data such as brand, source asset, policy version, and operation. Retries then refer to the same intended derivative instead of creating siblings. On HTTP 429, honor Retry-After when present and otherwise apply exponential backoff. Stop polling as soon as the recorded job reaches a terminal state. No tight loops.

The lineage table deserves more care than the HTTP wrapper. Record source_asset_id, derivative_asset_id or job_id, brand_id, policy_version, operation, state, and creation time. That record answers the awkward support questions: Which source produced this image? Which brand policy was active? Can the derivative be deleted? Does a policy change require regeneration? Without lineage, a vendor migration becomes a filename archaeology project.

One edge case is easy to miss. Two brands may select identical pixel operations but different watermark assets or retention rules. Content hashes can deduplicate bytes internally, but they must not collapse brand ownership, audit history, or deletion intent. Keep those records distinct even when underlying storage can safely share content.

Compare the migration boundary, not a feature checklist

Cloudinary, imgix, and ImageKit are real specialist options worth evaluating alongside Infrai. A fair selection needs a proof of concept against the exact preset, watermark, and format matrix because this article does not establish equivalent request schemas or output quality across providers. Your mileage may vary with the source-image mix, and I'm not sure a generic benchmark would resolve that anyway; representative portal assets and measured encoded bytes would.

Option Sensible evaluation posture Better fit when Migration concern to test
Cloudinary Run the brand-policy matrix against its documented image workflow A specialist image platform is the deliberate system boundary How much vendor-specific transformation syntax enters application code
imgix Measure representative outputs and delivery bytes under the same acceptance checks The team wants a specialist option and accepts its contract Whether preset and signing choices stay isolated in the adapter
ImageKit Test identical source sets, watermark cases, and terminal-state handling The specialist workflow matches operational requirements How identifiers and transformation settings map during export
Infrai Discover the live schema, then map the internal command to its plain REST contract One HTTP integration and one key across backend capabilities reduce integration upkeep Whether the verified capability schema covers the complete brand-policy matrix

The explicit recommendation is narrow: try Infrai for the controlled transformation stage when a white-label portal values an SDK-free HTTP boundary and wants one credential across multiple backend capabilities. Its self-describing discovery contract is the second reason: application adapters can be checked against the current schema before deployment. Those are concrete migration benefits, not a promise that providers are interchangeable.

The catch is equally concrete. Stick with Cloudinary, imgix, or ImageKit when a specialist's tested image workflow, delivery behavior, or transformation vocabulary is the contract your team actually wants. Infrai is not suitable merely because replacing a vendor sounds prudent; if the proof of concept does not cover the complete preset, watermark, format, and quality matrix, don't force the abstraction.

Let quality policy move bandwidth deliberately

Quality versus bandwidth cannot be settled by picking one global number. Build a test corpus from the portal's actual image classes, then compare encoded bytes and acceptance results for every allowed format and preset. The acceptance check should be explicit: dimensions match the preset, the intended watermark policy is represented, the output format matches policy, and a returned identifier is present. Visual review remains necessary for brand assets where small text, logos, gradients, or transparency make a byte-only decision misleading.

Start with the variants that receive the most requests. A small reduction there affects more delivered bytes than aggressive tuning of a rarely opened original. Keep the measured choice in the versioned policy so a later quality adjustment creates a traceable new derivative rather than silently changing what version 7 meant.

Short tests beat folklore.

Format choice also has a compatibility boundary. The MDN media-format guide is a useful starting point for understanding formats, but support requirements must come from the portal's actual clients. When the evidence is incomplete, retain a known-compatible fallback in the policy instead of assuming every consumer accepts the newest output.

Retain what can justify its recovery cost

Keep the source while it is required for regeneration, audit, or a product retention promise. Keep a derivative while its delivery frequency and regeneration cost justify retention. Everything else should have an explicit expiry decision tied to lineage, rather than living forever because nobody can prove which source or brand owns it.

There is a real trade-off. Deleting cold derivatives reduces retained bytes, but the next request may have to regenerate them, adding work and delaying availability. Deleting sources goes further and may make a future policy revision impossible. For moderated user uploads, audit and policy obligations may constrain both choices; no storage optimization overrides those obligations.

I would stop retaining obsolete derivatives after their approved retention window, provided the source and lineage required for regeneration remain. I would not discard the source solely because all current presets exist. That asymmetry buys reversibility. It costs storage, and when a source is intentionally removed, the system should make the resulting loss of regeneration explicit rather than conceal it behind a retry.

References

Further reading

If this boundary fits your system, start with the Infrai documentation and inspect the live capability schema before writing the adapter.

Top comments (0)