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.
The public API is currently in beta, some endpoints and their conetns may change over time.
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:
Sign in to your Lab Atlas tenant.
Open the user menu in the top-right corner and choose Account settings.
Select the API keys tab.
Click Create new key.
Give the key a memorable display name (for example,
lims-importer,nightly-backup) and choose how long it should remain valid.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
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:
Visibility —
findAllandfindByIdendpoints 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.Contribute —
create,update,delete,status, andarchiveendpoints require the user to be a project member (or higher). Calls that fail this check return403 Forbidden. Looking up a record the user cannot view returns404 Not Foundinstead — 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_ADMINrole on the owning user. A non-admin key on these endpoints returns403.
If you revoke a user's admin role, every key they own immediately loses access to admin-only endpoints.
Conventions
Base URL & versioning
/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:
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
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.
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:
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:
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):
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.
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.
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.
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.
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.
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.
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.
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
External links are URL references attached to a project, study, or assay.
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.
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.
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.
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.
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.
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".
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.
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.
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.
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_xxxxxxxxprefix is safe to share; never share the secret after the dot)
Last updated