Jobs API

Create and manage long-running workflow jobs. Jobs are used for complex operations like video generation that take several minutes to complete.

Endpoints

POST
/api/v1/jobs

Create a new job

GET
/api/v1/jobs

List your jobs

GET
/api/v1/jobs/{id}

Get job status

DELETE
/api/v1/jobs/{id}

Cancel a job

Create a Job

POST
/api/v1/jobs

Request Body

NameTypeDescription
workflowrequiredstringWorkflow slug (e.g., 'house-timelapse', 'renovation-timelapse')
inputobjectWorkflow-specific input parameters
webhookUrlstringURL to receive completion notification

Example Request

"text">-purple-600">curl "text-blue-600">-X POST https://vydra.app/api/v1/jobs \
  "text-blue-600">-H "Authorization: Bearer YOUR_API_KEY" \
  "text-blue-600">-H "Content">-Type: application/json" \
  "text-blue-600">-d '{
    "workflow": "house">-timelapse",
    "input": {
      "theme": "Cliffside Ocean Mansion"
    },
    "webhookUrl": "https://yourapp.com/webhook/vydra"
  }'
Response (201 Created)json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "running",
  "workflow": "house-timelapse",
  "creditsCharged": 150,
  "createdAt": "2026-01-26T12:00:00.000Z",
  "_links": {
    "self": "https://vydra.app/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000",
    "status": "https://vydra.app/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000"
  }
}

List Jobs

GET
/api/v1/jobs

Query Parameters

NameTypeDescription
limitnumberDefault: 20Max results (1-100)
offsetnumberDefault: 0Pagination offset
statusstringFilter by status: pending, running, completed, failed
Examplebash
"text">-purple-600">curl "https://vydra.app/api/v1/jobs?limit=10&status=completed" \
  "text-blue-600">-H "Authorization: Bearer YOUR_API_KEY"
Responsejson
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "completed",
      "progress": 100,
      "workflowSlug": "house-timelapse",
      "statusMessage": "Video generated successfully",
      "creditsCharged": 150,
      "createdAt": "2026-01-26T12:00:00.000Z",
      "completedAt": "2026-01-26T12:15:00.000Z",
      "output": {
        "videoUrl": "https://pub-xxx.r2.dev/videos/output.mp4"
      }
    }
  ],
  "pagination": {
    "limit": 10,
    "offset": 0,
    "count": 1
  }
}

Get Job Status

GET
/api/v1/jobs/{id}
Examplebash
"text">-purple-600">curl https://vydra.app/api/v1/jobs/550e8400"text-blue-600">-e29b-41d4"text-blue-600">-a716-446655440000 \
  "text-blue-600">-H "Authorization: Bearer YOUR_API_KEY"

Response States

Pending

{
  "id": "...",
  "status": "pending",
  "progress": 0,
  "statusMessage": "Waiting to start..."
}

Running

{
  "id": "...",
  "status": "running",
  "progress": 45,
  "statusMessage": "Generating video 2 of 3..."
}

Completed

{
  "id": "...",
  "status": "completed",
  "progress": 100,
  "output": {
    "videoUrl": "https://pub-xxx.r2.dev/videos/output.mp4",
    "thumbnailUrl": "https://pub-xxx.r2.dev/videos/thumbnail.jpg"
  }
}

Failed

{
  "id": "...",
  "status": "failed",
  "error": {
    "message": "Processing error",
    "code": "PROCESSING_ERROR"
  },
  "creditsRefunded": 150,
  "refundStatus": "completed"
}

Cancel a Job

DELETE
/api/v1/jobs/{id}
Examplebash
"text">-purple-600">curl "text-blue-600">-X DELETE https://vydra.app/api/v1/jobs/550e8400"text-blue-600">-e29b-41d4"text-blue-600">-a716-446655440000 \
  "text-blue-600">-H "Authorization: Bearer YOUR_API_KEY"
Responsejson
{
  "success": true,
  "message": "Job cancelled",
  "refund": {
    "applied": true,
    "creditsRefunded": 150
  }
}

Auto-Refund on Cancel

When you cancel a job, any charged credits are automatically refunded.

Webhooks

Instead of polling for status, provide a webhookUrl to receive notifications when jobs complete.

Webhook Payload (Success)

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "workflow": "house-timelapse",
  "output": {
    "videoUrl": "https://pub-xxx.r2.dev/videos/output.mp4"
  },
  "completedAt": "2026-01-26T12:15:00.000Z"
}

Webhook Payload (Failure)

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "workflow": "renovation-timelapse",
  "error": {
    "message": "Processing failed",
    "code": "PROCESSING_ERROR"
  },
  "refund": {
    "applied": true,
    "creditsRefunded": 50
  },
  "completedAt": "2026-01-26T12:15:00.000Z"
}

Job Statuses

StatusDescriptionFinal?
pendingJob created, waiting to startNo
runningJob is actively processingNo
completedJob finished successfullyYes
failedJob encountered an errorYes
cancelledJob was cancelled by userYes

Polling Example

If you can't use webhooks, poll for status:

Polling Implementationjavascript
"text-purple-600">async "text-purple-600">function waitForJob(jobId, apiKey, maxWaitMs = 30 * 60 * 1000) {
  "text-purple-600">const startTime = Date.now();
  "text-purple-600">const pollInterval = 5000; // 5 seconds
  
  while (Date.now() - startTime < maxWaitMs) {
    "text-purple-600">const response = "text-purple-600">await fetch(`https://vydra.app/api/v1/jobs/${jobId}`, {
      headers: { "text-green-600">'Authorization': `Bearer ${apiKey}` }
    });
    
    "text-purple-600">const job = "text-purple-600">await response.json();
    
    console.log(`Status: ${job.status}, Progress: ${job.progress}%`);
    
    "text-purple-600">if (job.status === "text-green-600">'completed') {
      "text-purple-600">return job.output;
    }
    
    "text-purple-600">if (job.status === "text-green-600">'failed') {
      throw "text-purple-600">new Error(job.error?.message || "text-green-600">'Job failed');
    }
    
    // Wait before next poll
    "text-purple-600">await "text-purple-600">new Promise(resolve => setTimeout(resolve, pollInterval));
  }
  
  throw "text-purple-600">new Error("text-green-600">'Job timed out');
}

// Usage
"text-purple-600">const output = "text-purple-600">await waitForJob("text-green-600">'job-id', "text-green-600">'YOUR_API_KEY');
console.log("text-green-600">'Video URL:', output.videoUrl);

Rate Limits

Polling counts against your rate limits. Use webhooks when possible, or poll no more than once every 5 seconds.