For the complete documentation index, see llms.txt. This page is also available as Markdown.

REST API Reference

How to use the Lab Atlas public API

The Lab Atlas public REST API lets external clients — scripts, integrations, internal tooling, and partner systems — interact with the same projects, studies, assays, and supporting resources that the Lab Atlas web client uses. This guide explains how to obtain credentials, authenticate requests, and call each endpoint.

The Lab Atlas public API is currently available only to Enterprise customers.

Quick start

# Set your tenant base URL and API key
export LA_BASE=https://your-tenant.labatlas.com
export LA_KEY=lak_xxxxxxxx.xxxxxxxxxxxxxxxxxxxx

# List the projects visible to your account
curl -s "$LA_BASE/api/beta/projects" \
     -H "Authorization: ApiKey $LA_KEY" | jq .

# Create a new project
curl -s -X POST "$LA_BASE/api/v1/projects" \
     -H "Authorization: ApiKey $LA_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "code": "MYPROJ",
       "name": "My Project",
       "description": "Created via the public API.",
       "visibility": "PRIVATE"
     }'

Getting an API key

API keys are issued from inside the Lab Atlas web application. Each key is owned by a specific organization user, and every request made with that key executes under that user's permissions — there is no separate service account.

To create a key:

  1. Sign in to your Lab Atlas tenant.

  2. Open the user menu in the top-right corner and choose Account settings.

  3. Select the API keys tab.

  4. Click Create new key.

  5. Give the key a memorable display name (for example, lims-importer, nightly-backup) and choose how long it should remain valid.

  6. Copy the key value from the modal that appears. This is the only time the full key value is shown. Store it somewhere safe — a secret manager, your CI's environment variables, or an encrypted note.

You can also list, rotate, or revoke your existing keys from the same screen. Revoking a key takes effect immediately; any request presenting the revoked key will fail with 401 Unauthorized.

Heads up: the API-key UI is only available when your Lab Atlas tenant has the Enterprise feature flag enabled. If you do not see the API keys tab, contact your administrator.

A key value looks like lak_<keyId>.<secret>. The lak_ prefix and keyId segment are stable identifiers; the segment after the dot is the secret material.

Per-user, per-organization

Each key is bound to exactly one (user, organization) pairing. If you belong to two organizations, you need a separate key for each. Switching your active organization in the web UI does not change the org a previously-issued key is scoped to.

Key lifecycle

Action
Result

Create

Key is active and usable immediately

View list

Shows key metadata only (display name, creation date, last-used time). Secret material is never re-displayed.

Rotate

Revoke the old key and create a new one. There is no atomic rotation primitive.

Revoke

Future requests with this key return 401 Unauthorized. The key remains in the audit log.

Expire

Keys expire automatically after their configured duration. Expired requests return 401.


Authentication

Every request to /api/beta/** must present a valid API key. Two header formats are accepted; pick whichever fits your client best:

Do not use Authorization: Bearer …. Bearer tokens are reserved for the internal /api/i/** API and will be rejected on the public endpoints.

Missing, expired, malformed, or revoked keys return 401 Unauthorized with no response body. Keys that are valid but lack the permission required for the action return 403 Forbidden.

Required transport

All requests must be made over HTTPS. The API enforces HSTS and will reject plain-HTTP traffic at the load balancer.

CORS

The public API is intended for server-to-server use. Cross-origin browser requests are not enabled by default. If you need to call the API from a browser-based application, deploy a thin server-side proxy in front of it.


Permissions model

Because every key is bound to an organization user, authorization decisions follow the same rules used by the web UI:

  • VisibilityfindAll and findById endpoints only return resources the key's owning user can view. For projects this is determined by the project's visibility setting (PUBLIC, PROTECTED, PRIVATE) plus the user's role and team memberships.

  • Contributecreate, update, delete, status, and archive endpoints require the user to be a project member (or higher). Calls that fail this check return 403 Forbidden. Looking up a record the user cannot view returns 404 Not Found instead — this is intentional and avoids leaking the existence of records the user has no business knowing about.

  • Admin-only operations — Team writes and Assay-Type writes additionally require the ORGANIZATION_ADMIN role on the owning user. A non-admin key on these endpoints returns 403.

If you revoke a user's admin role, every key they own immediately loses access to admin-only endpoints.


Conventions

Base URL & versioning

Mount
Status

/api/beta/...

Current. Use this.

/api/v1/...

Once finalized, this will become the stable base of the version 1 API.

/api/i/...

Internal API used by the Lab Atlas web client. Not for external use. API keys are rejected on this mount.

Content types

All requests and responses use application/json with UTF-8 encoding.

Resource identifiers

Identifiers are UUIDs (version 4), serialised as canonical lowercase strings with hyphens — e.g. b1d1b15a-19e0-4d6f-8d33-7a3d3aef6cba.

Dates

Dates are ISO 8601 strings in UTC. Both date-only (2026-05-06) and date-time (2026-05-06T14:32:11.000+00:00) formats are accepted on input.

Pagination

List endpoints return a page object compatible with Spring Data's Page<T> envelope:

You can request a specific page and size using query parameters:

Param
Default
Notes

page

0

Zero-indexed.

size

20

Maximum permitted size depends on the resource; large pages may be capped at 200.

sort

resource-specific

field,direction pairs. Repeat the parameter to sort by multiple fields.

Status codes

Code
Meaning

200 OK

Successful read or update. Response body present.

201 Created

New resource created. Body contains the created representation.

204 No Content

Successful delete. No body.

400 Bad Request

Malformed payload or invalid query parameter.

401 Unauthorized

Missing, expired, malformed, or revoked API key.

403 Forbidden

Authenticated but lacking the required permission (e.g. not a contributor, or not an org admin).

404 Not Found

Resource does not exist, or does exist but the key's owner cannot view it.

409 Conflict

Concurrent update conflict or duplicate identifier.

5xx

Server-side error. Try again or contact support if it persists.


Endpoint reference

The tables below list every public endpoint. Path-prefix is https://<tenant>/api/beta. Where an endpoint requires ORGANIZATION_ADMIN, this is noted in the right-most column.

Projects

Projects are the top-level container for studies and assays.

Method
Path
Description
Auth

GET

/projects

Paginated list of projects visible to the caller.

Member

GET

/projects/{id}

Fetch a single project.

Member

POST

/projects

Create a new project.

Member

PUT

/projects/{id}

Update name, description, visibility, etc.

Contributor

POST

/projects/{id}/status

Change project status. Body: {"status": "ACTIVE"}.

Contributor

POST

/projects/{id}/archive?archived=true|false

Archive or restore.

Contributor

DELETE

/projects/{id}

Delete the project (and its studies, assays).

Contributor

Create example

Project status values: CREATED, INITIALIZING, INITIALIZATION_FAILED, ACTIVE, COMPLETE, ON_HOLD, DEACTIVATED.

Visibility values: PUBLIC, PROTECTED, PRIVATE.

Choosing storage (optional)

By default a new project's files are provisioned in your organization's default cloud storage. To place the project's primary folder elsewhere, include an optional storage object on the create payload:

Field
Type
Description

driveId

UUID

The storage drive to use (see GET /storage-drives).

mode

enum

CREATE_NEW — create a new folder under path; USE_EXISTING — attach the folder that already exists at path.

path

string

For CREATE_NEW, the parent folder the new folder is created under; for USE_EXISTING, the folder itself. Ignored when driveId is the default cloud drive (the org's project root is always used).

name

string

Optional folder name (CREATE_NEW only).

Omit the storage object entirely to use the default cloud location. Each project has exactly one primary folder at creation; attach additional folders afterward via POST /projects/{id}/storage-folders (see Storage folders).

Choosing an ELN notebook (optional)

If your organization has a connected Electronic Lab Notebook (ELN), you can link the new project to an existing notebook folder at creation time. Each platform has its own block; include the one matching your integration. Omit both to leave ELN provisioning off. The folder must already exist — the API links it as the project's primary notebook folder, it does not create a new one.

Benchling — benchling object:

Field
Type
Description

integrationId

UUID

The Benchling integration to use (see GET /integrations).

folderId

string

The Benchling folder to link as the project's primary notebook folder.

CDD Vault — cddVault object (CDD Vault has no nested folders, so you link an existing project container):

Field
Type
Description

integrationId

UUID

The CDD Vault integration to use (see GET /integrations).

projectId

number

The CDD Vault project (container) to link.

Choosing a default Git group (optional)

If your organization has a connected GitLab integration, you can give the project a default Git group via a defaultGitGroup object on create/update. It is a convenience pre-fill only: when Git repositories are created for studies or assays under this project, this group is pre-selected — callers may always choose a different group, and no repository is created for the project itself. Omit it for no default.

Field
Type
Description

integrationId

UUID

The GitLab integration the group belongs to (see GET /integrations).

groupId

string

The id of the Git group to use as the default, as it appears in your GitLab instance.

A stored default that no longer resolves (group removed, integration disconnected) is simply ignored when pre-filling — it never blocks repository creation.


Studies

Studies belong to projects and group together related experiments.

Method
Path
Description
Auth

GET

/studies

Paginated list of studies visible to the caller.

Member

GET

/studies/{id}

Fetch a single study.

Member

POST

/studies

Create a new study under a project.

Contributor on parent project

PUT

/studies/{id}

Update study fields.

Contributor

POST

/studies/{id}/status

Change status. Body: {"status": "COMPLETE"}.

Contributor

POST

/studies/{id}/archive?archived=true|false

Archive or restore.

Contributor

PATCH

/studies/{id}/move

Move to a different project. Body: {"projectId": "..."}.

Contributor on both projects

DELETE

/studies/{id}

Delete the study.

Contributor

Create example

Study status values: CREATED, INITIALIZING, INITIALIZATION_FAILED, ACTIVE, COMPLETE, ON_HOLD, DEACTIVATED.

Choosing an ELN notebook (optional)

If the parent project is linked to an ELN, you can provision notebook content for the study at creation time. Unlike a project (which links an existing folder), a study inherits the project's ELN integration and gets its own child content. Omit both fields to leave ELN provisioning off.

Benchling — benchling object: creates a notebook folder under the project's Benchling folder and a notebook entry in it.

Field
Type
Description

templateId

string

Optional Benchling entry template to apply. Omit for a blank entry.

templateFields

object

Optional field values for the chosen template ({ "Field name": value }).

CDD Vault — useCddVault boolean: when true, creates a notebook entry under the project's CDD Vault container. CDD Vault has no nested folders, so there is nothing else to configure per study.


Assays

Assays are the lowest-level experimental unit; each assay belongs to one study and has a defined assay type.

Method
Path
Description
Auth

GET

/assays

Paginated list of assays visible to the caller.

Member

GET

/assays/{id}

Fetch a single assay.

Member

POST

/assays

Create a new assay under a study. Body must include studyId and assayTypeId.

Contributor on parent study

PUT

/assays/{id}

Update assay fields.

Contributor

POST

/assays/{id}/status

Change status.

Contributor

POST

/assays/{id}/archive?archived=true|false

Archive or restore.

Contributor

PATCH

/assays/{id}/move

Move to a different study. Body: {"studyId": "..."}.

Contributor on both studies

DELETE

/assays/{id}

Delete the assay.

Contributor

Create example

Choosing an ELN notebook (optional)

If the assay's project is linked to an ELN, you can provision notebook content for the assay at creation time. Like a study, an assay inherits the project's ELN integration and gets its own child content. Omit both fields to leave ELN provisioning off.

Benchling — benchling object: creates a notebook folder under the parent study's Benchling folder and a notebook entry in it.

Field
Type
Description

templateId

string

Optional Benchling entry template to apply. Omit for a blank entry.

templateFields

object

Optional field values for the chosen template ({ "Field name": value }).

CDD Vault — useCddVault boolean: when true, creates a notebook entry under the project's CDD Vault container. CDD Vault has no nested folders, so there is nothing else to configure per assay.


Notes

Notes are free-text annotations attached to a project, study, assay, or your organization. The v1 API exposes the current content of a note; full version history is internal-only. Note endpoints are read-only on the public API — create, update, and archive are performed through the Lab Atlas web UI.

Method
Path
Description

GET

/projects/{projectId}/notes

List notes on a project.

GET

/projects/{projectId}/notes/{id}

Fetch a single note.

GET

/studies/{studyId}/notes

List notes on a study.

GET

/studies/{studyId}/notes/{id}

Fetch a single note.

GET

/assays/{assayId}/notes

List notes on an assay.

GET

/assays/{assayId}/notes/{id}

Fetch a single note.

GET

/organization-notes

List notes on your organization.

GET

/organization-notes/{id}

Fetch a single organization note.

Archived notes are not returned by either the list or get-by-id endpoints. The id returned by a list is the note's id and resolves directly via the matching get-by-id endpoint.

Note representation

A note's type is one of NOTE, COMMENT, CONCLUSION, TEMPLATE; its format is one of TIPTAP, MARKDOWN, TEXT, QUILL. The content field holds the current version's body.


Tasks

Tasks are checklist items attached to a study or assay.

Method
Path
Description

GET

/studies/{studyId}/tasks

List tasks.

GET

/studies/{studyId}/tasks/{taskId}

Fetch a single task.

POST

/studies/{studyId}/tasks

Add a task.

PUT

/studies/{studyId}/tasks/{taskId}

Update a task.

PATCH

/studies/{studyId}/tasks/{taskId}/complete

Mark a task complete. Optional body for completion data.

DELETE

/studies/{studyId}/tasks/{taskId}

Remove a task.

GET

/assays/{assayId}/tasks

List tasks.

GET

/assays/{assayId}/tasks/{taskId}

Fetch a single task.

POST

/assays/{assayId}/tasks

Add a task.

PUT

/assays/{assayId}/tasks/{taskId}

Update.

PATCH

/assays/{assayId}/tasks/{taskId}/complete

Mark complete.

DELETE

/assays/{assayId}/tasks/{taskId}

Remove.

Task input body

Status values: TODO, COMPLETE, INCOMPLETE.


External links are URL references attached to a project, study, or assay.

Method
Path
Description

GET

/projects/{projectId}/external-links

List links.

GET

/projects/{projectId}/external-links/{linkId}

Fetch a single link.

POST

/projects/{projectId}/external-links

Add a link.

PUT

/projects/{projectId}/external-links/{linkId}

Update.

DELETE

/projects/{projectId}/external-links/{linkId}

Remove.

GET

/studies/{studyId}/external-links

List links.

GET

/studies/{studyId}/external-links/{linkId}

Fetch.

POST

/studies/{studyId}/external-links

Add.

PUT

/studies/{studyId}/external-links/{linkId}

Update.

DELETE

/studies/{studyId}/external-links/{linkId}

Remove.

GET

/assays/{assayId}/external-links

List links.

GET

/assays/{assayId}/external-links/{linkId}

Fetch.

POST

/assays/{assayId}/external-links

Add.

PUT

/assays/{assayId}/external-links/{linkId}

Update.

DELETE

/assays/{assayId}/external-links/{linkId}

Remove.

External-link input body


Collaborators

Collaborators model the external organizations (CROs, partner labs, vendors) you work with. They are organization-scoped — every collaborator belongs to one Lab Atlas organization.

Method
Path
Description

GET

/collaborators

Paginated list.

GET

/collaborators/{id}

Fetch a single collaborator.

POST

/collaborators

Create.

PUT

/collaborators/{id}

Update.

DELETE

/collaborators/{id}

Remove.

Collaborator input body


Teams

Teams are reusable groups of organization users that can be granted access to projects in one step. Team writes require ORGANIZATION_ADMIN.

Method
Path
Description
Auth

GET

/teams

Paginated list.

Member

GET

/teams/{id}

Fetch a team.

Member

POST

/teams

Create a team.

Admin

PUT

/teams/{id}

Update name, description, active flag, member list.

Admin

POST

/teams/{id}/users

Add users. Body: {"userIds": ["...", "..."]}.

Admin

DELETE

/teams/{id}/users

Remove users. Body: {"userIds": [...]}.

Admin


Keywords

Keywords are simple tag strings shared across an organization.

Method
Path
Description

GET

/keywords

Paginated list.

GET

/keywords/{id}

Fetch a keyword.

POST

/keywords

Create. Body: {"value": "AKT1"}.

Keyword values are case-sensitive and must be unique within an organization.


Assay types

Assay types define the metadata schema (fields and standard tasks) for assays. Assay-type writes require ORGANIZATION_ADMIN.

Method
Path
Description
Auth

GET

/assay-types

Paginated list.

Member

GET

/assay-types/{id}

Fetch a single assay type, including its fields and tasks.

Member

POST

/assay-types

Create.

Admin

PUT

/assay-types/{id}

Update.

Admin

POST

/assay-types/{id}/status

Change status. Body: {"status": "ACTIVE"}.

Admin

DELETE

/assay-types/{id}

Delete.

Admin

Status values: DRAFT, ACTIVE, INACTIVE, ARCHIVED, DELETED.


Study collections

Study collections are named groupings of studies, useful for ad-hoc reporting or cross-project comparisons.

Method
Path
Description

GET

/study-collections

List collections visible to the caller.

GET

/study-collections/{id}

Fetch a single collection (includes its studies).

POST

/study-collections

Create.

PUT

/study-collections/{id}

Update name/description/visibility.

DELETE

/study-collections/{id}

Delete.

POST

/study-collections/{id}/studies/{studyId}

Add a study to the collection.

DELETE

/study-collections/{id}/studies/{studyId}

Remove a study from the collection.

Create body


Study relationships

Study relationships record links between studies — e.g. "is parent of", "is blocked by", "is related to".

Method
Path
Description

GET

/studies/{studyId}/relationships

List relationships originating from the given study.

POST

/studies/{studyId}/relationships

Create a new relationship.

DELETE

/studies/{studyId}/relationships/{relationshipId}

Remove a relationship.

Create body

Relationship types: IS_RELATED_TO, IS_PARENT_OF, IS_CHILD_OF, IS_BLOCKING, IS_BLOCKED_BY, IS_PRECEDED_BY, IS_SUCCEEDED_BY.

When you create a relationship, the inverse relationship is automatically created on the target study.


Activity

Activity records describe events that have happened in the organization — study status changes, assay creation, note edits, and so on. Activity is read-only; events are generated by Lab Atlas itself as side effects of other API calls.

Visibility follows the same rules as the underlying resources: a caller can only see activity for projects, studies, and assays they are allowed to view. Cross-organization lookups return 404.

Method
Path
Description

GET

/activity

Paginated org-wide activity visible to the caller.

GET

/activity/{id}

Fetch a single activity event by UUID.

GET

/projects/{id}/activity

Activity scoped to a single project.

GET

/studies/{id}/activity

Activity scoped to a single study.

GET

/assays/{id}/activity

Activity scoped to a single assay.

GET

/organization-users/{id}/activity

Activity performed by a specific organization user.

Activity DTO shape

The eventType field is a dotted string (e.g. study.created, assay.status_changed, note.updated). The shape of data depends on the event type — treat it as an opaque map of strings to JSON values when consuming generically.


Storage folders

Storage folders link a project, study, or assay to a folder on a connected storage drive. Each record can have several attached folders, one of which is the primary folder. {folderId} is the id of the attachment (the join record), not the underlying drive folder. Responses contain folder metadata only — the API never lists the folder's subfolders or files.

Method
Path
Description

GET

/projects/{projectId}/storage-folders

List folders attached to a project.

GET

/projects/{projectId}/storage-folders/{folderId}

Fetch a single attachment.

POST

/projects/{projectId}/storage-folders

Attach an existing drive folder. Returns 201.

PATCH

/projects/{projectId}/storage-folders/{folderId}

Update the attachment (set as primary).

DELETE

/projects/{projectId}/storage-folders/{folderId}

Detach the folder (the drive folder is left intact).

GET

/studies/{studyId}/storage-folders

List folders attached to a study.

GET

/studies/{studyId}/storage-folders/{folderId}

Fetch a single attachment.

POST

/studies/{studyId}/storage-folders

Attach an existing drive folder.

PATCH

/studies/{studyId}/storage-folders/{folderId}

Set as primary.

DELETE

/studies/{studyId}/storage-folders/{folderId}

Detach.

GET

/assays/{assayId}/storage-folders

List folders attached to an assay.

GET

/assays/{assayId}/storage-folders/{folderId}

Fetch a single attachment.

POST

/assays/{assayId}/storage-folders

Attach an existing drive folder.

PATCH

/assays/{assayId}/storage-folders/{folderId}

Set as primary.

DELETE

/assays/{assayId}/storage-folders/{folderId}

Detach.

All write operations require contribute permission on the parent project/study/assay.

Attach body

The folder at path must already exist on the drive. Set-primary body is {"primary": true}.


Read-only resources

The following resources are exposed for reads only in the public API. Create/update/delete must be performed through the Lab Atlas web UI.

Resource
Endpoints

Users

GET /users, GET /users/{id}

Organization users

GET /organization-users, GET /organization-users/{id}

Integrations

GET /integrations, GET /integrations/{id}

Storage drives

GET /storage-drives, GET /storage-drives/{id}

Shared storage folders

GET /shared-storage-folders, GET /shared-storage-folders/{id}


Errors

Errors are returned as a JSON envelope:

For validation failures (400), additional fields are included describing each invalid field:

The statusCode field carries the HTTP status as an integer (separate from the per-resource status enum some response bodies carry). The details field is a one-line human-readable summary of all field violations — handy for log lines and quick triage; for programmatic handling, iterate errors[].

A request that authenticates successfully but is denied at the controller layer (e.g. an admin endpoint called by a non-admin key) returns 403 Forbidden with an empty body. A request that targets a resource the caller cannot view returns 404 Not Found rather than 403 — this avoids leaking the existence of records the caller has no business knowing about.


OpenAPI / Swagger

The full machine-readable specification is available at:

  • Swagger UI: https://<your-tenant>.labatlas.com/docs/swagger-ui (select the public-v1 group)

  • OpenAPI JSON: https://<your-tenant>.labatlas.com/docs/api-docs/public-v1

The Swagger UI is gated behind the same API-key check as the API itself — paste your key into the Authorize dialog (header name X-API-KEY) to try requests inline.

If you generate client SDKs from the OpenAPI document, regenerate after each Lab Atlas release; field additions are backwards-compatible but new endpoints may appear.


Support

If you hit a bug, need an endpoint that isn't listed here, or want to raise a security concern, email support@labatlas.com and include:

  • The full URL of the failing request

  • The HTTP status code returned

  • The response body (with any sensitive data redacted)

  • A timestamp (UTC) so support can correlate against server logs

  • The key id portion of the API key in use (the lak_xxxxxxxx prefix is safe to share; never share the secret after the dot)

Last updated