> This page is for Developer Documentation.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.benchling.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.benchling.com/_mcp/server.

# Working with Schemas and Fieldsets

Every schema type has its own family of endpoints, such as `dna-sequence-schema`, `equipment-schema`, and `run-schema`. Fieldsets have one too, at `/fieldset`.

Field definitions don't have top-level endpoints. They are children of the schema or fieldset that defines them, so you read and write them through that schema's or fieldset's endpoints.

The examples on this page use the custom entity schema endpoints. Every other schema type mirrors them. Replace `/custom-entity-schema` in the path with your schema type, such as `/molecule-schema`, or with `/fieldset` to work with fieldsets.

> **Info**
>
> ### Beta endpoints
>
> Listing field definitions and working with validation rules are beta. Send those requests with the `EARLY-ACCESS: true` header. Every other endpoint on this page is stable. See the [V3 Stability Guidelines](/docs/stability-guidelines).

## Reading schemas and fieldsets

### List schemas

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/custom-entity-schema/items
```

Like other listing endpoints, this one accepts optional filtering, sorting, and pagination parameters. See [Listing endpoints](/docs/v3-api-overview#listing-endpoints).

### Get a schema

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/custom-entity-schema/{schema_id}
```

All properties are returned by default. Use the `returning` and `omit` query parameters to include or exclude properties by name.

### List field definitions

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/custom-entity-schema/{schema_id}/field-definitions/items
EARLY-ACCESS: true
```

Each field definition's data type is in its `__typename` property, for example `TextFieldDefinition`. The `derivationType` property tells normal fields (`DIRECT`) apart from `COMPUTED` and `SNAPSHOT` fields.

A schema's other children are listed the same way: its validation rules at `/validation-rules/items` (beta) and the fieldsets it implements at `/fieldsets/items`.

## Creating schemas and fieldsets

Create and update operations come in three forms:

* **Single:** one schema per request.
* **Batch:** up to 25 schemas per request, with `:batch-create` and `:batch-update`.
* **Bulk:** a file-based asynchronous task for large imports, with `:bulk-create` and `:bulk-update`. See [Importing Data with the Bulk Import API](/docs/bulk-import).

The examples below create and update single schemas. Batch and bulk requests use the same object shape.

### Create a schema

**`Endpoint`**

```text title="Endpoint"
POST /api/v3/custom-entity-schema
```

Send `name`, `organizationId`, `description`, and `itemIdPrefix`:

**`Minimal request`**

```json title="Minimal request"
{
  "name": "My schema name",
  "organizationId": "ent_abcdefg",
  "description": "My schema description",
  "itemIdPrefix": "myprefix"
}
```

> **Note**
>
> `displayName` is a deprecated alias for `name`. Use `name`; support for `displayName` will be removed.

A schema can be created with field definitions. Every field definition needs `name`, `systemName`, `type`, and `isRequired`, and some types need more. Link fields also need `isMulti` and a `linkDefinitionId`, and dropdown fields need `isMulti` and a `dropdownId`. The accepted `type` values, such as `TEXT`, `INTEGER`, and `CUSTOM_ENTITY_LINK`, are listed in the [API Reference](/docs/api).

**`Request with one text field`**

```json title="Request with one text field"
{
  "name": "My schema name",
  "organizationId": "ent_abcdefg",
  "description": "My schema description",
  "itemIdPrefix": "myprefix",
  "fieldDefinitions": [
    {
      "name": "My field name",
      "systemName": "my_field_system_name",
      "type": "TEXT",
      "isRequired": false
    }
  ]
}
```

Computed and snapshot fields can be read through the API but not written.

Schema types differ in which optional properties they accept. Three need more explanation:

* **`constraint`:** its `fieldDefinitionNames` property refers to fields in `fieldDefinitions` by their `name`.
* **`fieldsets`:** schema types that support fieldsets can be created already implementing one or more of them, referenced by ID. The fieldsets must already exist. Their field definitions are added to the schema automatically, after any fields in `fieldDefinitions`. If you list a fieldset's fields in `fieldDefinitions` too, they must match the fieldset's definitions exactly.
* **Name templates:** a schema can't be created with one. Some schema types accept `nameTemplate` on update instead.

### Create a fieldset

**`Endpoint`**

```text title="Endpoint"
POST /api/v3/fieldset
```

Fieldset creation works like schema creation, with a slightly different shape: send `displayName`, `organizationId`, and `itemIdPrefix`. `fieldDefinitions` works the same way as on schemas.

## Updating schemas and fieldsets

### Update a schema

**`Endpoint`**

```text title="Endpoint"
PATCH /api/v3/custom-entity-schema/{schema_id}
```

Updates are partial. Only the schema ID in the path is required, and properties you leave out are unchanged.

This endpoint only changes properties that can't alter data in the objects that use the schema. Field definitions and fieldsets aren't among them. Change those with [Set field definitions](#set-field-definitions) and [Apply or remove a fieldset](#apply-or-remove-a-fieldset).

**`Rename and redescribe a schema`**

```json title="Rename and redescribe a schema"
{
  "name": "New name",
  "description": "New description"
}
```

The same endpoint archives a schema, using `archived` and `archiveReason`:

**`Archive a schema`**

```json title="Archive a schema"
{
  "name": "[DEPRECATED] name",
  "description": "Do not use this schema anymore!",
  "archived": true,
  "archiveReason": "Retired"
}
```

**`Unarchive a schema`**

```json title="Unarchive a schema"
{
  "archived": false
}
```

Fieldsets are updated the same way, with `PATCH /api/v3/fieldset/{fieldset_id}`.

### Set field definitions

**`Endpoint`**

```text title="Endpoint"
POST /api/v3/custom-entity-schema/{schema_id}:set-field-definitions
```

Changing a schema's field definitions can change data in the objects that use it, so this endpoint runs as a task. It returns `202` with a `taskId` and a `pollingUri`. Poll the task with:

**`Poll the task`**

```text title="Poll the task"
GET /api/v3/tasks/custom-entity-schema/set-field-definitions/{task_id}
```

The request replaces the schema's field definitions with the list you send:

* **Include every existing field.** Schema field definitions can't be deleted, so the request fails if an existing field is missing.
* **Mix new and existing fields.** A field with an `id` updates that field. A field without one is created.
* **Order matters.** The order of the list becomes the order of fields on the schema.
* **Send whole fields.** Partial updates aren't supported, so every field needs all of its required properties, even if they don't change.
* **Archive in place.** To archive or unarchive a field, set `archived` and `archiveReason` on it.

Changing a field's data type can change existing data in objects that use the schema.

Because the request has to include every existing field, the usual pattern is to [list the field definitions](#list-field-definitions), edit that list, and send it back. The list response doesn't match the request shape exactly, so convert each field first:

1. **Drop read-only properties:** `createdAt`, `modifiedAt`, and `derivationType`. Optional properties the API can update, such as `description`, `isParent`, and `archived`, can stay.
2. **Replace `__typename` with `type`.** For example, `CustomEntityLinkFieldDefinition` becomes `CUSTOM_ENTITY_LINK`.
3. **Flatten `linkDefinition`.** Replace the `linkDefinition` object with `linkDefinitionId`, set to its `id`.

For example, this is the list response for a schema with one link field:

**`List field definitions response`**

```json title="List field definitions response"
{
  "items": [
    {
      "name": "Link Field",
      "id": "tsf_abcde",
      "archived": false,
      "archiveReason": null,
      "createdAt": "2026-09-25T22:11:42.087839Z",
      "modifiedAt": "2026-09-25T22:11:42.087839Z",
      "systemName": "link_field",
      "isRequired": false,
      "derivationType": "DIRECT",
      "description": null,
      "isMulti": false,
      "isParent": false,
      "linkDefinition": {
        "id": "ts_vwxyz",
        "__typename": "CustomEntitySchema"
      },
      "__typename": "CustomEntityLinkFieldDefinition"
    }
  ]
}
```

This request renames that field and adds a text field after it:

**`Set field definitions request`**

```json title="Set field definitions request"
{
  "fieldDefinitions": [
    {
      "name": "New name",
      "id": "tsf_abcde",
      "systemName": "link_field",
      "isRequired": false,
      "isMulti": false,
      "linkDefinitionId": "ts_vwxyz",
      "type": "CUSTOM_ENTITY_LINK"
    },
    {
      "name": "New Text Field",
      "systemName": "new_text_field",
      "isRequired": false,
      "type": "TEXT"
    }
  ]
}
```

The existing field keeps its `id`. The new field has none.

#### On a fieldset

**`Endpoints`**

```text title="Endpoints"
POST /api/v3/fieldset/{fieldset_id}:set-field-definitions
GET  /api/v3/tasks/fieldset/set-field-definitions/{task_id}
```

Setting a fieldset's field definitions works the same way, with three differences:

* **Changes propagate.** Updating a fieldset field updates it on every schema that implements the fieldset, which can change entity data on all of them.
* **Adding a field needs a match.** A field can be added to a fieldset only if every schema that implements the fieldset already has an identical field. Those schema fields then become controlled by the fieldset.
* **No archiving.** Fieldset fields can't be archived or unarchived. Delete them instead; see [Delete a fieldset field](#delete-a-fieldset-field).

### Apply or remove a fieldset

Schema types that support fieldsets can change which fieldsets they implement.

#### Apply a fieldset

**`Endpoints`**

```text title="Endpoints"
POST /api/v3/custom-entity-schema/{schema_id}:apply-fieldset
GET  /api/v3/tasks/custom-entity-schema/apply-fieldset/{task_id}
```

Applying a fieldset can change existing field definitions, and with them entity data, so it runs as a task, like setting field definitions. It returns a task ID to poll.

The fieldset's properties are applied to the schema where present, such as its prefix and name template, and its field definitions are added. If the schema already has a field with the same name as a fieldset field, the schema's field is changed to match the fieldset's.

To avoid surprises, preview those changes first. Send `acknowledgeWarnings` along with the `fieldsetId`; it defaults to `false`:

**`Apply a fieldset`**

```json title="Apply a fieldset"
{
  "fieldsetId": "fs_abcde",
  "acknowledgeWarnings": false
}
```

* If `acknowledgeWarnings` is `false` and applying the fieldset would change existing fields, the response lists those changes as warnings and the schema is not updated.
* If there are no warnings, or `acknowledgeWarnings` is `true`, the schema is updated.

#### Remove a fieldset

**`Endpoint`**

```text title="Endpoint"
POST /api/v3/custom-entity-schema/{schema_id}:remove-fieldset
```

Removing a fieldset is synchronous, and it never changes entity data:

* **Field definitions stay on the schema**, unlinked from the fieldset and no longer controlled by it. The shape of the schema's fields doesn't change, which is why no entity data can.
* **Name template and labeling strategy stay** as the fieldset set them.
* **The fieldset's constraint is removed** from the schema.
* **A replacement prefix is needed** if the fieldset supplied the schema's prefix. Send it in `schemaPrefixReplacement`:

**`Remove a fieldset that supplied the prefix`**

```json title="Remove a fieldset that supplied the prefix"
{
  "fieldsetId": "fs_abcde",
  "schemaPrefixReplacement": "newprefix"
}
```

### Delete a fieldset field

**`Endpoint`**

```text title="Endpoint"
POST /api/v3/fieldset/{fieldset_id}:delete-field
```

Fieldset fields can't be archived, but unlike schema fields they can be deleted. Send the ID of the field to delete:

**`Delete a fieldset field`**

```json title="Delete a fieldset field"
{
  "fieldId": "tsf_abcde"
}
```

Deleting is synchronous and doesn't change schema data. Schemas that implement the fieldset keep the field, unlinked from the fieldset, just as when a fieldset is removed from a schema.