API

The endpoints

POST/api/uploadskairos:write

Ask for somewhere to put a broadcast. Creates nothing and analyses nothing — it hands back a path and a resumable upload session.

Body

FieldTypeRequiredNotes
filenamestringyesReduced to a safe basename. Only the name is used, never a path.
sizeBytesnumberyesThe exact size. Checked against your plan now — including anything you have already uploaded and not yet analysed — and against the object itself when you start the analysis.

Answers

201 { path, sessionUri, expiresAt }. Keep `path` — it is what you name to start the analysis.

StatuserrorWhen
400—filename is missing, or sizeBytes is not a positive number.
413file-too-largeThe file is larger than a single file may be on your plan.
409period-bytes-exhaustedIt would not fit in what your plan allows this month. Uploads you have not analysed count toward that, so clean them up or analyse them.
503—Uploads are unavailable because the deployment has no cloud credentials.
PUT<sessionUri>no key

Send the bytes, straight to Google Cloud Storage. This one does not go to KAIROS at all, and takes no KAIROS key — the session URI is the credential.

Answers

200 or 201 when the last chunk lands. 308 after every earlier chunk, carrying a `Range` header.

StatuserrorWhen
308—Normal. More bytes wanted — read the `Range` header for how much was actually kept.
404—The session has expired or was cancelled. Ask for a new one and start again.
POST/api/analyseskairos:write

Analyse an object you have uploaded. This is what creates the broadcast and queues the work.

Body

FieldTypeRequiredNotes
pathstringyesThe `path` from POST /api/uploads, exactly as it was given.
titlestringyesWhat you will see it called. Trimmed at 300 characters.
domain'sport' | 'news' | 'auto'yes`news` also covers talks and podcasts: bulletins, radio shows, talking-head formats. `auto` lets the analyser classify it. **This cannot be changed afterwards** — the pipeline it selects is the one that runs.

Answers

201 { assetId, status, sizeBytes, container }. Keep `assetId`: progress and the timeline are both under it.

StatuserrorWhen
400—The path is not one this workspace can analyse, or the title is missing.
400domain-requiredNo domain was given. It is required, and there is no default.
400domain-not-in-planYour plan does not carry the sport pipeline. `auto` needs it too, because the answer may be sport.
400mime-not-allowedThe container is not a video type. In practice the 415 below fires first.
409—There is no object at that path, or that object has already been analysed.
413file-too-largeThe object is larger than a single file may be on your plan.
409period-bytes-exhaustedYour plan’s bytes for this month are spent.
409lifetime-minutes-exhaustedYour plan’s lifetime minutes are spent.
409monthly-minutes-exhaustedYour plan’s minutes for this month are spent.
415—The first bytes are not a video container. The object is deleted, and nothing is created or charged.
GET/api/assets/<assetId>/progresskairos:read

Where one analysis has got to, and whether the machine running it is still alive. Poll this.

Answers

200 { phase, generation, heartbeatAgeSeconds, cancelRequested, startedAt, uploadedBytes }. See the phases below.

StatuserrorWhen
404—No such broadcast in your workspace. Not yours and not existing are one answer.
GET/api/assets/progresskairos:read

The same answer for every analysis still in flight, in one call. Use it instead of polling per broadcast when you have several running.

Answers

200 { "<assetId>": { phase, heartbeatAgeSeconds, cancelRequested } , … }, up to 50.

GET/api/assets/<assetId>/timelinekairos:read

The finished analysis: segments with their start, end, kind and description, their key moments, and the highlights found.

Query

ParameterTypeNotes
offsetnumberFirst segment to return. Default 0.
limitnumberHow many. Default 50, capped at 200.

Answers

200 { id, title, domain, durationSeconds, total, returned, offset, segments, keyMoments }.

StatuserrorWhen
404—No such broadcast in your workspace.
409—It has not been analysed yet. Poll progress until the phase is `done`.
GET/api/assets/<assetId>/timeline/rawkairos:read

The timeline file exactly as the analysis wrote it — `sport-timeline.json` or `news-timeline.json` — before any edits made in the editor. Sent as a download.

Answers

200, the file itself, with Content-Disposition: attachment.

StatuserrorWhen
404—No such broadcast in your workspace.
409—It has not been analysed yet. Poll progress until the phase is `done`.
POST/api/assets/<assetId>/cancelkairos:write

Ask for a running analysis to stop. Intent, not an instant stop — the machine notices on its next check-in, up to 30 seconds later.

Answers

200 { ok, cancelRequested }.

StatuserrorWhen
404—No such broadcast, or it has no analysis to cancel.
409—That analysis has already finished.
DELETE/api/assets/<assetId>kairos:write

Delete a broadcast and its bytes. A broadcast that was analysed keeps counting toward your lifetime minutes — deleting is not a refund.

Answers

200 { ok }.

StatuserrorWhen
404—No such broadcast in your workspace.
GET/api/healthno key

Is KAIROS up and can it still reach its database. No credential needed, and it says nothing about your workspace.

Answers

200 { ok: true, database: "up", checkedAt }, or 503 with database: "down".

StatuserrorWhen
503—KAIROS is up but cannot reach its database. Nothing will progress until this clears.

Branch on the code in error, not on the sentence beside it. The codes above are the contract and will not change under you. The wording is written for people and does change.

Something here wrong or missing? Tell us — it is the kind of bug nobody reports.Back to KAIROS

KAIROS needs one cookie to keep you signed in, and remembers your light/dark choice. May we also measure which adverts bring people here, and which parts of the app get used? Those set a Google advertising cookie and a PostHog one, and refusing changes nothing about the product.

Privacy