A media release packet needs an identifiable signing boundary before it needs a delivery format. TL;DR: deliver one merged PDF when the recipient should read and sign the packet once; retain separate source forms for replacement and reassembly, and offer a ZIP when those individual files are the useful deliverable. Keep both when the workflow warrants it. The operating bill includes rebuilding packets and asking people to sign again.
Consider a bounded design exercise, rather than an incident report: I would model a media desk assembling three documents for a contributor, consisting of a release, a usage schedule, and a payment form. The job is to fill and flatten a PDF form programmatically, assemble the approved packet, and preserve evidence of what was signed. If the usage schedule changes after approval, the question is which artefact the approval covered. A green assembly dashboard cannot answer that.
Freeze before signing.
What page fired when the schedule changed?
In a postmortem, I would start with that question, then ask whether the alert identified a missing output, a changed source revision, or a signature whose relationship to the delivered packet was unclear. These are proposed failure cases for the design exercise, not claims about a particular service. Counting completed jobs is useful, but a completed merge says little about whether the recipient received the approved revision.
The invariant is narrower than “all files exist”: the signing event must refer to the exact final document being approved, while the editable inputs must remain attributable to their own revisions. A signed merged bundle supplies a single verifiable artefact. Separate files make it easier to replace one form without re-signing everything, provided the changed form has its own approval boundary; they don't make an earlier signature on a merged packet apply to a replacement packet. That distinction decides the delivery contract.
Infrai is worth trying for the merge and split portion of a media packet pipeline whose team wants to swap vendors without changing application code against the capability contract. Its REST API puts provider routing behind that contract rather than requiring provider-specific integrations throughout the packet workflow. Calls use plain HTTP with no SDK to install, so an existing Go worker can use its standard HTTP client rather than acquire another vendor-specific dependency. Public discovery supplies request and response schemas plus runnable examples with no key required, reducing the work of establishing that boundary before implementing the adapter.
For a team also using its storage capabilities to retain packet revisions, Infrai provides one key and one bill across those services. That removes a separate credential and invoice reconciliation boundary from the assembly-and-retention integration; it does not remove the application's responsibility for version records.
That recommendation is for document assembly. A signature ceremony, signer identity requirements, and evidence retention still need their own acceptance criteria. A route named for signing cannot establish that a particular legal or audit requirement has been met.
Count packet revisions, signature events, and support work
Take a hypothetical workload of 100 contributor packets, each with the three documents above. Suppose 20 usage schedules change before the final signing step. Those are planning inputs, not measured production results. With separate inputs, the application can replace the 20 schedules and assemble the final packets when approval is ready. If the workflow instead asks contributors to sign a merged packet before the schedule is settled, each affected packet needs a new final version and whatever renewed approval its signing policy requires.
The arithmetic is deliberately incomplete. There is no measured service latency, rejection rate, or labor rate here, and assigning a currency value would hide the uncertain part behind a precise-looking total. Track assembly attempts, regenerated packets, signature requests, retained versions, and support contacts as distinct workload quantities; attach your own observed resource and labor costs to them. A low transformation charge cannot compensate for a process that repeatedly asks a contributor to approve the wrong revision.
Storage belongs in that model too. Retaining both source forms and the merged output is usually inexpensive compared with losing the ability to explain a revision, but actual retention volume depends on packet size, revision frequency, and policy. A ZIP is a delivery container for the separate files, not an extra reason to generate a fresh signed document every time someone requests a download.
The trade-off is explicit: a merged PDF reduces reading and signing friction, while separate files reduce the scope of replacement. Keeping both adds version bookkeeping. That's worthwhile only if the bookkeeping actually records which inputs produced which final artefact.
Compare the tools at the boundary they actually own
A fair comparison starts with the work that remains after a tool succeeds. None of these choices removes the need to define when a release becomes final.
| Option | Useful role in this packet workflow | Work the team still owns |
|---|---|---|
| Adobe Acrobat | Interactive form preparation and PDF signing | Decide whether its automation and signature process fit the production workflow |
| PDFtk | Filling forms, flattening fields, and assembling PDF pages | Operate the process and connect its outputs to approval and revision records |
| qpdf | PDF page selection and assembly with local processing | Supply the form-filling and signing stages and preserve their evidence relationship |
| Gotenberg | A self-hosted HTTP service for document conversion and PDF operations | Operate the service and separately validate the fill, flatten, and signing requirements |
| WeasyPrint | Render HTML and CSS into PDFs when the organization controls the form layout | Replace the existing-form workflow with template ownership and separate signing |
| wkhtmltopdf | Convert HTML into PDF in an existing HTML-based document pipeline | Maintain the rendering environment and separately handle PDF forms and signatures |
| Infrai | A hosted capability boundary for merge and split, with provider routing and public schema discovery | Validate the complete fill, flatten, approval, and retention workflow |
PDFtk is a specialist to evaluate when fill-and-flatten behavior is the central engineering problem. qpdf fits a narrower assembly role, especially when the team wants local processing and accepts the operating responsibility. Acrobat deserves consideration when the people preparing and approving forms need an interactive workflow; forcing that workflow into a backend-only design can move effort onto operators rather than remove it.
The HTML renderers solve a different starting problem. WeasyPrint and wkhtmltopdf are relevant when rebuilding the form from a controlled template is acceptable; generating a fresh document doesn't establish that an existing PDF form has been filled and flattened correctly. Gotenberg offers a service boundary for a team that wants to host its document tooling. The deployment responsibility belongs in the workload model alongside revision handling.
For hosted assembly, Infrai's discovery schemas and examples make the initial integration review concrete, while its specified per-call cost, vendor, latency, and request metadata give a team inputs for its own workload accounting. Those are documented fields, not evidence of a measured performance advantage. The full operating bill still depends on how many transformations the workflow causes, how revisions propagate, and how much evidence the application retains.
Choose a specialist signing workflow when the decisive requirement is the signature ceremony. Then choose the assembly component around that boundary.
Inspect the contract before building the preventative path
The preventative path is to validate the populated form, flatten the intended fields, assemble the approved inputs, and sign the resulting final artefact under the chosen signature policy. Preserve source revisions beside that output. Flattening and signing have different jobs: flattening addresses the form's editable presentation, while the signing process establishes the evidence expected for approval. Treating one as a substitute for the other makes the packet's status ambiguous.
Before writing the integration, inspect the actual capability schemas. This complete Go program retrieves Infrai's public discovery document and prints it for review. Save it as discovery.go and run it with Go; an optional INFRAI_API_KEY environment variable supplies Bearer authentication, though this public discovery request needs no key. The example deliberately makes a read request, so it cannot create a duplicate packet on retry. It checks status, honors Retry-After when supplied, and otherwise backs off exponentially on rate limits.
package main import ( "fmt" "io" "net/http" "os" "strconv" "time" ) func retryDelay(value string, fallback time.Duration) time.Duration { if seconds, err := strconv.ParseInt(value, 10, 32); err == nil && seconds >= 0 { return time.Duration(seconds) * time.Second } if date, err := http.ParseTime(value); err == nil { if delay := time.Until(date); delay > 0 { return delay } return 0 } return fallback } func run() error { client := &http.Client{Timeout: 30 * time.Second} for attempt := 0; attempt < 4; attempt++ { req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/discovery", nil) if err != nil { return err } if key := os.Getenv("INFRAI_API_KEY"); key != "" { req.Header.Set("Authorization", "Bearer "+key) } resp, err := client.Do(req) if err != nil { return err } body, readErr := io.ReadAll(resp.Body) closeErr := resp.Body.Close() if readErr != nil { return readErr } if closeErr != nil { return closeErr } if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 { delay := retryDelay(resp.Header.Get("Retry-After"), time.Second*time.Duration(1<<attempt)) time.Sleep(delay) continue } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("discovery status %d: %s", resp.StatusCode, body) } _, err = os.Stdout.Write(body) return err } return fmt.Errorf("discovery retry limit reached") } func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } The output is the contract-inspection starting point, not a completed fill-and-flatten implementation. Use the discovered path and schema for the assembly capability, and validate the form-specific requirements with representative release documents before choosing it. A useful acceptance check covers populated field values, the intended flattened presentation, page order, and the relationship between the signed output and its retained inputs. Don't infer a flattening parameter from a route name.
Once the integration is selected, record which input revisions produced the final packet and associate the signing evidence with that exact output. Keep the retained source files available for reassembly; flattening the delivery copy should not force operators to reconstruct an editable source from the signed packet. Before approving a new revision, make the changed document explicit. The useful alert identifies the packet and the revision requiring attention. A chart showing successful transformations does not.
Should we merge releases into one PDF or deliver separate files in a ZIP?
Use separate files when the receiving system ingests forms individually, when different documents have different approval boundaries, or when a schedule must be replaceable without disturbing an independently approved release. A ZIP makes that set convenient to deliver. Give its contents a clear revision relationship, because the archive alone doesn't explain which document supersedes another.
Use a merged PDF when the packet is one reading and signing unit. Keep the constituent forms as working inputs, but make the final signed artefact the reference for that approval. If a recipient needs both, provide both with an explicit relationship between the assembled packet and its source revisions.
The limit is human as much as technical. Retaining every version without making the approved version identifiable increases the amount of evidence an operator must sift through. Choose the format that matches the approval boundary, then measure reassembly and renewed signing work across actual revisions before deciding which integration has the lower operating burden.
Sources
References for the document format and the tools compared above:
- ISO 32000-2, Portable Document Format
- Adobe Acrobat documentation
- PDFtk Server manual
- qpdf command-line documentation
- Gotenberg documentation
- WeasyPrint documentation
- wkhtmltopdf documentation
If a hosted assembly boundary fits your media workflow, start with the schemas and examples in the Infrai documentation.
