Skip to main content

Jobs API

All long-running processes in ViralSync are modelled as Jobs. This API gives you a unified interface to list, inspect, stream progress, cancel, and delete jobs of any type.


Job object

Every endpoint in this API returns Job objects. Here is the full 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",
"movie": { /* the original movie JSON */ }
},
"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"
}

Job status values

StatusDescription
queuedJob is waiting to be picked up by a worker.
runningJob is actively being processed.
waitingParent job is waiting for child jobs to complete.
doneJob completed successfully. outputUrl is populated.
failedJob ended with an error. error field contains the message.
cancelledJob was cancelled via POST /v1/jobs/{jobId}/cancel.

Render-specific phase values

For jobs with type: "render", the phase field provides finer-grained status within the running/waiting state:

PhaseDescription
planningAnalysing the movie, creating the render plan and child jobs.
dispatchingSending scene jobs to render workers.
waitingWaiting for all scene jobs to complete.
renderingScene jobs are actively rendering (reported per-scene).
assemblingConcatenating rendered scenes into the final video.
uploadingUploading the final video to Cloudflare R2.

GET /v1/jobs

List all jobs for your account, ordered by createdAt descending (most recent first).

Endpoint: https://api.viralsync.io/v1/jobs
Authentication: x-api-key header

Query parameters

ParameterTypeDefaultDescription
limitinteger 1–10025Number of jobs to return per page.
cursorstringOpaque cursor from the previous response's nextCursor field. Omit for the first page.
typestringFilter by job type. Example: render, publish, workflow.
statusstringFilter by status. Example: running, done, failed.

Example

curl -s \
-H "x-api-key: vs_prod_YOUR_API_KEY" \
"https://api.viralsync.io/v1/jobs?limit=10&type=render"

Response

{
"jobs": [
{
"jobId": "job_a1b2c3d4e5f6",
"type": "render",
"status": "done",
"progress": 1.0,
"outputUrl": "https://r2.viralsync.io/outputs/job_a1b2c3d4e5f6/output.mp4",
"createdAt": "2026-04-02T10:00:00.000Z",
"updatedAt": "2026-04-02T10:00:42.000Z"
}
],
"nextCursor": "eyJsYXN0SWQiOiJqb2JfYTFiMmMzZDRlNWY2In0"
}

Pass nextCursor as the cursor parameter in your next request to retrieve the next page. When nextCursor is null, there are no more results.


GET /v1/jobs/{jobId}

Retrieve the full details of a specific job.

Endpoint: https://api.viralsync.io/v1/jobs/{jobId}
Authentication: x-api-key header

Example

curl -s \
-H "x-api-key: vs_prod_YOUR_API_KEY" \
https://api.viralsync.io/v1/jobs/job_a1b2c3d4e5f6

Response — job in progress

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

Response — completed job

{
"jobId": "job_a1b2c3d4e5f6",
"type": "render",
"status": "done",
"phase": null,
"progress": 1.0,
"outputUrl": "https://r2.viralsync.io/outputs/job_a1b2c3d4e5f6/output.mp4",
"error": null,
"mediaData": {
"duration": 10.0,
"width": 1920,
"height": 1080,
"fileSize": 8432156
},
"children": {
"total": 3,
"succeeded": 3,
"running": 0,
"queued": 0,
"failed": 0
},
"createdAt": "2026-04-02T10:00:00.000Z",
"updatedAt": "2026-04-02T10:00:42.000Z"
}

Error responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or missing API key.
404JOB_NOT_FOUNDThe job does not exist or belongs to another account.

GET /v1/jobs/{jobId}/progress

Stream real-time progress updates for a job using Server-Sent Events (SSE).

SSE vs Polling

This endpoint is a streaming connection, not a one-shot request. The server keeps the connection open and pushes updates as the job progresses. Use this for real-time UI updates. For simple integrations, polling GET /v1/jobs/{jobId} every 3–5 seconds is sufficient.

Endpoint: https://api.viralsync.io/v1/jobs/{jobId}/progress
Authentication: x-api-key header
Response type: text/event-stream

The connection stays open until the job reaches a terminal state (done, failed, cancelled) or 30 minutes elapses (after which you should reconnect).

SSE event format

Each event is a JSON-encoded progress snapshot:

data: {"jobId":"job_a1b2c3d4e5f6","status":"running","phase":"rendering","progress":0.46,"children":{"total":3,"succeeded":1,"running":1,"queued":1,"failed":0},"outputUrl":null}

data: {"jobId":"job_a1b2c3d4e5f6","status":"done","phase":null,"progress":1.0,"outputUrl":"https://r2.viralsync.io/outputs/job_a1b2c3d4e5f6/output.mp4"}

Example — curl (stream to terminal)

curl -N \
-H "x-api-key: vs_prod_YOUR_API_KEY" \
https://api.viralsync.io/v1/jobs/job_a1b2c3d4e5f6/progress

The -N flag disables curl's output buffering so events print as they arrive.

Example — JavaScript (browser / Node.js)

const source = new EventSource(
'https://api.viralsync.io/v1/jobs/job_a1b2c3d4e5f6/progress',
{ headers: { 'x-api-key': 'vs_prod_YOUR_API_KEY' } }
);

source.onmessage = (event) => {
const update = JSON.parse(event.data);
console.log(`Progress: ${Math.round(update.progress * 100)}% — ${update.status}`);

if (update.status === 'done') {
console.log('Video ready:', update.outputUrl);
source.close();
}
if (update.status === 'failed') {
console.error('Render failed');
source.close();
}
};

source.onerror = () => {
// Reconnect after a short delay
source.close();
};

Example — Node.js with eventsource package

npm install eventsource
import EventSource from 'eventsource';

const jobId = 'job_a1b2c3d4e5f6';
const source = new EventSource(
`https://api.viralsync.io/v1/jobs/${jobId}/progress`,
{ headers: { 'x-api-key': process.env.VIRALSYNC_API_KEY } }
);

source.onmessage = (event) => {
const update = JSON.parse(event.data);
console.log(`[${update.status}] ${Math.round((update.progress ?? 0) * 100)}%`);
if (['done', 'failed', 'cancelled'].includes(update.status)) {
source.close();
if (update.status === 'done') console.log('Output:', update.outputUrl);
}
};

POST /v1/jobs/{jobId}/cancel

Cancel a running or queued job.

Endpoint: https://api.viralsync.io/v1/jobs/{jobId}/cancel
Authentication: x-api-key header
Body: None required

Example

curl -s -X POST \
-H "x-api-key: vs_prod_YOUR_API_KEY" \
https://api.viralsync.io/v1/jobs/job_a1b2c3d4e5f6/cancel

Response

Returns the updated job object with status: "cancelled".

{
"jobId": "job_a1b2c3d4e5f6",
"status": "cancelled",
"progress": 0.31,
"outputUrl": null
}

Error responses

StatusCodeDescription
404JOB_NOT_FOUNDJob does not exist or belongs to another account.
409INVALID_TRANSITIONJob is already in a terminal state (done, failed, cancelled). Check status before cancelling.
Cancellation and render minutes

Cancelled jobs do not consume render minutes. See Render Minutes →


DELETE /v1/jobs/{jobId}

Delete a job and its associated data. Only terminal jobs (done, failed, cancelled) can be deleted.

Endpoint: https://api.viralsync.io/v1/jobs/{jobId}
Method: DELETE
Authentication: x-api-key header

Example

curl -s -X DELETE \
-H "x-api-key: vs_prod_YOUR_API_KEY" \
https://api.viralsync.io/v1/jobs/job_a1b2c3d4e5f6

Response

204 No Content on success.

Error responses

StatusCodeDescription
400JOB_CANNOT_BE_DELETEDJob is still running. Cancel it first, then delete.
404JOB_NOT_FOUNDJob does not exist.