Skip to main content

The Unified Job Model

In ViralSync, every long-running process is a Job. This provides a unified interface for status monitoring, error handling, and scaling across all product features.


Job domains

ViralSync uses jobs to represent four types of work:

DomainPurposeJob types
RenderVideo generation via FFmpegrender.movie, render.scene, render.assemble, render.finalize
AgentAI-powered content and analysis tasksagent.chat, agent.analyze
WorkflowMulti-step automation pipelinesworkflow.run, workflow.trigger
PublishDistribution to social platformspublish.youtube, publish.tiktok, publish.instagram

All four domains share the same status model and the same Jobs API, making it possible to build a single dashboard or monitoring system that covers all activity.


Parent / child job hierarchy

Complex tasks are broken down into parent jobs and child jobs.

A render.movie parent job for a 3-scene video creates the following child jobs:

render.movie (parent) — job_a1b2c3d4e5f6
├── render.scene — scene_job_a1b2c3d4e5f6_001 (Scene 1)
├── render.scene — scene_job_a1b2c3d4e5f6_002 (Scene 2)
├── render.scene — scene_job_a1b2c3d4e5f6_003 (Scene 3)
└── render.assemble — assemble_job_a1b2c3d4e5f6 (merge all scenes)

The parent job stays in waiting status while child jobs are running. It transitions to done only when the assemble job completes successfully.

Why this matters for you:

  • GET /v1/jobs/{jobId} on the parent job returns a children summary: { total, succeeded, running, queued, failed }
  • If one scene fails, only that scene job is affected — other scenes continue rendering
  • The progress field on the parent aggregates across all child jobs

Job status reference

General status values

All job types use these status values:

StatusDescriptionTerminal?
queuedJob is waiting to be dispatched to a workerNo
runningJob is actively executingNo
waitingParent job is waiting for child jobs to completeNo
doneJob completed successfully. outputUrl is setYes
failedJob ended with an error. error field is setYes
cancelledJob was manually cancelled via the APIYes

Render-specific phase values

For render.* jobs in running or waiting state, the phase field provides finer granularity:

PhaseMeaning
planningAnalysing the movie JSON and creating the render plan
dispatchingSending child scene jobs to render workers
waitingWaiting for all scene jobs to complete
renderingScene workers are actively processing scenes
assemblingConcatenating rendered scenes into the final video
uploadingUploading the finished video to Cloudflare R2

Status state machine

                    ┌──────────┐
┌─────>│ queued │
│ └────┬─────┘
│ │ dispatcher picks up job
│ ┌────▼─────┐
│ │ running │
│ └────┬─────┘
│ │ creates child jobs
│ ┌────▼─────┐
│ │ waiting │
│ └────┬─────┘
│ │ all children done
│ ┌────▼─────┐ ┌──────────┐
│ │ done │ │ failed │
│ └──────────┘ └──────────┘

└──────────────── (retry logic for stalled jobs)

Job object shape

{
"jobId": "job_a1b2c3d4e5f6",
"type": "render",
"status": "running",
"phase": "rendering",
"progress": 0.62,
"parentJobId": null,
"outputUrl": null,
"error": null,
"mediaData": null,
"payload": {
"projectId": "proj_abc123"
},
"children": {
"total": 3,
"succeeded": 1,
"running": 1,
"queued": 1,
"failed": 0
},
"createdAt": "2026-04-02T10:00:00.000Z",
"updatedAt": "2026-04-02T10:00:08.432Z"
}

The children summary is only populated on parent jobs. Scene and assemble jobs (render.scene, render.assemble) have parentJobId set to the parent job's ID.


Monitoring a job

Three approaches, ranked by recommended priority for production:

ApproachBest forHow
WebhooksProduction systemsSet callbackUrl on the render request
SSE streamReal-time UI updatesGET /v1/jobs/{jobId}/progress
PollingSimple integrationsGET /v1/jobs/{jobId} every 3–5 seconds

See Webhooks → and Jobs API → for implementation details.


Why this model?

Scalability — Each scene renders on its own isolated worker. A 20-scene video renders in the same wall-clock time as a 2-scene video (given enough workers).

Fault isolation — A failed scene does not cancel other scenes. The system can retry individual scenes without re-rendering the entire video.

Transparency — Every step is visible via the API. You can inspect progress at the parent level (overall %) or drill into individual scene jobs.

Runtime independence — The same job model works across Modal.com, Google Cloud Run, and AWS Batch. Switching render infrastructure does not change the API contract.