API Reference

BIO·Stor API

A REST API for typed file storage. Authenticate with an API key, upload a file against a profile, let it run through the validation and post-processing pipeline, then retrieve the original, a rendered thumbnail, or a tabular data preview as a 302 redirect to a presigned object-storage URL. Full request/response schemas are in the interactive Swagger UI.

Base URL & docs

All endpoints are served under /api. The interactive, always-current schema (try requests in-browser) is the Swagger UI; a read-only rendering is at ReDoc. This page is the orientation; those are the source of truth for exact field names and response shapes.

Authentication & API keys

Every request authenticates with an API key passed in the api-key header. A key is a per-account UUID.

api-key: 00000000-0000-0000-0000-000000000000

Getting a key

API keys are issued per account by an administrator in the admin console (Admin → API Keys). Keys may carry an expiry date and can be rotated; rotating immediately invalidates the previous value. Each account also has a webhook secret used to sign callbacks (see Webhooks). Service integrations (e.g. the workbench) authenticate the same way with their own account key.

What a key authorizes

  • Writes (upload, update, delete) require a valid key and act as the key's owner.
  • Reads of a private object require a valid key. Objects marked is_public at upload are readable with no key. A private object returns 404 (not 401) to an unauthenticated caller, so its existence isn't disclosed.
Treat keys as secrets: send them only over HTTPS and keep them out of client-side code and URLs (use the header).

Object model & lifecycle

Each object is characterized on two independent axes: its extension drives the technical pipeline (validation, stats, rendered previews) and its profile drives the domain schema (e.g. specimen_image, chromatogram, permit). Object IDs look like:

biostor-{bucket}_{group}.{name}.{ext}      e.g. biostor-3_157.abc123def.jpg

After upload an object moves through the pipeline; GET /api/objects/{id}/metadata reports its state:

pending_validation -> (validated) -> pending_postprocessing -> ready
                       \-> validation_failed        \-> postprocessing_failed

Originals are retrievable once validated; rendered surrogates once ready.

Uploading

Single file — PUT /api/objects

Multipart form upload. The profile is a query parameter; the file is the object_file part; any other form fields are treated as profile metadata (e.g. sampleids, processids, and profile-specific fields). Optional query params: is_public (default false), callback_validation, callback_postprocessing (webhook URLs).

curl -X PUT "https://<host>/api/objects?profile=specimen_image" \
  -H "api-key: $BIOSTOR_API_KEY" \
  -F "[email protected]" \
  -F "sampleids=NZG7253"
# -> { "object_id": "biostor-3_157.abc123def.jpg" }
An extension with a single default profile (e.g. .jpg → specimen_image, .tsv → specimen_batch, .parquet/.jsonl → occurrence_dataset) lets you omit profile; multi-profile extensions (e.g. .tar, .pdf) require it. Per-type size limits apply and are enforced at upload.

Batch package — PUT /api/batch

Upload many objects in one call as a package (the package part): a .tar/.zip containing a metadata.csv manifest plus the data files it references.

curl -X PUT "https://<host>/api/batch" \
  -H "api-key: $BIOSTOR_API_KEY" \
  -F "[email protected]"

Download a per-profile metadata.csv template from the portal's Upload page.

Retrieval & thumbnails

GET /api/objects/{id} returns a 302 redirect to a short-lived presigned URL; the client streams bytes directly from object storage. With no parameters you get the original. Two query parameters control retrieval:

  • subunit=<blob> — fetch a specific rendered surrogate (thumbnail/preview) instead of the original.
  • url=true — return the presigned URL as JSON ({ "url": "..." }) instead of a 302, handy for server-side use.
# original (302 -> storage)
curl -L -H "api-key: $KEY" "https://<host>/api/objects/$ID"

# a 640px image thumbnail
curl -L -H "api-key: $KEY" "https://<host>/api/objects/$ID?subunit=640.jpg"

# just the presigned URL, no redirect
curl -H "api-key: $KEY" "https://<host>/api/objects/$ID?subunit=thumbnail.jpg&url=true"

Surrogate names by object type

Each type renders different surrogates during post-processing. Pass the name as subunit:

Object typesubunit value(s)What it is
Images (jpg, gif, png)320.<ext>, 640.<ext>, 1024.<ext>Thumbnails at three widths
PDF documentsthumbnail.jpgFirst-page preview image
Spreadsheets (xlsx)thumbnail.jpg, preview.pdfFirst-page image; full PDF render
Sequence (fasta, fastq)graph.pngRendered sequence graphic
Traces (ab1, scf)graph.pngChromatogram graphic
Genotyping (fsa)graph.pngFragment-analysis graphic
Audio (mp3, wav) / acoustic surveys (tar)spectrogram.pngSpectrogram preview
Tabular (parquet, jsonl, tsv)preview.jsonSchema + 3 sample rows as JSON
Any typeoriginal.<ext>The original file (same as no subunit)
Surrogates exist only once an object is ready. To resolve many URLs at once without redirects, POST /api/objects/presign with a body like [{"object_id":"...","subunit":"thumbnail.jpg"}].

Metadata & profiles

  • GET /api/objects/{id}/metadata — universal object metadata (status, size, md5, profile, timestamps).
  • GET /api/{type}/{id}/metadata — format-specific metadata joined with its profile fields (type = images, fasta, fastq, traces, documents, spreadsheets, microsatellite, audio, archive, parquet, jsonlines, tsv).
  • PATCH /api/objects/{id} — update domain metadata (some fields, e.g. visibility, are upload-only and cannot be changed here).
  • GET /api/profiles and GET /api/profile/{name}/schema — discover the available profiles and the exact fields each accepts. See also the profiles reference page for the complete list with accepted extensions and auto-computed fields.
  • GET /api/{type}, /{type}/recent, /{type}/count — list and count objects of a type.

Webhooks

Pass callback_validation and/or callback_postprocessing (HTTPS URLs) as query params on upload. When that stage completes (or fails) the service POSTs a JSON body and signs it with your account's webhook secret:

POST <your callback URL>
X-BIOS-Signature: sha256=<hmac-sha256 of the raw body>
Content-Type: application/json

{ "event": "postprocessing.complete", "object_id": "biostor-3_157.abc123def.jpg",
  "status": "ready", "file_name": "specimen.jpg", "timestamp": "2026-06-16T01:00:00Z" }

Verify the signature by computing HMAC-SHA256 over the raw request body with your webhook secret and comparing to the header.

Endpoint reference

EndpointDescription
PUT /api/objectsUpload one file (multipart), against a profile.
PUT /api/batchUpload a package of many objects with a metadata.csv manifest.
GET /api/objects/{id}Retrieve (302) the original or a subunit surrogate; url=true returns the URL.
POST /api/objects/presignBatch-resolve presigned URLs for many (object, subunit) pairs.
GET /api/objects/{id}/metadataUniversal object metadata.
PATCH /api/objects/{id}Update an object's domain metadata.
DELETE /api/objects/{id}Delete an object.
GET /api/profiles · /api/profile/{name}/schemaList profiles and inspect a profile's field schema.
POST /api/profile/{name}/uploadProfile-first upload — schema validated against the profile before the object is accepted.
GET /api/{type}/{id}/metadataFormat-specific metadata. type: images, fasta, fastq, traces, documents, spreadsheets, microsatellite, audio, archive, parquet, jsonlines, tsv.
GET /api/{type} · /{type}/count · /{type}/recentList, count, and fetch recent objects of a given type.