All skills
davila7 avatar

/benchling-integration

@2fe0cfa

Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.

Use this Skill: https://skilld.dev/gh/davila7/claude-code-templates/benchling-integration

This session only. Nothing lands on disk.

referencesapi_endpoints.md

≈3.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Benchling REST API Endpoints Reference

Base URL

All API requests use the base URL format:

https://{tenant}.benchling.com/api/v2

Replace {tenant} with your Benchling tenant name.

API Versioning

Current API version: v2 (2025-10-07)

The API version is specified in the URL path. Benchling maintains backward compatibility within a major version.

Authentication

All requests require authentication via HTTP headers:

API Key (Basic Auth):

curl -X GET \
  https://your-tenant.benchling.com/api/v2/dna-sequences \
  -u "your_api_key:"

OAuth Bearer Token:

curl -X GET \
  https://your-tenant.benchling.com/api/v2/dna-sequences \
  -H "Authorization: Bearer your_access_token"

Common Headers

Authorization: Bearer {token}
Content-Type: application/json
Accept: application/json

Response Format

All responses follow a consistent JSON structure:

Single Resource:

{
  "id": "seq_abc123",
  "name": "My Sequence",
  "bases": "ATCGATCG",
  ...
}

List Response:

{
  "results": [
    {"id": "seq_1", "name": "Sequence 1"},
    {"id": "seq_2", "name": "Sequence 2"}
  ],
  "nextToken": "token_for_next_page"
}

Pagination

List endpoints support pagination:

Query Parameters:

  • pageSize: Number of items per page (default: 50, max: 100)
  • nextToken: Token from previous response for next page

Example:

curl -X GET \
  "https://your-tenant.benchling.com/api/v2/dna-sequences?pageSize=50&nextToken=abc123"

Error Responses

Format:

{
  "error": {
    "type": "NotFoundError",
    "message": "DNA sequence not found",
    "userMessage": "The requested sequence does not exist or you don't have access"
  }
}

Common Status Codes:

  • 200 OK: Success
  • 201 Created: Resource created
  • 400 Bad Request: Invalid parameters
  • 401 Unauthorized: Missing or invalid credentials
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Resource doesn't exist
  • 422 Unprocessable Entity: Validation error
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error

Core Endpoints

DNA Sequences

List DNA Sequences:

GET /api/v2/dna-sequences

Query Parameters:
- pageSize: integer (default: 50, max: 100)
- nextToken: string
- folderId: string
- schemaId: string
- name: string (filter by name)
- modifiedAt: string (ISO 8601 date)

Get DNA Sequence:

GET /api/v2/dna-sequences/{sequenceId}

Create DNA Sequence:

POST /api/v2/dna-sequences

Body:
{
  "name": "My Plasmid",
  "bases": "ATCGATCG",
  "isCircular": true,
  "folderId": "fld_abc123",
  "schemaId": "ts_abc123",
  "fields": {
    "gene_name": {"value": "GFP"},
    "resistance": {"value": "Kanamycin"}
  },
  "entityRegistryId": "src_abc123",  // optional for registration
  "namingStrategy": "NEW_IDS"        // optional for registration
}

Update DNA Sequence:

PATCH /api/v2/dna-sequences/{sequenceId}

Body:
{
  "name": "Updated Plasmid",
  "fields": {
    "gene_name": {"value": "mCherry"}
  }
}

Archive DNA Sequence:

POST /api/v2/dna-sequences:archive

Body:
{
  "dnaSequenceIds": ["seq_abc123"],
  "reason": "Deprecated construct"
}

RNA Sequences

List RNA Sequences:

GET /api/v2/rna-sequences

Get RNA Sequence:

GET /api/v2/rna-sequences/{sequenceId}

Create RNA Sequence:

POST /api/v2/rna-sequences

Body:
{
  "name": "gRNA-001",
  "bases": "AUCGAUCG",
  "folderId": "fld_abc123",
  "fields": {
    "target_gene": {"value": "TP53"}
  }
}

Update RNA Sequence:

PATCH /api/v2/rna-sequences/{sequenceId}

Archive RNA Sequence:

POST /api/v2/rna-sequences:archive

Amino Acid (Protein) Sequences

List AA Sequences:

GET /api/v2/aa-sequences

Get AA Sequence:

GET /api/v2/aa-sequences/{sequenceId}

Create AA Sequence:

POST /api/v2/aa-sequences

Body:
{
  "name": "GFP Protein",
  "aminoAcids": "MSKGEELFTGVVPILVELDGDVNGHKF",
  "folderId": "fld_abc123"
}

Custom Entities

List Custom Entities:

GET /api/v2/custom-entities

Query Parameters:
- schemaId: string (required to filter by type)
- pageSize: integer
- nextToken: string

Get Custom Entity:

GET /api/v2/custom-entities/{entityId}

Create Custom Entity:

POST /api/v2/custom-entities

Body:
{
  "name": "HEK293T-Clone5",
  "schemaId": "ts_cellline_abc",
  "folderId": "fld_abc123",
  "fields": {
    "passage_number": {"value": "15"},
    "mycoplasma_test": {"value": "Negative"}
  }
}

Update Custom Entity:

PATCH /api/v2/custom-entities/{entityId}

Body:
{
  "fields": {
    "passage_number": {"value": "16"}
  }
}

Mixtures

List Mixtures:

GET /api/v2/mixtures

Create Mixture:

POST /api/v2/mixtures

Body:
{
  "name": "LB-Amp Media",
  "folderId": "fld_abc123",
  "schemaId": "ts_mixture_abc",
  "ingredients": [
    {
      "componentEntityId": "ent_lb_base",
      "amount": {"value": "1000", "units": "mL"}
    },
    {
      "componentEntityId": "ent_ampicillin",
      "amount": {"value": "100", "units": "mg"}
    }
  ]
}

Containers

List Containers:

GET /api/v2/containers

Query Parameters:
- parentStorageId: string (filter by location/box)
- schemaId: string
- barcode: string

Get Container:

GET /api/v2/containers/{containerId}

Create Container:

POST /api/v2/containers

Body:
{
  "name": "Sample-001",
  "schemaId": "cont_schema_abc",
  "barcode": "CONT001",
  "parentStorageId": "box_abc123",
  "fields": {
    "concentration": {"value": "100 ng/μL"},
    "volume": {"value": "50 μL"}
  }
}

Update Container:

PATCH /api/v2/containers/{containerId}

Body:
{
  "fields": {
    "volume": {"value": "45 μL"}
  }
}

Transfer Container:

POST /api/v2/containers:transfer

Body:
{
  "containerIds": ["cont_abc123"],
  "destinationStorageId": "box_xyz789"
}

Check Out Container:

POST /api/v2/containers:checkout

Body:
{
  "containerIds": ["cont_abc123"],
  "comment": "Taking to bench"
}

Check In Container:

POST /api/v2/containers:checkin

Body:
{
  "containerIds": ["cont_abc123"],
  "locationId": "bench_loc_abc"
}

Boxes

List Boxes:

GET /api/v2/boxes

Query Parameters:
- parentStorageId: string
- schemaId: string

Get Box:

GET /api/v2/boxes/{boxId}

Create Box:

POST /api/v2/boxes

Body:
{
  "name": "Freezer-A-Box-01",
  "schemaId": "box_schema_abc",
  "parentStorageId": "loc_freezer_a",
  "barcode": "BOX001"
}

Locations

List Locations:

GET /api/v2/locations

Get Location:

GET /api/v2/locations/{locationId}

Create Location:

POST /api/v2/locations

Body:
{
  "name": "Freezer A - Shelf 2",
  "parentStorageId": "loc_freezer_a",
  "barcode": "LOC-A-S2"
}

Plates

List Plates:

GET /api/v2/plates

Get Plate:

GET /api/v2/plates/{plateId}

Create Plate:

POST /api/v2/plates

Body:
{
  "name": "PCR-Plate-001",
  "schemaId": "plate_schema_abc",
  "barcode": "PLATE001",
  "wells": [
    {"position": "A1", "entityId": "ent_abc"},
    {"position": "A2", "entityId": "ent_xyz"}
  ]
}

Entries (Notebook)

List Entries:

GET /api/v2/entries

Query Parameters:
- folderId: string
- schemaId: string
- modifiedAt: string

Get Entry:

GET /api/v2/entries/{entryId}

Create Entry:

POST /api/v2/entries

Body:
{
  "name": "Experiment 2025-10-20",
  "folderId": "fld_abc123",
  "schemaId": "entry_schema_abc",
  "fields": {
    "objective": {"value": "Test gene expression"},
    "date": {"value": "2025-10-20"}
  }
}

Update Entry:

PATCH /api/v2/entries/{entryId}

Body:
{
  "fields": {
    "results": {"value": "Successful expression"}
  }
}

Workflow Tasks

List Workflow Tasks:

GET /api/v2/tasks

Query Parameters:
- workflowId: string
- statusIds: string[] (comma-separated)
- assigneeId: string

Get Task:

GET /api/v2/tasks/{taskId}

Create Task:

POST /api/v2/tasks

Body:
{
  "name": "PCR Amplification",
  "workflowId": "wf_abc123",
  "assigneeId": "user_abc123",
  "schemaId": "task_schema_abc",
  "fields": {
    "template": {"value": "seq_abc123"},
    "priority": {"value": "High"}
  }
}

Update Task:

PATCH /api/v2/tasks/{taskId}

Body:
{
  "statusId": "status_complete_abc",
  "fields": {
    "completion_date": {"value": "2025-10-20"}
  }
}

Folders

List Folders:

GET /api/v2/folders

Query Parameters:
- projectId: string
- parentFolderId: string

Get Folder:

GET /api/v2/folders/{folderId}

Create Folder:

POST /api/v2/folders

Body:
{
  "name": "2025 Experiments",
  "parentFolderId": "fld_parent_abc",
  "projectId": "proj_abc123"
}

Projects

List Projects:

GET /api/v2/projects

Get Project:

GET /api/v2/projects/{projectId}

Users

Get Current User:

GET /api/v2/users/me

List Users:

GET /api/v2/users

Get User:

GET /api/v2/users/{userId}

Teams

List Teams:

GET /api/v2/teams

Get Team:

GET /api/v2/teams/{teamId}

Schemas

List Schemas:

GET /api/v2/schemas

Query Parameters:
- entityType: string (e.g., "dna_sequence", "custom_entity")

Get Schema:

GET /api/v2/schemas/{schemaId}

Registries

List Registries:

GET /api/v2/registries

Get Registry:

GET /api/v2/registries/{registryId}

Bulk Operations

Batch Archive

Archive Multiple Entities:

POST /api/v2/{entity-type}:archive

Body:
{
  "{entity}Ids": ["id1", "id2", "id3"],
  "reason": "Cleanup"
}

Batch Transfer

Transfer Multiple Containers:

POST /api/v2/containers:bulk-transfer

Body:
{
  "transfers": [
    {"containerId": "cont_1", "destinationId": "box_a"},
    {"containerId": "cont_2", "destinationId": "box_b"}
  ]
}

Async Operations

Some operations return task IDs for async processing:

Response:

{
  "taskId": "task_abc123"
}

Check Task Status:

GET /api/v2/tasks/{taskId}

Response:
{
  "id": "task_abc123",
  "status": "RUNNING", // or "SUCCEEDED", "FAILED"
  "message": "Processing...",
  "response": {...}  // Available when status is SUCCEEDED
}

Field Value Format

Custom schema fields use a specific format:

Simple Value:

{
  "field_name": {
    "value": "Field Value"
  }
}

Dropdown:

{
  "dropdown_field": {
    "value": "Option1"  // Must match exact option name
  }
}

Date:

{
  "date_field": {
    "value": "2025-10-20"  // Format: YYYY-MM-DD
  }
}

Entity Link:

{
  "entity_link_field": {
    "value": "seq_abc123"  // Entity ID
  }
}

Numeric:

{
  "numeric_field": {
    "value": "123.45"  // String representation
  }
}

Rate Limiting

Limits:

  • Default: 100 requests per 10 seconds per user/app
  • Rate limit headers included in responses:
    • X-RateLimit-Limit: Total allowed requests
    • X-RateLimit-Remaining: Remaining requests
    • X-RateLimit-Reset: Unix timestamp when limit resets

Handling 429 Responses:

{
  "error": {
    "type": "RateLimitError",
    "message": "Rate limit exceeded",
    "retryAfter": 5  // Seconds to wait
  }
}

Filtering and Searching

Common Query Parameters:

  • name: Partial name match
  • modifiedAt: ISO 8601 datetime
  • createdAt: ISO 8601 datetime
  • schemaId: Filter by schema
  • folderId: Filter by folder
  • archived: Boolean (include archived items)

Example:

curl -X GET \
  "https://tenant.benchling.com/api/v2/dna-sequences?name=plasmid&folderId=fld_abc&archived=false"

Best Practices

Request Efficiency

  1. Use appropriate page sizes:

    • Default: 50 items
    • Max: 100 items
    • Adjust based on needs
  2. Filter on server-side:

    • Use query parameters instead of client filtering
    • Reduces data transfer and processing
  3. Batch operations:

    • Use bulk endpoints when available
    • Archive/transfer multiple items in one request

Error Handling

// Example error handling
async function fetchSequence(id) {
  try {
    const response = await fetch(
      `https://tenant.benchling.com/api/v2/dna-sequences/${id}`,
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Accept': 'application/json'
        }
      }
    );

    if (!response.ok) {
      if (response.status === 429) {
        // Rate limit - retry with backoff
        const retryAfter = response.headers.get('Retry-After');
        await sleep(retryAfter * 1000);
        return fetchSequence(id);
      } else if (response.status === 404) {
        return null;  // Not found
      } else {
        throw new Error(`API error: ${response.status}`);
      }
    }

    return await response.json();
  } catch (error) {
    console.error('Request failed:', error);
    throw error;
  }
}

Pagination Loop

async function getAllSequences() {
  let allSequences = [];
  let nextToken = null;

  do {
    const url = new URL('https://tenant.benchling.com/api/v2/dna-sequences');
    if (nextToken) {
      url.searchParams.set('nextToken', nextToken);
    }
    url.searchParams.set('pageSize', '100');

    const response = await fetch(url, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Accept': 'application/json'
      }
    });

    const data = await response.json();
    allSequences = allSequences.concat(data.results);
    nextToken = data.nextToken;
  } while (nextToken);

  return allSequences;
}

References

Source: SKILL.md on GitHub

1 warning15d4 checks · Risk SAFE
  • Gen Agent Trust Hub15d

    The skill provides a standard integration for the Benchling R&D platform using its official Python SDK and REST API. It follows security best practices for credential management, emphasizing the use of environment variables and secure storage rather than hardcoding secrets. No malicious patterns or unauthorized behaviors were detected.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: LOW · No issues

  • Runlayer7mo

    4/4 files flagged

Signed by skilld at 2fe0cfa. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 14 hours ago.

Activeupdated 9 months ago

README badge

README badge for davila7/claude-code-templates/benchling-integration