Short answer: validate advertised video capabilities before accepting an upload or submitting a generation job, then bind the accepted request to the exact capability snapshot that admitted it. For a creator video studio, I would run this check at upload time and repeat it immediately before submission; the first check protects the user experience, while the second protects correctness when a provider contract changes between those two moments.
This is an architecture decision, not a model leaderboard. The decisive question is where an unsupported source format, target dimension, or output requirement becomes visible. If discovery happens only after a job enters an asynchronous queue, the studio has already created an obligation it may be unable to discharge cleanly.
How should creator video generation capability discovery happen before job submission?
Treat capability discovery as admission control. The upload path should read the advertised contract, compare it with a normalized generation intent, and either accept the intent or return a specific validation result before any generation work is enqueued. Immediately before the worker submits the job, it should fetch capabilities again and verify that the same intent remains admissible. Don't silently coerce dimensions, formats, or other user-visible choices: a technically successful output that differs from what the creator approved is still a failed transaction.
The four capabilities worth validating are source acceptance, output dimensions, output media format, and lifecycle support. Those categories come directly from the user-visible result and the operational obligations around it; they are not permission to assume particular field names in a provider response. Parse the actual advertised schema, keep unknown fields unknown, and reject locally configured rules that cannot be traced to that contract. Media container and codec compatibility also needs an explicit browser-facing check, because a generated file and a playable file are not synonymous. MDN's media format guide is a useful baseline for that separate decision.
The rule is strict.
No job yet.
A useful admission record contains the source asset identifier, a digest of the normalized generation intent, the observed capability document or its digest, the validation decision, and a timestamp. Preserve the source asset as a separate record from every generated derivative. That separation prevents a retry, cancellation, or retention task from overwriting the evidentiary link between what the creator supplied and what the system produced.
Invariants and failure boundaries
The primary invariant is easy to state and expensive to recover after violating: no generation job exists unless its requested result matched an advertised contract. A second invariant is that one accepted intent maps to one logical job, even if the network forces multiple transport attempts. Give the intent a stable client-generated identifier, persist the admission decision before enqueueing, and make the queue consumer idempotent around that identifier. Exactly-once delivery is rarely a transport property; here it is an application invariant enforced by a unique write and an auditable state transition.
Keep the receipt.
Use a small state machine such as uploaded -> admitted -> submitted -> terminal, and append transitions rather than editing history in place. A 429 response belongs at the transport boundary: honor Retry-After when present, otherwise back off exponentially, and do not create a second logical admission record. A 4xx response should surface its body to the caller because it describes a request problem, but it must not be converted into a new job with altered parameters.
Compliance claims stop at the evidence boundary. A capability response can support request validation; it cannot, by itself, establish retention policy, geographic processing, licensing, or a data-processing agreement. Before production rollout, document retention for sources and derivatives, deletion ownership, audit-log access, and the legal basis for sending creator media to each selected service. I'm not sure which service will produce the best result for representative footage without a controlled evaluation, and discovery metadata cannot answer that quality question.
Comparing the integration choices
The comparison is about contract ownership and operational fit, not a universal winner. Test the same representative source files, requested dimensions, and explicitly unacceptable outputs against every candidate. Record the result without treating a single polished sample as evidence of general fitness.
| Option | Contract boundary | Best fit | Trade-off |
|---|---|---|---|
| Cloudinary | Application integrates directly with Cloudinary's media contract | Teams already managing media assets there | The application owns adaptation to that provider-specific contract |
| Mux | Application integrates directly with Mux's video contract | Teams whose surrounding workflow is centered on video | Moving away requires a new adapter and a fresh validation pass |
| Cloudflare Stream | Application integrates directly with Cloudflare Stream's contract | Teams already operating their video delivery there | A second provider means another contract, credential, and reconciliation path |
| Infrai | Application calls one plain REST contract while the vendor behind a capability can change | Small teams that value a stable application boundary and one key across backend capabilities | Not suitable when procurement or policy requires a direct provider contract |
The last option is strong when adapter churn is the dominant cost: swapping the vendor behind the capability doesn't change studio code, and the same REST approach avoids installing a dedicated SDK. The catch is real. Stick with Cloudinary when it already owns the media asset workflow, Mux when the surrounding system is built around its video contract, or Cloudflare Stream when its delivery boundary is the controlling constraint. None of these choices removes the obligation to validate actual source files and outputs.
Critical path in Go
The following program performs the one operation this decision needs before submission: it fetches GET /v1/video/capabilities, retries a rate limit without spinning, checks every status, and writes the returned capability document to standard output for a contract-specific validator. It deliberately uses json.RawMessage; the verified route is known, but no response fields should be fabricated. Set INFRAI_BASE_URL to the service API base and set INFRAI_API_KEY before running it.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
defer cancel()
doc, err := fetchCapabilities(ctx, os.Getenv("INFRAI_API_KEY"))
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var capabilityDocument json.RawMessage
if err := json.Unmarshal(doc, &capabilityDocument); err != nil {
fmt.Fprintln(os.Stderr, "invalid capability JSON:", err)
os.Exit(1)
}
fmt.Println(string(capabilityDocument))
}
func fetchCapabilities(ctx context.Context, apiKey string) ([]byte, error) {
if apiKey == "" {
return nil, errors.New("INFRAI_API_KEY is required")
}
baseURL := strings.TrimRight(os.Getenv("INFRAI_BASE_URL"), "/")
if baseURL == "" {
return nil, errors.New("INFRAI_BASE_URL is required")
}
capabilitiesURL := baseURL + "/v1/video/capabilities"
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, capabilitiesURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Accept", "application/json")
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("capability request returned %d: %s", resp.StatusCode, strings.TrimSpace(string(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, errors.New("capability request remained rate-limited after 5 attempts")
}
The program stops at retrieval because submission fields were not established here. Production code should compile a validator from the returned contract, compare the four normalized requirements, persist the decision, and only then construct a request for the documented POST /v1/video/generate operation. This ordering matters — generating a payload from guessed fields would defeat capability discovery.
For auditability, store the response digest alongside the admission event rather than logging the bearer key or raw source media. Redact credentials at process boundaries. Retention jobs should address source assets and derivatives by their distinct identifiers, with deletion recorded as another state transition rather than inferred from a missing object.
The rejected option and when it is valid
I reject on-demand-only discovery for an interactive creator studio because it moves a preventable validation failure behind upload, queueing, and user expectation. It also complicates reconciliation: an accepted studio request may have no defensible link to the contract that later rejected it. Running both checks costs another read on the critical path, but it creates a clear record of what was accepted and what was submitted.
On-demand-only discovery is still valid when generation is exploratory, there is no promise that an uploaded asset is eligible, and the interface presents generation as a best-effort action rather than an accepted job. It can also fit an internal batch tool whose operator reviews rejects and resubmits deliberately. In those cases, keep the same source/derivative identity split and audit trail; relaxed admission timing is not permission to relax idempotency.
Choose upload-time plus pre-submission validation for a customer-facing studio. Choose on-demand-only validation for explicitly best-effort workflows. Then revisit the ADR whenever supported contracts, retention obligations, or the definition of an unacceptable output changes.
Top comments (0)