Documentation

Everything you need to use Openkova.

Conversion modes

Openkova supports three ways to produce screenshots:

Sessions

Every conversion is associated with a session — a UUID that groups your screenshots together. The session ID is returned in every API response and stored in an openkova_session HTTP-only cookie (7-day expiry). Pass it back as sessionId in subsequent requests to keep results in the same gallery. If you omit it, a new session is created automatically.

Storage retention: 24 hours. Screenshots are automatically deleted 24 hours after the session was last written to. The server runs a cleanup pass every hour. Download your images before then — use the Download All button or the GET /api/session/:sessionId/download endpoint to grab everything as a ZIP.

Streaming responses (SSE)

All three convert endpoints respond with a Server-Sent Events (SSE) stream rather than a single JSON blob. This lets the UI (and your own code) display live progress as the browser launches, pages load, and snapshots are taken.

Each line in the stream is a JSON event in one of three shapes:

// A step in progress
{ "type": "progress", "message": "Launching virtual browser" }

// Final success — stream closes after this
{ "type": "done", "message": "Done — 3 screenshots saved", "data": { ... } }

// Unrecoverable error — stream closes after this
{ "type": "error", "message": "Conversion failed" }

The data field on done events contains the same payload that was previously returned as JSON (see per-endpoint details below).

The Set-Cookie header for the session is set on the streaming response itself, so cookies work normally even though there is no JSON body.

Consuming the stream (JavaScript)

const res = await fetch('/api/convert/snippet', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ html: '<h1>Hello</h1>' }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  // SSE events are separated by double newline
  const parts = buffer.split('\n\n');
  buffer = parts.pop() ?? '';

  for (const part of parts) {
    const line = part.split('\n').find(l => l.startsWith('data: '));
    if (!line) continue;
    const event = JSON.parse(line.slice(6));

    if (event.type === 'progress') console.log(event.message);
    if (event.type === 'done')     console.log('Result:', event.data);
    if (event.type === 'error')    console.error(event.message);
  }
}

POST /api/convert/snippet

Renders a raw HTML string and returns a single screenshot. The HTML is wrapped in a minimal full document with a reset stylesheet before rendering.

RequestContent-Type: application/json

{
  "html":      "<h1>Hello</h1>",   // required — raw HTML string
  "sessionId": "uuid"              // optional — omit to create a new session
}

Done event data:

{
  "sessionId": "uuid",
  "imageId":   "uuid",
  "url":       "/api/image/{sessionId}/{imageId}"
}

POST /api/convert/file

Accepts one or more HTML files as multipart form data and returns one screenshot per file. Files are rendered sequentially in the order they are submitted.

RequestContent-Type: multipart/form-data

files     File[]   // required — one or more .html/.htm files
sessionId string   // optional form field

Done event data:

{
  "sessionId": "uuid",
  "results": [
    { "imageId": "uuid", "filename": "index.html", "url": "/api/image/..." },
    ...
  ]
}

POST /api/convert/url

Crawls a public URL and screenshots discovered pages 10 at a time.

Request (crawl mode)Content-Type: application/json

{
  "url":       "https://example.com",  // required — valid absolute URL
  "depth":     1,                      // optional — 1 (default) or 2
  "sessionId": "uuid"                  // optional
}

Request (paginate mode) — pass pre-known URLs to screenshot without re-crawling:

{
  "urls":      ["https://example.com/page-11", ...],  // required
  "sessionId": "uuid",   // required — use the session from the first request
  "offset":    10,       // how many pages were already captured
  "total":     25        // total discovered in the original crawl
}

Done event data:

{
  "sessionId": "uuid",
  "results":   [{ "imageId": "uuid", "url": "https://example.com/" }, ...],
  "remaining": ["https://example.com/page-11", ...],
  "total":     25
}

GET /api/image/:sessionId/:id

Returns a single PNG screenshot with Content-Type: image/png. Responses are cached for 1 hour (Cache-Control: public, max-age=3600, immutable). Returns 404 if the image does not exist.

GET /api/session/:sessionId/download

Downloads all screenshots in a session as a single .zip file. Returns 404 if the session has no images.

GET /api/session/:sessionId

Returns all image IDs associated with a session.

{ "images": ["uuid1", "uuid2", ...] }