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_publicat upload are readable with no key. A private object returns404(not401) to an unauthenticated caller, so its existence isn't disclosed.
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" }
.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 type | subunit value(s) | What it is |
|---|---|---|
| Images (jpg, gif, png) | 320.<ext>, 640.<ext>, 1024.<ext> | Thumbnails at three widths |
| PDF documents | thumbnail.jpg | First-page preview image |
| Spreadsheets (xlsx) | thumbnail.jpg, preview.pdf | First-page image; full PDF render |
| Sequence (fasta, fastq) | graph.png | Rendered sequence graphic |
| Traces (ab1, scf) | graph.png | Chromatogram graphic |
| Genotyping (fsa) | graph.png | Fragment-analysis graphic |
| Audio (mp3, wav) / acoustic surveys (tar) | spectrogram.png | Spectrogram preview |
| Tabular (parquet, jsonl, tsv) | preview.json | Schema + 3 sample rows as JSON |
| Any type | original.<ext> | The original file (same as no subunit) |
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/profilesandGET /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
| Endpoint | Description |
|---|---|
PUT /api/objects | Upload one file (multipart), against a profile. |
PUT /api/batch | Upload 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/presign | Batch-resolve presigned URLs for many (object, subunit) pairs. |
GET /api/objects/{id}/metadata | Universal 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}/schema | List profiles and inspect a profile's field schema. |
POST /api/profile/{name}/upload | Profile-first upload — schema validated against the profile before the object is accepted. |
GET /api/{type}/{id}/metadata | Format-specific metadata. type: images, fasta, fastq, traces, documents, spreadsheets, microsatellite, audio, archive, parquet, jsonlines, tsv. |
GET /api/{type} · /{type}/count · /{type}/recent | List, count, and fetch recent objects of a given type. |