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:
| Domain | Purpose | Job types |
|---|---|---|
| Render | Video generation via FFmpeg | render.movie, render.scene, render.assemble, render.finalize |
| Agent | AI-powered content and analysis tasks | agent.chat, agent.analyze |
| Workflow | Multi-step automation pipelines | workflow.run, workflow.trigger |
| Publish | Distribution to social platforms | publish.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 achildrensummary:{ total, succeeded, running, queued, failed }- If one scene fails, only that scene job is affected — other scenes continue rendering
- The
progressfield on the parent aggregates across all child jobs
Job status reference
General status values
All job types use these status values:
| Status | Description | Terminal? |
|---|---|---|
queued | Job is waiting to be dispatched to a worker | No |
running | Job is actively executing | No |
waiting | Parent job is waiting for child jobs to complete | No |
done | Job completed successfully. outputUrl is set | Yes |
failed | Job ended with an error. error field is set | Yes |
cancelled | Job was manually cancelled via the API | Yes |
Render-specific phase values
For render.* jobs in running or waiting state, the phase field provides finer granularity:
| Phase | Meaning |
|---|---|
planning | Analysing the movie JSON and creating the render plan |
dispatching | Sending child scene jobs to render workers |
waiting | Waiting for all scene jobs to complete |
rendering | Scene workers are actively processing scenes |
assembling | Concatenating rendered scenes into the final video |
uploading | Uploading 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:
| Approach | Best for | How |
|---|---|---|
| Webhooks | Production systems | Set callbackUrl on the render request |
| SSE stream | Real-time UI updates | GET /v1/jobs/{jobId}/progress |
| Polling | Simple integrations | GET /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.