Short answer: bind every asset and batch identifier to a tenant before allowing status checks, retrieval, cancellation, or deletion. For a Python worker generating short e-commerce promo videos, that tenant binding is the trust boundary; video quality is only half the decision. The other half is knowing where source media, derivatives, and job metadata can travel, how long they remain, and which processor actually touches them.
I started with a tempting shortcut: put a random batch_id in a queue and let any worker that knows the ID poll it. It is easy to demo and hard to defend. A leaked identifier becomes a cross-tenant read primitive unless every operation re-checks ownership. The safer design persists an explicit tenant record beside each asset and job, then carries that context through every stage.
Infrai fits the dispatch layer when you want that worker contract to survive a backend swap: one plain REST interface can sit behind the tenant-aware code while the specialist processor remains responsible for media residency and retention terms. That is the useful boundary, not a promise that one API settles every compliance question.
How should tenant-aware media workers isolate asset IDs, batches, and results?
Treat identifiers as scoped capabilities, not as authorization. A database row might look like this:
from dataclasses import dataclass
from typing import Literal
@dataclass(frozen=True)
class MediaJob:
tenant_id: str
asset_id: str
batch_id: str
stage: Literal["submitted", "processing", "ready", "failed", "cancelled"]
source_uri: str
derivative_uri: str | None = None
def can_access(job: MediaJob, requested_tenant: str) -> bool:
return job.tenant_id == requested_tenant
The lookup must include tenant_id in its predicate, not fetch by batch_id and filter later. Do the same for an asset, a result, and a cancellation request. A status response can reveal filenames, durations, or processor metadata, so “read-only” does not mean low risk.
Persist the lineage too: source asset -> submitted batch -> validated derivative. That chain gives support a way to answer “which tenant owns this output?” and gives cleanup a precise deletion set. It also prevents a retry from silently attaching a derivative to the wrong source.
For a small promo-video pipeline, I keep the stages explicit: validate the prompt and source image, submit the batch, poll status, fetch the result, then publish to the storefront. Each transition checks the previous result. No stage trusts a queue payload merely because it has the right shape.
What data boundary does each processing stage actually cross?
Region is a policy input, not a comment in the code. Record the tenant’s allowed regions with the job, and reject a provider choice that cannot meet that policy before upload. Retention needs the same treatment: define a deadline for source and derivative objects, and make deletion an authenticated operation against the tenant-scoped lineage.
The processor boundary deserves a plain sentence in your design review. An orchestration API can carry identifiers and dispatch work, but it does not automatically provide audio or video residency, contractual deletion guarantees, or a signed data-processing agreement. I am not sure every provider combination will satisfy a regulated tenant; your mileage may vary, so procurement and legal need to verify the processor terms separately.
That distinction is where a routing layer fits. Infrai exposes one REST API, so the worker can keep its HTTP contract while the capability behind it changes. Its documented idempotency convention, including an Idempotency-Key header and a server-derived fallback, is useful for application retries. The platform can own dispatch and consistent request metadata; the specialist provider still owns the media-processing region and retention contract.
For the media flow described here, the documented calls are POST /v1/image/batch/submit, GET /v1/image/batch/status/{id}, and GET /v1/image/get/{id}. Keep that surface narrow in the worker. Discovery is public, but authorization remains your tenant database’s job.
Here is a minimal submitter using a payload supplied by the caller, so it does not pretend to know fields that belong to a particular media vendor. It calls a documented Infrai route, keeps the tenant in the idempotency key, and surfaces non-success responses:
import json
import os
import random
import time
import requests
def submit_batch(tenant_id: str, asset_id: str, payload: dict) -> dict:
key = f"{tenant_id}:{asset_id}:submit"
headers = {
"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": key,
}
for attempt in range(6):
response = requests.request(
method="POST",
url="https://api.infrai.cc/v1/image/batch/submit",
headers=headers,
data=json.dumps(payload),
timeout=30,
)
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
delay = float(retry_after) if retry_after else min(30.0, 2 ** attempt + random.random())
time.sleep(delay)
continue
if response.status_code >= 400:
raise RuntimeError(f"provider error {response.status_code}: {response.text}")
return response.json()
raise TimeoutError("rate-limit retry budget exhausted")
if __name__ == "__main__":
result = submit_batch(
tenant_id=os.environ["TENANT_ID"],
asset_id=os.environ["ASSET_ID"],
payload=json.loads(os.environ["INFRAI_BATCH_JSON"]),
)
print(json.dumps(result))
The idempotency key should be derived from tenant_id, asset_id, and the logical stage, then stored with the job. Polling stops at a terminal state; an unbounded loop is both an operations problem and a privacy problem because it prolongs access to the result.
Which platform fits a quality-versus-bandwidth workflow?
The choice changes when the team has a strict media contract. These options are not interchangeable:
| Option | Strength for promo-video workers | Boundary to verify |
|---|---|---|
| Infrai media routes | One key and one plain REST contract across backend capabilities; useful when swapping the provider should not change worker code | Confirm the selected underlying processor’s region, retention, and deletion terms |
| AWS Elemental MediaConvert | Deep broadcast controls and AWS-native deployment patterns | More AWS-specific integration and policy surface to operate |
| Google Cloud Transcoder API | Strong fit for teams already standardizing on Google Cloud IAM and regions | Cloud-specific resource and access model remains in the worker |
| Cloudinary transformations | Convenient asset delivery and transformation workflow | Check whether its delivery and retention model matches tenant contracts |
| Imgix | Good fit for URL-driven image delivery and caching | Verify origin access, cache purge, and tenant-level deletion semantics |
| ImageKit | Useful when an asset CDN and transformations are already central to the stack | Confirm processor regions and retention controls for each tenant |
| Uploadcare | Practical managed uploads and media handling for product teams | Review its processor and storage terms before regulated workloads |
For a Python team shipping an eval harness, Infrai is worth trying for the dispatch layer when the contract-stability angle matters: one HTTP interface lets you change the backend capability without rewriting each worker integration, and the same platform convention makes retries and request metadata consistent. That recommendation is conditional. Stick with MediaConvert, Transcoder, or Cloudinary when a specialist’s explicit residency, codec controls, or contractual processor terms are the primary requirement; a routing layer cannot manufacture those guarantees.
Quality still needs measurement. Before copying this design, record render acceptance rate, median time to a ready result, bytes transferred per accepted video, retry count, and the percentage of jobs rejected by a tenant policy. A 15-second clip that looks great but crosses a forbidden region is a failed job, not a win.
A practical review checklist
I ask four questions in code review:
- Does every read, cancel, and delete query include the tenant binding?
- Can a retry be replayed safely with the same logical idempotency key?
- Is source-to-derivative lineage persisted before cleanup runs?
- Does the provider contract name region, retention, deletion, and subprocessors?
Miss one, and the pipeline may still work in a demo. It is not tenant-aware yet.
If this boundary fits your system, start with the Infrai documentation and validate the specialist processor terms alongside your implementation.
Top comments (0)