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.

Yoink uses a lightweight session system to associate download jobs with individual clients. When a client registers, it receives a unique clientId that is passed on every subsequent job request. The server uses this identifier to enforce per-client concurrency limits (a maximum of 3 concurrent jobs), route progress events, and authorize cancel and finish-early requests. Sessions are entirely ephemeral — they are held in memory and expire automatically when idle.

Session Lifecycle

A typical session flows through four stages:
  1. ConnectPOST /api/connect registers a new client and returns a clientId.
  2. Use — Include clientId as a query parameter on download, convert, compress, and other job requests.
  3. Heartbeat — Call POST /api/heartbeat/{clientId} every ~15 seconds to keep the session alive.
  4. Expiry — If no heartbeat arrives within 60 seconds the session is automatically cleaned up and its job slots are released.
[Browser/Client]

    ├─ POST /api/connect ──────────────────► {clientId: "abc-123"}

    ├─ GET  /api/download?clientId=abc-123 ► (binary stream)

    ├─ GET  /api/progress/job-456 ─────────► SSE stream

    ├─ POST /api/heartbeat/abc-123 ────────► {success: true, activeJobs: 1}

    └─ (idle > 60s) ───────────────────────► session auto-expires
A heartbeat interval of 15 seconds is recommended — well within the 30-second heartbeat timeout and the 60-second session idle timeout. Browsers can use setInterval to fire the heartbeat request in the background while a download is in progress.

POST /api/connect

Registers a new client session and returns a UUID clientId. No request body is required. The session is created immediately and the idle timer starts. Request
curl -X POST http://your-server:3001/api/connect
No request body or parameters are needed. Response 200 OK
{
  "clientId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
clientId
string
required
A UUID v4 string that uniquely identifies this client session. Store this value — it must be passed as the clientId query parameter on all job submission requests.
Each call to /api/connect creates a new, independent session. If you call it multiple times, you will receive multiple clientId values, each with its own job-count quota. Reuse the same clientId for the lifetime of a browser tab or application instance.

POST /api/heartbeat/

Resets the idle timer for an existing session and returns the number of currently active jobs associated with that client. Call this endpoint on a recurring interval (every 15 seconds is recommended) while the client has work in progress or is expecting to submit more jobs. If the clientId does not exist (session already expired), the heartbeat call re-registers it transparently, so a client that missed its window can recover by heartbeating again. Path Parameters
clientId
string
required
The UUID returned by POST /api/connect. Must match an active session.
Request
curl -X POST http://your-server:3001/api/heartbeat/f47ac10b-58cc-4372-a567-0e02b2c3d479
Response 200 OK
{
  "success": true,
  "activeJobs": 2
}
success
boolean
true when the heartbeat was recorded successfully.
activeJobs
integer
The number of jobs currently tracked against this client session. Use this value to display a live job counter in your UI or to decide whether to submit additional jobs.
Error 400 Bad Request
{
  "error": "Client ID required"
}
Returned when the clientId path segment is missing or empty.

Session Timeouts

Two distinct timeout values govern session health:
ConstantValueEffect
HeartbeatTimeout30 secondsAmount of time the server waits between heartbeats before marking the session as stale.
SessionIdleTimeout60 secondsAmount of time with no heartbeat activity before the session is fully evicted and its resources released.
If a session expires while a job is running, the job continues to completion — expiry does not cancel in-flight downloads. However, the client will lose the ability to cancel the job or start new ones under the same clientId. Re-connect with a new clientId to resume normal operation.

Per-Client Job Limits

The server enforces a hard limit of 3 concurrent jobs per clientId (MaxJobsPerClient). Attempting to submit a fourth job while three are already active returns an error response from the relevant job endpoint. The activeJobs field in heartbeat responses reflects this count in real time.
clientId: abc-123
├── job-001  [downloading]   ← slot 1
├── job-002  [converting]    ← slot 2
└── job-003  [compressing]   ← slot 3
     ↑ limit reached — new job submissions will be rejected
Poll activeJobs from the heartbeat response to drive UI state. When activeJobs drops below 3, re-enable the submit button so the user can queue another download without hitting the limit.

JavaScript Example

The following snippet shows a minimal client that connects, sends heartbeats every 15 seconds, and cleans up on page unload.
const BASE = "http://your-server:3001";

// 1. Register a session
const { clientId } = await fetch(`${BASE}/api/connect`, {
  method: "POST",
}).then((r) => r.json());

console.log("Session registered:", clientId);

// 2. Send heartbeats every 15 seconds
const heartbeatInterval = setInterval(async () => {
  const { success, activeJobs } = await fetch(
    `${BASE}/api/heartbeat/${clientId}`,
    { method: "POST" }
  ).then((r) => r.json());

  console.log(`Heartbeat OK — active jobs: ${activeJobs}`);

  if (!success) {
    // Session was evicted; re-register
    clearInterval(heartbeatInterval);
  }
}, 15_000);

// 3. Stop heartbeating when the page closes
window.addEventListener("beforeunload", () => {
  clearInterval(heartbeatInterval);
});

// 4. Use clientId when submitting a job (GET /api/download uses query parameters)
const params = new URLSearchParams({
  url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  clientId,
  format: "video",
  quality: "1080p",
});
const response = await fetch(`${BASE}/api/download?${params}`);
// The response streams the media binary directly — save it or pipe it to a <video> element

Build docs developers (and LLMs) love