Skip to Content
ResearchSignals REST API (Revvity)

Research: Signals Notebook REST API (Revvity)

FieldValue
TypeResearch
StatusActive
AuthorxRED Dev Team
Created2026-04-08
SourceRevvity Signals Developer Guide — REST API 
Related SADSAD-001
Related ADRADR-005, ADR-009
Related ResearchSignals Python SDK, LEM Platform Requirements

1. Overview

The Signals Notebook REST API is Revvity’s public API for programmatic access to the ELN platform. The majority of GUI actions are available via the API. Documentation is served via a Swagger UI specific to each Signals tenant — the Swagger is dynamic, meaning new APIs appear in real time when released.

All APIs are versioned, allowing clients to pin to a specific version to avoid breaking changes. The current base path is /api/rest/v1.0/.

Important: At Roche, direct access to this API is governed by LEM — all calls should go through the DataRiver Facade on MuleSoft or the LEM API Proxy (ADR-005). This page documents the native Signals API as provided by Revvity, which is what sits behind the MuleSoft layer.

2. Standards

  • JSON:API specification — all responses follow the JSON:API standard for document structure, headers, response codes, and resource relationships
  • SCIM 2.0 — User and Group Management APIs follow the System for Cross-domain Identity Management standard

Benefits of JSON:API

  • Consistent data structure across all endpoints
  • Efficient resource relationships — fetch related data in a single call
  • Included resources — request related data alongside the primary resource
  • Sparse fieldsets — minimise data overhead
  • Pre-existing client/server libraries: jsonapi.org/implementations 

3. Authentication

Two authentication mechanisms are supported:

API Keys

  • Included in the x-api-key HTTP header
  • Generated by Signals system admins for specific users
  • Max 10 users with API keys per tenant
  • API access rights match the associated user’s permissions
  • Created via: System Configuration → System Settings → API Key

When to use: Server-to-server integrations with no end-user interaction (automated archival, compliance dashboards, data syncs). Typically tied to a system/API user account.

OAuth Bearer Token (Implicit Grant)

  • Signals supports only the implicit grant type (RFC 6749 §4.2 )
  • User is redirected to <your-domain-url>/auth/oauth/authorize
  • After login + authorization, a token is returned via the redirect URI: <redirect_uri>#access_token=f60fxxxxd134d26a685737f3e6ceabe
  • Used as: Authorization: Bearer f60fxxxxd134d26a685737f3e6ceabe
  • Token valid for 30 days of activity — invalidated after 30 days of inactivity

Setup process:

  1. Request a Client ID from Revvity (via CSM, Account Manager, or Signals support), providing: application name and redirect URI(s)
  2. Your app redirects user to /auth/oauth/authorize with response_type, client_id, scope query params (values provided by Revvity)
  3. User authenticates via their IdP
  4. User authorizes your application
  5. Token returned via redirect URI fragment
  6. Your app extracts and caches the token

When to use: Integrations involving end-user interaction, especially those that modify data (entity modification, audit trail actions). Ensures actions are attributed to the correct user with their specific permissions.

4. Rate Limits

  • 1,000 requests per minute per tenant (API Keys + Bearer Tokens combined)
  • Rate limits are subject to change
  • This is the same tenant-wide quota documented in LEM Platform Requirements

5. JSON:API Response Structure

Every response has three top-level objects:

{ "links": { "self": "..." }, "data": { ... }, "included": [ ... ] }

data object

The primary resource with:

  • type — resource type (e.g. "entity")
  • id — resource identifier (e.g. "experiment:966a7304-...")
  • attributes — resource data (name, description, state, digest, fields, flags)
  • relationships — links to related resources (createdBy, editedBy, children, owner, pdf)

included array

Related resources referenced by relationships — users, child entities, etc. Match by type + id from the relationship’s data object.

Entity ID format

Entity IDs follow the pattern \1`:`\1:

  • experiment:966a7304-4436-4f84-b56b-053c2ba2e439
  • text:18849188-9d5a-4064-a287-e4fe402028f8
  • chemicalDrawing:865d61d8-16c0-41c5-adb1-9d2de7999a43
  • samplesContainer:ca05dfb4-d4cd-4910-a486-3ebaaf7982d7

Experiment entity types (children)

An experiment can contain these child entity types:

  • text — text blocks (Experiment Description, Setup, Conclusions)
  • chemicalDrawing — chemical structure drawings
  • samplesContainer — sample collections
  • materialsTable — materials tables
  • worksheet — worksheets
  • viewonly — attached documents
  • plateContainer — plate containers

6. Concurrent Editing and Digest

Every entity has a digest value — a unique identifier updated on every change, used for optimistic concurrency control.

How it works

  1. Entity is created/modified → server generates a digest
  2. Client reads entity, receives current digest
  3. Client sends modification with the digest as a URL parameter
  4. Server compares submitted digest with current state
  5. Match → changes accepted
  6. Mismatch428 error returned:
{ "errors": [{ "status": "428", "code": "DigestNotMatch", "detail": "Digest mismatch." }] }

Force override

Include force=true in URL parameters to bypass digest checking. Use with caution — this overwrites concurrent changes. If the entity is in a state that prevents editing (e.g. trashed), a 403 is returned even with force=true.

7. Error Handling

Standard HTTP response codes: 2xx success, 4xx client error, 5xx server error.

Error response format:

{ "errors": [{ "status": "401", "code": "Unauthorized", "title": "User is unauthorized", "detail": "No x-api-key is found in request header or the value of x-api-key is invalid." }] }

Key error codes:

CodeMeaning
401Missing or invalid x-api-key / Bearer token
403Access forbidden (e.g. entity is trashed)
428Digest mismatch (concurrent modification)

8. Search API

POST /entities/search with a JSON query body.

Basic search structure

{ "query": { "$and": [ { "$match": { "field": "type", "value": "experiment" } }, { "$match": { "field": "name", "value": "My Experiment" } }, { "$match": { "field": "isTemplate", "value": false } } ] }, "options": { "sort": { "modifiedAt": "desc" } } }

Query operators

  • $and — combine multiple conditions
  • $match — match a field value (with optional mode: "keyword")
  • $child — search within child entities
  • $chemsearch — chemical structure search (SMILES, InChI)

Search by custom metadata field

{ "query": { "$and": [ { "$match": { "field": "type", "value": "experiment", "mode": "keyword" } }, { "$match": { "field": "isTemplate", "value": false } }, { "$match": { "field": "fields.Project Identifier", "in": "tags", "as": "text", "value": "Biology-100", "mode": "keyword" } } ] }, "options": { "sort": { "createdAt": "desc" } } }

Chemical structure search (SMILES)

{ "query": { "$and": [ { "$match": { "field": "type", "value": "experiment", "mode": "keyword" } }, { "$match": { "field": "isTemplate", "value": false } }, { "$child": { "$chemsearch": { "molecule": "C1=CC=CC=C1", "mime": "chemical/x-daylight-smiles", "options": "full=true" } } } ] } }

Additional searchable fields, query operators, and search options are documented in the System Configuration Guide (admin access required).

9. Key API Endpoints

Based on the Swagger documentation and the MuleSoft SDK mapping:

CategoryEndpointMethodDescription
Entities/entities/\1“GETFetch any entity by EID
/entities/\1/childrenGETList child entities
/entities/searchPOSTSearch entities
Experiments/entities (type filter)GETList experiments
/entitiesPOSTCreate experiment
/entities/\1“PATCHUpdate experiment
Tables/entities/\1“GETGet table entity
/entities/\1/rowsGETGet table rows
/entities/\1/rowsPOSTAdd row
/entities/\1/rows/\1“PATCHUpdate row
/entities/\1/rows/\1“DELETEDelete row
Files/entities/\1/attachmentsGETList attachments
/entities/\1/attachmentsPOSTUpload file
/entities/\1/attachments/\1“GETDownload file
Users/usersGETList users (SCIM 2.0)
/users/\1“GETGet user
Groups/groupsGETList groups (SCIM 2.0)
Templates/templatesGETList templates

10. External Actions

External Actions allow users in Signals to trigger an external web application via HTTP GET or POST. They are the primary mechanism for xRED’s Apps layer (SAD-001).

How they work

  1. Admin configures an External Action for an entity type (experiment, sample, table, etc.)
  2. User clicks the action button in the Signals UI
  3. Signals redirects to the configured URL, passing entity context
  4. GET: Entity ID passed as URL parameter (default __eid): https://yourapplication.com/?__eid=experiment:03ae2d17-...
  5. POST: Full entity JSON sent as form data (includes id, type, attributes, relationships, ancestors)

Authentication pattern

Signals does not authenticate users on the external site — it simply redirects. The external application must:

  1. Authenticate the user independently (credentials, VPN, intranet, SSO)
  2. Initiate Bearer Token Exchange with Signals to get an API token
  3. Use the Bearer token for subsequent Signals API calls

Dialog messages (iFrame mode)

When opened in a dialog (iFrame), the external app communicates back to Signals via window.parent.postMessage():

CommandEffect
closeAndContinueClose dialog, continue signing flow
closeAndAbortClose dialog, abort signing flow
closeAndRefreshClose dialog, refresh the entity
closeClose the dialog
resizeResize the dialog
setTitleSet the dialog title

Triggers

  • User Action — button click (default, available for all entity types)
  • Signing Event — triggered during experiment signing workflows (experiments, parallel experiments, admin defined objects). Can specify which signing events (sign+close, sign+keep open, reopen, etc.)

Entity-specific configuration

EntityExtra Options
ExperimentsSigning event triggers
Tables/WorksheetsApply to all templates or specific ones
SamplesFilter by sample status
FoldersMust use HTTP POST; select specific folders

11. External Notifications

Notifications allow external systems to react to events in Signals. Two types:

Push Notifications

Sent to a configured URL when events trigger. For real-time workflows.

Notification body follows JSON:API format with:

  • type — event type (e.g. "close", "create", "print")
  • isDismissed — auto-set to true on 2xx response (if enabled)
  • isRead, isFlagged — manageable via API
  • comment — signing comment (for signing events)
  • relationships.entity — the entity that triggered the event
  • relationships.createdBy — the user who triggered it

Pull Notifications

Available via GET /notifications API. Used to catch up after downtime. Expire after 180 days.

Available triggers

TriggerNotification type values
Top Level Entity Creationcreate
All Export to PDF/ZIPprint
Signing Eventsclose, sign, close_without_signing, reopen, review_request_for_reviewer, two_stage_review_request_for_reviewer, review_accepted_for_owner, review_rejected_for_owner, first_review_accepted_for_owner, first_review_rejected_for_owner, second_review_accepted_for_owner, second_review_rejected_for_owner, archive, unarchive, void

Limits

  • 1 push webhook URL per tenant
  • Pull notifications expire after 180 days
  • If multiple systems need notifications, a fan-out service is required

Use both push and pull:

  • Auto-dismissed push notifications for real-time response
  • Regular pull polling + pull on service restart to catch missed notifications
  • Store notifications in a database, process asynchronously via a queue

12. External Data Sources and Lists

External Lists

Externally hosted dropdown lists for attribute fields.

Data format:

{ "data": [ { "type": "Cat" }, { "type": "Dog" }, { "type": "Mouse" } ] }

Each object can have multiple fields — one is selected as the “key” during configuration.

Limits:

  • Max 20,000 items per list
  • Max 200 lists per tenant
  • 30-second response timeout

External Data Sources

REST endpoints for table row CRUD — fetch, create, update based on a key value.

Operations:

MethodWhenExample
GET /\1“User enters a key in a table cellGET /flasks/88 → returns {id, name, description}
POST /User creates a new rowPOST /flasks/ with {name, description}
PUT /User updates an existing rowPUT /flasks/ with {id, name, description}

Limits:

  • 20-second response timeout

Authentication

Both External Lists and External Data Sources support a single header value (API key) configured by the administrator. This is the single-header constraint documented in SAD-001 and ADR-004.

13. Relevance to xRED ELN

What xRED accesses via MuleSoft (not directly)

The DataRiver Facade / MuleSoft Experiments API wraps a subset of these native endpoints. The Signals Python SDK auto-generates from the MuleSoft spec, which in turn proxies to these native endpoints.

Key constraints for integrations

ConstraintImpact
1,000 req/min tenant-wideAll consumers share this — caching is critical (ADR-009)
Max 10 API keys per tenantAPI keys are scarce — prefer OAuth Bearer for user-context operations
Implicit grant onlyNo authorization code flow, no PKCE, no client credentials for user tokens
30-day token expiry on inactivityLong-running background jobs need token refresh handling
Digest for concurrent editingEvery write must include the current digest or use force=true
JSON:API formatResponses are verbose — included array can be large for experiments with many children

MuleSoft SDK vs native API

AspectMuleSoft SDKNative Signals API
Rate limit300 calls/hour1,000 calls/min
AuthMuleSoft client credentials + certAPI key or OAuth Bearer
Endpoint coverage27 methods (subset)Full Swagger (~100+ endpoints)
AccessVia DataRiver gatewayDirect (if LEM grants proxy access)
GovernanceLEM-compliant by defaultRequires LEM approval

14. Example Workflows (from Revvity Developer Guide)

User Management

GET /users → List all users (paginated) POST /users/ → Create user (email, name, org, roles)

Users have roles (System Admin, Config Admin, Standard User, Inventory Admin) and systemGroups. User creation request body:

{ "data": { "attributes": { "country": "USA", "emailAddress": "user@roche.com", "firstName": "Jane", "lastName": "Doe", "organization": "roche", "roles": [{ "id": "3", "name": "Standard User" }] } } }

Create Experiment and Update Properties

POST /entities/ → Create experiment PUT /entities/`\1`/properties → Update properties (with digest)

Create a basic experiment:

{ "data": { "type": "experiment", "attributes": { "name": "My New Experiment" } } }

Update description (include digest from creation response as URL param):

PUT /entities/experiment%3A`\1`/properties?digest=60367023
{ "data": [{ "attributes": { "name": "Description", "value": "This is the description of our new experiment" } }] }

Sample Registration (External Action workflow)

Complete end-to-end flow using External Actions + REST API:

  1. User clicks “Register” button on a Sample in Signals (External Action)
  2. External app receives sampleId as URL parameter
  3. App calls GET /samples/\1/properties to get sample data
  4. App registers sample with external LIMS using the properties
  5. App sets digests.external to match digests.self:
    PATCH /samples/`\1`/properties/digests.external
    { "data": { "attributes": { "content": { "value": "48ac6e467af8aebe152be89895cb0a81ada157bd632809528bc07c993b4bf753" } } } }
  6. Signals tracks if the sample changes after registration — warns user if out of sync

Key concept: digests.self vs digests.external

  • digests.self — current state of the sample in Signals (auto-updated on any change)
  • digests.external — state at time of external registration (set via API)
  • If they diverge, Signals shows a warning that the sample needs re-registration

Automated Archival (Notification workflow)

Using External Notifications to auto-archive closed experiments:

  1. Notification fires on “Sign and Close” event (type: "close")
  2. Extract experiment EID from relationships.entity.data.id
  3. Start async PDF generation:
    PUT /entities/export/pdf?eid=`\1`&attachments=true
    Returns fileId
  4. Poll for completion:
    HEAD /entities/export/pdf/`\1`
    Check content-length > 0 (retry with 5s interval)
  5. Download PDF:
    GET /entities/export/pdf/`\1`
    Returns application/pdf
  6. Store in archive

Signing Compliance Check (External Action + postMessage)

Using External Actions on signing events to enforce custom compliance:

  1. External Action configured for “Sign and Close” event on experiments
  2. At signing, Signals opens external app in dialog (iFrame)
  3. App receives experiment EID, calls GET /entities/\1“ to check children
  4. App looks for required entity (e.g. an excel entity named “Safety Sheet”)
  5. Found: Show success, post closeAndContinue:
    window.parent.postMessage(['closeAndContinue', []], 'https://snb.example.com/')
  6. Not found: Show warning, post closeAndAbort:
    window.parent.postMessage(['closeAndAbort', []], 'https://snb.example.com/')
    Signing is aborted, experiment stays open
Last updated on