TUBERESULTS / API V1
Connect your tester.
Upload a complete test record. Receive a permanent URL, ready for a QR label.
Publish a signed run
A VT-1 Lite tester with a signing key signs every run inside its hardware. Upload the run as the tester sent it: every {"t":"rec"} frame, unchanged, one per line. No key is needed. The site checks the signature against the public key it holds for that tester, builds the report from the frames, and keeps the frames as the downloadable source, so anyone can recheck them. The report says it was signed by the tester.
curl --request POST 'https://tuberesults.com/api/v1/tests' \ --header 'Content-Type: application/x-ndjson' \ --data-binary @KTL-3TSW-0000K-V.frames.jsonl
The VT-1 Lite PC app does this from the Archive's Upload button. A tester is registered once by the site's operator from the public key the tester reports (key.get). Altered, incomplete, unregistered or revoked runs are refused, as is a run whose record names another tester than the key's.
Publish a record
Send the native JSON export from VT-1 Lite (schema 1) or VT-1 Claude (record_version 1). Uploads are immediately public. Use a bearer key authorised for the tester ID in the record. These reports are shown as not signed by the tester, and a tester registered for signed runs cannot be uploaded this way.
curl --request POST 'https://tuberesults.com/api/v1/tests' \ --header "Authorization: Bearer $TR_UPLOAD_TOKEN" \ --header 'Content-Type: application/json' \ --data-binary @record.json
A new report returns 201 Created. An identical retry returns 200 OK and the same URLs.
{
"id": "KTL-3TTG-00001-2",
"created": true,
"signed": false,
"test_url": "https://tuberesults.com/T/KTL-3TTG-00001-2",
"tube_url": "https://tuberesults.com/V/E88CC-1"
}Read reports & history
GET /api/v1/tests/{testId}Normalized results, metadata, samples and plotsGET /api/v1/tests/{testId}/sourceExact original upload: the JSON record, or a signed run's framesGET /api/v1/tests/{testId}/samples/{index}.csvOriginal sample table for a zero-based procedure indexGET /api/v1/tubes/{tubeSerial}?limit=50&offset=0Published history, ordered by upload timePublic reads need no key. The corresponding human-readable pages are /T/{testId} and /V/{tubeSerial}.
Identity & retries
The tube serial comes from the tester and must already be globally unique. Preserve it when retesting on another instrument. A missing serial is accepted for older records; its report has no tube-history link.
Lite exports use their printable id as the public test ID and their UUID for source identity. Claude exports must include a stable run_id. IDs and serials are case-sensitive, limited to 128 characters, and cannot contain slashes or control characters. URL-encode path values.
Keep the same run ID when retrying an upload. Different results under an existing ID return 409 Conflict. Lite archive and publication marks are ignored when recognising retries. The original upload is preserved unchanged.
Upload limits & responses
Maximum upload: 5 MiB, 512 procedures, 100,000 rows per sample table. Unknown record versions are rejected; new procedure names within supported versions retain their data.
401 / 403Invalid upload key / tester not authorised, signing key unregistered or revoked, or a signing tester uploaded without its signature429Too many signed uploads from one tester in an hour409Existing report conflicts with this upload413 / 415 / 422Record too large / wrong content type / invalid record, or frames that are incomplete or do not match their signature503Uploads are not configured on this server