Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/coah80/yoink/llms.txt

Use this file to discover all available pages before exploring further.

Once a job is submitted, Yoink pushes real-time status updates to the client using Server-Sent Events (SSE). This is a unidirectional HTTP stream: the client opens a persistent GET connection and the server writes newline-delimited data: frames as the job progresses. No WebSocket upgrade or polling is required. Three endpoints work together to give you full control over a running job:
MethodPathPurpose
GET/api/progress/{id}Open SSE stream for a job
POST/api/cancel/{id}Cancel a running job
POST/api/finish-early/{id}Stop a playlist download and package what has been downloaded so far

GET /api/progress/

Opens an SSE stream for the given job ID. The connection stays open until the job reaches a terminal stage (complete, error, cancelled, or finishing-early) or the client disconnects. Response headers
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Path Parameters
id
string
required
The job ID returned by the endpoint that started the download, conversion, or compression job.

Keep-Alive Pings

The server sends a keep-alive comment frame every 15 seconds when no progress event has been written. This prevents proxies and load balancers from closing the idle connection:
data: {"stage":"starting","message":"Starting download..."}

: keep-alive

: keep-alive

Event Frame Format

Each event is a single data: line containing a JSON object, followed by a blank line:
data: {"stage":"downloading","message":"Downloading... 45%","progress":45,"speed":"5.2MiB/s","eta":"00:12"}


Event Payload Schema

Every event object contains at least stage and message. The remaining fields are present only when relevant to the current stage.
stage
string
required
The current lifecycle stage of the job. See Stage Reference for all possible values.
message
string
required
A human-readable description of what is happening. Suitable for display directly in a progress UI.
progress
number
Completion percentage as a float from 0 to 100. Present during downloading, processing, compressing, zipping, and similar active stages. Omitted for instantaneous stages like starting or complete.
speed
string
Current transfer or processing speed, e.g. "5.2MiB/s". Present during active download stages when the underlying tool reports it.
eta
string
Estimated time remaining, e.g. "00:12" (mm:ss). Present alongside speed when available.

Stage Reference

Example events for each stage
data: {"stage":"starting","message":"Starting download..."}

data: {"stage":"downloading","message":"Downloading... 12%","progress":12,"speed":"3.1MiB/s","eta":"01:04"}

data: {"stage":"downloading","message":"Downloading... 45%","progress":45,"speed":"5.2MiB/s","eta":"00:32"}

data: {"stage":"processing","message":"Processing video...","progress":80}

data: {"stage":"complete","message":"Download complete"}

Reconnection

If the client disconnects mid-job (e.g. due to a network blip) and reconnects to the same GET /api/progress/{id} URL, the server checks whether the job is still in progress. If it is, the first event sent to the reconnected client is a resuming event carrying the last recorded progress value:
data: {"stage":"resuming","message":"Reconnected! Resuming download...","progress":45}
Subsequent events continue as normal from the current point in the job. This means you can safely use the browser EventSource API’s built-in reconnection logic without losing track of progress.
Reconnection only works while the job is still running on the server. If the job completed or was cancelled while the client was disconnected, opening the progress stream will receive no further events (or may receive the terminal event immediately if it is still buffered).

POST /api/cancel/

Cancels a running job, kills the underlying process, cleans up temporary files, and emits a cancelled event on the progress stream. Path Parameters
id
string
required
The job ID to cancel.
Query Parameters
clientId
string
The clientId of the session that owns the job. If provided and the job is owned by a different client, the server returns 403. If omitted, ownership is not checked.
Request
curl -X POST "http://your-server:3001/api/cancel/job-456?clientId=f47ac10b-58cc-4372-a567-0e02b2c3d479"
Response 200 OK — job was found and cancelled
{
  "success": true,
  "message": "Download cancelled"
}
Response 200 OK — job not found (already complete or never existed)
{
  "success": false,
  "message": "Download not found or already completed"
}
Response 403 Forbidden — caller is not the job owner
{
  "success": false,
  "message": "Not authorized to cancel this job"
}
Cancellation is irreversible. Temporary files are deleted approximately 1 second after the cancel signal is sent. Do not rely on partial output being available after a cancel.

POST /api/finish-early/

Signals a playlist job to stop downloading new videos and immediately package whatever has been downloaded so far into a ZIP archive. The job transitions to the finishing-early stage and then to complete once zipping is done. This is useful when the user wants only the first N videos from a large playlist. Path Parameters
id
string
required
The playlist job ID to finish early.
Query Parameters
clientId
string
The clientId of the session that owns the job. Returns 403 if the caller is not the owner.
Request
curl -X POST "http://your-server:3001/api/finish-early/job-456?clientId=f47ac10b-58cc-4372-a567-0e02b2c3d479"
Response 200 OK
{
  "success": true,
  "message": "Finishing early"
}
Response 403 Forbidden
{
  "success": false,
  "message": "Not authorized to modify this job"
}
Add a “Stop and package” button to your playlist UI that calls finish-early. The SSE stream will deliver a finishing-early event followed by the normal zipping and complete sequence, so your progress indicator can keep updating until the file is ready.

JavaScript Example

The following snippet demonstrates a full SSE integration: opening the stream, handling each stage, reconnecting automatically, and wiring up cancel/finish-early controls.
const BASE = "http://your-server:3001";
const jobId = "job-456"; // returned by /api/download, /api/playlist, etc.
const clientId = "f47ac10b-58cc-4372-a567-0e02b2c3d479"; // from /api/connect

// Open the SSE stream
const source = new EventSource(`${BASE}/api/progress/${jobId}`);

source.addEventListener("message", (event) => {
  const payload = JSON.parse(event.data);
  const { stage, message, progress, speed, eta } = payload;

  console.log(`[${stage}] ${message}`);

  switch (stage) {
    case "starting":
      showStatus("Starting…");
      break;

    case "downloading":
      updateProgressBar(progress);
      showStatus(`${message}${speed} — ETA ${eta}`);
      break;

    case "processing":
    case "compressing":
    case "zipping":
      updateProgressBar(progress ?? null);
      showStatus(message);
      break;

    case "resuming":
      console.log("Reconnected — last progress:", progress);
      updateProgressBar(progress);
      break;

    case "playlist-info":
      showStatus(message);
      break;

    case "finishing-early":
      showStatus("Packaging downloaded videos…");
      break;

    case "complete":
      updateProgressBar(100);
      showStatus("Done!");
      source.close(); // terminal — no more events
      break;

    case "error":
      showError(message);
      source.close(); // terminal
      break;

    case "cancelled":
      showStatus("Cancelled.");
      source.close(); // terminal
      break;
  }
});

source.addEventListener("error", () => {
  // EventSource will automatically retry; log the interruption
  console.warn("SSE connection dropped — browser will retry automatically");
});

// Cancel button
document.getElementById("cancel-btn").addEventListener("click", async () => {
  await fetch(`${BASE}/api/cancel/${jobId}?clientId=${clientId}`, {
    method: "POST",
  });
});

// Finish-early button (for playlists)
document.getElementById("finish-early-btn").addEventListener("click", async () => {
  await fetch(`${BASE}/api/finish-early/${jobId}?clientId=${clientId}`, {
    method: "POST",
  });
});
The browser’s EventSource API reconnects automatically when the connection drops. Because Yoink sends a resuming event on reconnect, your handler will re-sync the progress bar to the correct value without any extra bookkeeping.

Build docs developers (and LLMs) love