Research: Signals Notebook REST API (Revvity)
| Field | Value |
|---|---|
| Type | Research |
| Status | Active |
| Author | xRED Dev Team |
| Created | 2026-04-08 |
| Source | Revvity Signals Developer Guide — REST API |
| Related SAD | SAD-001 |
| Related ADR | ADR-005, ADR-009 |
| Related Research | Signals 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-keyHTTP 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:
- Request a Client ID from Revvity (via CSM, Account Manager, or Signals support), providing: application name and redirect URI(s)
- Your app redirects user to
/auth/oauth/authorizewithresponse_type,client_id,scopequery params (values provided by Revvity) - User authenticates via their IdP
- User authorizes your application
- Token returned via redirect URI fragment
- 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-053c2ba2e439text:18849188-9d5a-4064-a287-e4fe402028f8chemicalDrawing:865d61d8-16c0-41c5-adb1-9d2de7999a43samplesContainer: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 drawingssamplesContainer— sample collectionsmaterialsTable— materials tablesworksheet— worksheetsviewonly— attached documentsplateContainer— 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
- Entity is created/modified → server generates a
digest - Client reads entity, receives current
digest - Client sends modification with the
digestas a URL parameter - Server compares submitted
digestwith current state - Match → changes accepted
- Mismatch →
428error 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:
| Code | Meaning |
|---|---|
401 | Missing or invalid x-api-key / Bearer token |
403 | Access forbidden (e.g. entity is trashed) |
428 | Digest 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 optionalmode: "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:
| Category | Endpoint | Method | Description |
|---|---|---|---|
| Entities | /entities/\1“ | GET | Fetch any entity by EID |
/entities/\1/children | GET | List child entities | |
/entities/search | POST | Search entities | |
| Experiments | /entities (type filter) | GET | List experiments |
/entities | POST | Create experiment | |
/entities/\1“ | PATCH | Update experiment | |
| Tables | /entities/\1“ | GET | Get table entity |
/entities/\1/rows | GET | Get table rows | |
/entities/\1/rows | POST | Add row | |
/entities/\1/rows/\1“ | PATCH | Update row | |
/entities/\1/rows/\1“ | DELETE | Delete row | |
| Files | /entities/\1/attachments | GET | List attachments |
/entities/\1/attachments | POST | Upload file | |
/entities/\1/attachments/\1“ | GET | Download file | |
| Users | /users | GET | List users (SCIM 2.0) |
/users/\1“ | GET | Get user | |
| Groups | /groups | GET | List groups (SCIM 2.0) |
| Templates | /templates | GET | List 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
- Admin configures an External Action for an entity type (experiment, sample, table, etc.)
- User clicks the action button in the Signals UI
- Signals redirects to the configured URL, passing entity context
- GET: Entity ID passed as URL parameter (default
__eid):https://yourapplication.com/?__eid=experiment:03ae2d17-... - 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:
- Authenticate the user independently (credentials, VPN, intranet, SSO)
- Initiate Bearer Token Exchange with Signals to get an API token
- 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():
| Command | Effect |
|---|---|
closeAndContinue | Close dialog, continue signing flow |
closeAndAbort | Close dialog, abort signing flow |
closeAndRefresh | Close dialog, refresh the entity |
close | Close the dialog |
resize | Resize the dialog |
setTitle | Set 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
| Entity | Extra Options |
|---|---|
| Experiments | Signing event triggers |
| Tables/Worksheets | Apply to all templates or specific ones |
| Samples | Filter by sample status |
| Folders | Must 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 totrueon2xxresponse (if enabled)isRead,isFlagged— manageable via APIcomment— signing comment (for signing events)relationships.entity— the entity that triggered the eventrelationships.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
| Trigger | Notification type values |
|---|---|
| Top Level Entity Creation | create |
| All Export to PDF/ZIP | print |
| Signing Events | close, 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
Recommended pattern
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:
| Method | When | Example |
|---|---|---|
GET /\1“ | User enters a key in a table cell | GET /flasks/88 → returns {id, name, description} |
POST / | User creates a new row | POST /flasks/ with {name, description} |
PUT / | User updates an existing row | PUT /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
| Constraint | Impact |
|---|---|
| 1,000 req/min tenant-wide | All consumers share this — caching is critical (ADR-009) |
| Max 10 API keys per tenant | API keys are scarce — prefer OAuth Bearer for user-context operations |
| Implicit grant only | No authorization code flow, no PKCE, no client credentials for user tokens |
| 30-day token expiry on inactivity | Long-running background jobs need token refresh handling |
| Digest for concurrent editing | Every write must include the current digest or use force=true |
| JSON:API format | Responses are verbose — included array can be large for experiments with many children |
MuleSoft SDK vs native API
| Aspect | MuleSoft SDK | Native Signals API |
|---|---|---|
| Rate limit | 300 calls/hour | 1,000 calls/min |
| Auth | MuleSoft client credentials + cert | API key or OAuth Bearer |
| Endpoint coverage | 27 methods (subset) | Full Swagger (~100+ endpoints) |
| Access | Via DataRiver gateway | Direct (if LEM grants proxy access) |
| Governance | LEM-compliant by default | Requires 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:
- User clicks “Register” button on a Sample in Signals (External Action)
- External app receives
sampleIdas URL parameter - App calls
GET /samples/\1/propertiesto get sample data - App registers sample with external LIMS using the properties
- App sets
digests.externalto matchdigests.self:PATCH /samples/`\1`/properties/digests.external{ "data": { "attributes": { "content": { "value": "48ac6e467af8aebe152be89895cb0a81ada157bd632809528bc07c993b4bf753" } } } } - 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:
- Notification fires on “Sign and Close” event (
type: "close") - Extract experiment EID from
relationships.entity.data.id - Start async PDF generation:
Returns
PUT /entities/export/pdf?eid=`\1`&attachments=truefileId - Poll for completion:
Check
HEAD /entities/export/pdf/`\1`content-length > 0(retry with 5s interval) - Download PDF:
Returns
GET /entities/export/pdf/`\1`application/pdf - Store in archive
Signing Compliance Check (External Action + postMessage)
Using External Actions on signing events to enforce custom compliance:
- External Action configured for “Sign and Close” event on experiments
- At signing, Signals opens external app in dialog (iFrame)
- App receives experiment EID, calls
GET /entities/\1“ to check children - App looks for required entity (e.g. an
excelentity named “Safety Sheet”) - Found: Show success, post
closeAndContinue:window.parent.postMessage(['closeAndContinue', []], 'https://snb.example.com/') - Not found: Show warning, post
closeAndAbort:Signing is aborted, experiment stays openwindow.parent.postMessage(['closeAndAbort', []], 'https://snb.example.com/')