Skip to content
All documents

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

MethodPathDescription
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

MethodPathDescription
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

MethodPathDescription
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

MethodPathDescription
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 groupLimit
All endpoints (per API key)120 requests / minute
AI instrument uploads20 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.

StatusSlugWhat it means
403esignature_requiredVerifying 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.
409incomplete_sampleThe sample has tests that are not entered and verified. Report (and Technical Review entry) require all results complete and verified.
409qc_failedA failed, non-overridden QC batch blocks release. The blocking batches are listed in the response.
409approval_requiredFinal approval is not complete and the org disallows CoA before final approval.
409confirmation_requiredA presumptive-positive / confirmatory (microbiology) result is awaiting confirmation.
409media_qc_failedA media lot used on the sample failed its growth-promotion / sterility QC.
409worksheet_incompleteA required method worksheet is incomplete or unsigned. The response includes missing and needsSignoff.
409blank_resultA result cannot be verified because it is blank.
409reagent_lot_expiredResult 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."
}
StatusCommon slug(s)Meaning
400invalid_body / invalid_reference / invalid_id / field_not_patchable / no_updatable_fieldsMalformed 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.
401unauthorizedMissing or invalid API key, or your plan does not include API access. Returned with a WWW-Authenticate: Bearer header.
403upload_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.
404not_foundThe resource does not exist in your organization.
409test_verified_locked / qc_failed / incomplete_sample / approval_required / confirmation_required / media_qc_failed / worksheet_incomplete / blank_result / reagent_lot_expired / not_receivableThe record is locked, or a release/QC/compliance gate blocked the action. See “Release gates & compliance blocks” below.
429rate_limitedYou exceeded the per-key request limit. Back off and retry.
500internalAn unexpected server error. Safe to retry idempotent reads.
503ai_unavailableThe 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