Developers
API guide & reference
Aliquora
aliquora.com
A practical guide to integrating with Aliquora's REST API — create and track samples, enter and verify results, and pull reference data from your own systems. Every endpoint and limit below reflects the live v1 API.
Document type
API guide & reference
Audience
Developers & integrators
Base URL
https://aliquora.com/api/v1
Overview
The Aliquora API is a JSON-over-HTTPS REST API. It lets you automate the lab workflows you already use in the app: registering samples, scheduling tests, entering and verifying results, and reading reference data such as test definitions, material types, projects, and clients. Every API key is scoped to the organization that owns it — keys only ever see and change data within their own tenant.
The current version is v1. All endpoints live under a single base URL:
https://aliquora.com/api/v1
You can confirm the live endpoint list at any time by calling GET /api/v1, which returns the API
name, version, and a complete list of routes. API access is available on plans that include the API feature —
see pricing for details.
Quick start. (1) In the app, go to Settings → API Access and create a key.
(2) Copy the rawKey — it is shown only once. (3) Call
GET /api/v1/whoami with the key to confirm it works, then start creating samples.
Authentication
Every request to /api/v1/* must include your API key as a bearer token in the
Authorization header:
Authorization: Bearer lk_your_key_here
API keys begin with the prefix lk_ followed by a long random string. Keys are stored only as a
one-way hash — Aliquora can never show you the full key again after creation, so store it securely (for example
in your secrets manager or environment variables). A request with a missing or invalid key returns
401 with a WWW-Authenticate: Bearer header. A request from a plan without API access
also returns 401.
Creating and managing keys
Keys are created in the app by an administrator (the system administrator or
administrator role) under Settings → API Access. Each key has a human-readable
name so you can tell your integrations apart. On creation, the API returns the plaintext key exactly once as the
rawKey field; afterwards only a masked keyPrefix is shown.
{
"id": 7,
"name": "Integration server",
"keyPrefix": "lk_3f9a2b1c...",
"createdBy": 13,
"createdAt": "2026-06-18T12:00:00.000Z",
"lastUsedAt": null,
"revokedAt": null,
"rawKey": "lk_3f9a2b1c… ← shown only once"
} You can list your keys, see when each was last used, and revoke a key at any time from the same screen. Revoking a key takes effect immediately. Every key is scoped to your organization and can only read and write your tenant's data.
Upload-only keys
When creating a key you can mark it upload-only. An upload-only key can call
POST /api/v1/instrument-ingest (and GET /api/v1/whoami) but nothing else — any other
endpoint returns 403 with error upload_only_key. Use upload-only keys on instrument
PCs and folder-watcher agents, where a leaked key should never be able to read your lab's data.
Verifying a key
curl https://aliquora.com/api/v1/whoami \
-H "Authorization: Bearer lk_your_key_here" GET /api/v1/whoami echoes back the key's organization and ids:
{
"ok": true,
"organizationId": 42,
"keyId": 7,
"userId": 13,
"version": "v1"
} Endpoints
The v1 endpoints, grouped by area. The discovery endpoint GET /api/v1 is always the authoritative, current list.
Identity
| Method | Path | Description |
|---|---|---|
GET | /api/v1 | Discovery — name, version, and the full endpoint list. |
GET | /api/v1/whoami | The calling key's organization, key id, and user id. |
Samples
| Method | Path | Description |
|---|---|---|
GET | /api/v1/samples | List samples (filterable, cursor-paginated). |
GET | /api/v1/samples/:id | Retrieve one sample with its scheduled tests. |
POST | /api/v1/samples | Create a sample and optionally schedule tests. |
PATCH | /api/v1/samples/:id | Update mutable fields (not workflow status). |
PATCH | /api/v1/samples/:id/status | Transition workflow status. |
POST | /api/v1/samples/receive | Bulk-receive a set of samples by id (409 not_receivable for samples not awaiting receiving). |
POST | /api/v1/samples/:id/report | Mark a sample as reported. Enforces every release gate — 409 incomplete_sample / qc_failed / approval_required / confirmation_required / media_qc_failed. |
GET | /api/v1/samples/:id/approvals | List approval records for a sample. |
Tests & results
| Method | Path | Description |
|---|---|---|
GET | /api/v1/sample-tests | List sample tests (filter by sample or status). |
PATCH | /api/v1/sample-tests/:id | Enter or update a result (409 test_verified_locked if verified; 409 reagent_lot_expired if a blocked reagent lot is used). |
POST | /api/v1/sample-tests/:id/verify | Verify and lock a result. Signing act — enforces release gates: 403 esignature_required, 409 qc_failed / confirmation_required / media_qc_failed / worksheet_incomplete / blank_result. |
POST | /api/v1/sample-tests/bulk-results | Enter many results in one call (409 test_verified_locked if any are locked). |
POST | /api/v1/instrument-uploads | AI extraction of results from an instrument file. |
POST | /api/v1/instrument-ingest | Deterministic instrument-file ingest — imports or quarantines for in-app review. |
PATCH | /api/v1/approval-records/:id | Approve / reject an approval record (403 esignature_required when password re-auth for signatures is required). |
Reference data
| Method | Path | Description |
|---|---|---|
GET | /api/v1/test-definitions | List test definitions (codes, units, spec limits). |
GET | /api/v1/material-types | List material/matrix types. |
GET | /api/v1/projects | List projects. |
POST | /api/v1/projects | Create a project. |
PATCH | /api/v1/projects/:id | Update a project. |
GET | /api/v1/clients | List clients. |
POST | /api/v1/clients | Create a client. |
PATCH | /api/v1/clients/:id | Update a client. |
GET | /api/v1/storage-locations | List storage locations. |
Working with samples
List samples
GET /api/v1/samples supports the filters status, materialTypeId,
projectId, trackingId, since and until (by received date),
plus the pagination parameters limit and cursor.
GET /api/v1/samples?limit=100&cursor=1042&status=received {
"data": [
{
"id": 1041,
"trackingId": "S-2026-001041",
"status": "received",
"materialTypeId": 3,
"projectId": 8,
"receivedAt": "2026-06-18T09:12:00.000Z"
}
],
"nextCursor": 1041,
"hasMore": true
} Create a sample
POST /api/v1/samples registers a sample and, if you pass testDefinitionIds, schedules
those tests in one call. Referenced ids (material type, project, test definitions) must belong to your
organization, or the call returns 400 invalid_reference. Projects that use a composite sample
identifier also require plantLocation. Creating a sample counts against your plan's monthly sample
allowance.
curl -X POST https://aliquora.com/api/v1/samples \
-H "Authorization: Bearer lk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"clientName": "Acme Water District",
"materialTypeId": 3,
"projectId": 8,
"collectionDate": "2026-06-18",
"priority": "normal",
"testDefinitionIds": [12, 15],
"notes": "Grab sample, intake basin"
}' Move a sample through its workflow
Status is changed only through the dedicated endpoint PATCH /api/v1/samples/:id/status (the general
PATCH /api/v1/samples/:id updates other fields but not status). Valid statuses are
pending, received, processing, completed, and
verified.
curl -X PATCH https://aliquora.com/api/v1/samples/1042/status \
-H "Authorization: Bearer lk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "status": "received" }'
Moving a sample to completed (Technical Review) is blocked with
409 incomplete_sample until every test has a result entered — an API key cannot skip the
completeness check the app enforces.
To bring in a batch at once, use POST /api/v1/samples/receive with a
{ "sampleIds": [ ... ] } body; samples that are not awaiting receiving are reported back
under skipped with 409 not_receivable. When results are complete and verified,
POST /api/v1/samples/:id/report marks the sample reported — this call enforces every release
gate (see below), so it can return 409 with incomplete_sample, qc_failed,
approval_required, confirmation_required, or media_qc_failed.
Entering results
List tests with GET /api/v1/sample-tests (filter by sampleId or status),
then enter a result with PATCH /api/v1/sample-tests/:id. Once a result is verified it is locked;
further edits return 409 test_verified_locked. Verify a result with
POST /api/v1/sample-tests/:id/verify.
Verification is a signing action, so the verify endpoint enforces the same release and compliance gates as the
app. It returns 403 esignature_required when your organization requires password re-authentication
for electronic signatures (an API key can never provide it), and 409 with qc_failed,
confirmation_required, media_qc_failed, worksheet_incomplete, or
blank_result when a gate is not satisfied. These blocks cannot be bypassed through the API — see the
release-gates section below.
For high-throughput entry, send many results in a single request:
curl -X POST https://aliquora.com/api/v1/sample-tests/bulk-results \
-H "Authorization: Bearer lk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"updates": [
{ "sampleTestId": 5001, "result": "7.2", "notes": "pH in range" },
{ "sampleTestId": 5002, "result": "1.4" }
]
}' AI instrument uploads
POST /api/v1/instrument-uploads accepts a raw instrument export as multipart form data
(file) and uses AI to extract results, matching them back to the right sample tests by tracking id
and test code. Optional fields: hint (free-text context), dryRun (preview matches
without writing), and confidenceThreshold (0–1). The response returns the proposed matches with a
per-row confidence score so you can review before committing. Because these calls run AI extraction, they are
rate-limited more tightly than other endpoints (see below). If AI data access is disabled for your lab the call
returns 403 ai_data_access_disabled, and if the server has no AI integration configured it returns
503 ai_unavailable.
curl -X POST https://aliquora.com/api/v1/instrument-uploads \
-H "Authorization: Bearer lk_your_key_here" \
-F "[email protected]" \
-F "dryRun=true" \
-F "confidenceThreshold=0.8" Pagination
List endpoints use cursor-based pagination. Pass limit (default 50, maximum
500) and an optional cursor (the id to read after). Each response includes the page
of records under data, plus nextCursor and hasMore. To page through
everything, keep calling with cursor set to the previous nextCursor until
hasMore is false.
Rate limits
| Endpoint group | Limit |
|---|---|
| All endpoints (per API key) | 120 requests / minute |
| AI instrument uploads | 20 requests / minute |
Exceeding a limit returns 429 with { "error": "rate_limited" }. Build a short
backoff into your client and retry.
Release gates & compliance blocks
Aliquora enforces the same data-integrity, QC, and compliance controls through the API that it enforces in the
app — an API key can never quietly defeat a release gate. The verify, report, status, and result endpoints return
a structured error slug and a 409 (or 403) so your integration can detect
the block and route it to the right person. Resolve the underlying condition in the app (or apply a documented
override where the workflow allows one) and retry.
| Status | Slug | What it means |
|---|---|---|
403 | esignature_required | Verifying or approving requires password re-authentication for the electronic signature. This must be signed by an authenticated user in the app; an API key cannot perform it. |
409 | incomplete_sample | The sample has tests that are not entered and verified. Report (and Technical Review entry) require all results complete and verified. |
409 | qc_failed | A failed, non-overridden QC batch blocks release. The blocking batches are listed in the response. |
409 | approval_required | Final approval is not complete and the org disallows CoA before final approval. |
409 | confirmation_required | A presumptive-positive / confirmatory (microbiology) result is awaiting confirmation. |
409 | media_qc_failed | A media lot used on the sample failed its growth-promotion / sterility QC. |
409 | worksheet_incomplete | A required method worksheet is incomplete or unsigned. The response includes missing and needsSignoff. |
409 | blank_result | A result cannot be verified because it is blank. |
409 | reagent_lot_expired | Result entry references an expired reagent lot while the org's reagent-expiry policy is set to block. |
Errors
Most errors are returned as JSON with an error slug and a human-readable message, alongside
the appropriate HTTP status code. Some validation and plan-limit responses instead carry a code field
(for example SAMPLE_LIMIT_EXCEEDED) or a message only.
{
"error": "rate_limited",
"message": "Too many requests. Limit is 120/minute per API key."
} | Status | Common slug(s) | Meaning |
|---|---|---|
400 | invalid_body / invalid_reference / invalid_id / field_not_patchable / no_updatable_fields | Malformed input, a referenced id does not belong to your organization, a field must be changed through its dedicated endpoint, or an empty update was sent. |
401 | unauthorized | Missing or invalid API key, or your plan does not include API access. Returned with a WWW-Authenticate: Bearer header. |
403 | upload_only_key / esignature_required / scope_required / ai_data_access_disabled (or code: SAMPLE_LIMIT_EXCEEDED) | The key is upload-only, a signing action requires in-app password re-auth, the key lacks the required scope, AI data access is off — or a plan limit (e.g. your monthly sample quota) was reached. |
404 | not_found | The resource does not exist in your organization. |
409 | test_verified_locked / qc_failed / incomplete_sample / approval_required / confirmation_required / media_qc_failed / worksheet_incomplete / blank_result / reagent_lot_expired / not_receivable | The record is locked, or a release/QC/compliance gate blocked the action. See “Release gates & compliance blocks” below. |
429 | rate_limited | You exceeded the per-key request limit. Back off and retry. |
500 | internal | An unexpected server error. Safe to retry idempotent reads. |
503 | ai_unavailable | The AI integration is not configured on this server (instrument-uploads only). |
Versioning & stability
The API is versioned in the path (/api/v1). We add new endpoints and optional fields without bumping
the version; any breaking change would ship under a new version path. The authoritative, always-current list of
routes is the discovery endpoint GET /api/v1 and the in-app API Access page.
Building an integration? Create a key under Settings → API Access and start
with GET /api/v1/whoami. If you need an endpoint we don't expose yet, or help mapping an
instrument feed, get in touch.
This document describes Aliquora product capabilities as of the date provided and is offered for evaluation purposes. It is not legal or regulatory advice and is not a warranty of compliance. Each organization is responsible for validating and confirming that the system meets its own regulatory obligations. Generated by Aliquora · aliquora.com