> 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.

# Introduction to App Canvas

## What is app canvas?

App canvas lets apps define custom user interfaces within Benchling. Interactive Benchling
Apps can use canvas "blocks" to build custom UI in the context of a scientist's workflow,
letting users interact directly with the app. Canvas UIs update in real time, so there's no
waiting and refreshing to get feedback from an app.

App canvases can appear in a few different places: in documents such as notebook entries,
in runs, and on an app's homepage.

## Canvas examples

App canvases are most commonly used to communicate with an external system: registering an
entity in a registry outside Benchling, for example, or importing data from an external
database.

![An app canvas in a notebook entry providing a form for registering an entity in an external registry](/_fern-files/_img/933aa657bae614cb753536cfbdd838034bf581998ca0fc2a665e722d5f55cb01.webp)![An app canvas providing a PubChem compound lookup interface](/_fern-files/_img/9836b602d4b479a27597090fc94a3ff7a926b5adbd79df3d869ddd21231f1e74.webp)

App canvases can also be combined with any Benchling API action. A multi-step notebook
template with conditional logic, for instance, can become a canvas for a more streamlined
experience.

Canvases can also bring external steps in a scientific workflow into Benchling. Some
examples:

* Validate inputs with custom logic before registering an entity in Benchling
* Clean up and validate data before sending instructions to an instrument
* Generate a report after an experiment completes

## How to design interactive apps with canvas

A prototypical interactive app has three key design components.

**An app manifest with features defined.** While a manifest isn't required for all
Benchling Apps, it is required for interactive apps that use a canvas. An interactive app
defines some number of "features," each determining where the app provides custom UI.
Distinct features typically serve distinct purposes, and an app can define as many or as
few as appropriate.

**A public webhook endpoint.** Webhooks are a key component of interactive apps, since canvases
trigger webhooks on button clicks. Apps using canvases must therefore be able to receive
webhooks from Benchling; see
[Getting Started with Webhooks](/docs/getting-started-with-webhooks).

**Application logic for interacting with the canvas.** Interactive apps define their UI
using canvas blocks and use the Canvas API to update canvas contents, which Benchling
renders in real time. Apps must be able to respond to canvas webhooks by querying and
updating the canvases in question to produce the desired experience.

## How to use app canvas

> **Info**
>
> ### Benchling App Python examples
>
> We recommend cloning the `chem-sync-local-flask` project from our public
> [app examples repo](https://github.com/benchling/app-examples-python) to get started
> building an app canvas app.

To use app canvas, your app must be created from an
[App Manifest](/docs/app-manifest-reference). An interactive app must define at least one
feature. Generic canvases can either be rendered anywhere canvas is available, or be
limited to specific locations.

Two special cases of app canvases can only be rendered in their designated location:

* The `ASSAY_RUN` feature type introduces a canvas to an interactive run.
* The `APP_HOMEPAGE` feature type introduces a canvas to the app's homepage.

An app's features as defined in the manifest might look like this:

**`App Manifest`**

```yaml title="App Manifest"
features:
  - name: Generic Canvas
    id: test_generic_canvas
    type: CANVAS
    locations:
      - ENTRY
      - ENTRY_TEMPLATE
  - name: Interactive Run Canvas
    id: test_interactive_run_feature
    type: ASSAY_RUN
  - name: App Homepage canvas
    id: test_app_homepage_feature
    type: APP_HOMEPAGE
```

The following fields are required:

* `name`: the user-facing name of the feature
* `id`: the app-defined unique identifier for the feature
* `type`: which feature is available

The following field is optional:

* `locations`: where a generic canvas should be available. Only supported when the feature
  type is `CANVAS`.

> **Info**
>
> An app manifest may define any number of `CANVAS` or `ASSAY_RUN` type features, but *at
> most one* `APP_HOMEPAGE` type feature. There is only one place an `APP_HOMEPAGE` canvas
> can be rendered, the app's own workspace page, so multiple `APP_HOMEPAGE` features
> would imply multiple canvases on that page, which we do not currently support.

### Required subscriptions

To receive webhook messages sent through a canvas's lifecycle, the app manifest must also
declare a subscription to those messages.

If the manifest declares at least one `ASSAY_RUN` feature type, it must include these
subscriptions:

**`App Manifest`**

```yaml title="App Manifest"
subscriptions:
  deliveryMethod: WEBHOOK
  messages:
    - type: v2.canvas.userInteracted
    - type: v2.canvas.initialized
```

If the manifest declares at least one `CANVAS` feature type, it must include these:

**`App Manifest`**

```yaml title="App Manifest"
subscriptions:
  deliveryMethod: WEBHOOK
  messages:
    - type: v2.canvas.userInteracted
    - type: v2.canvas.created
```

A manifest containing both `ASSAY_RUN` and `CANVAS` features must subscribe to
`v2.canvas.initialized`, `v2.canvas.userInteracted`, and `v2.canvas.created`.

### Canvas creation and initialization

Installed apps with a `CANVAS` feature appear in the canvas picker wherever the canvas is
made available.

![The Notebook canvas picker listing installed apps that provide canvases](/_fern-files/_img/b5b39541a8bc6370317c4df202166a97575aa94e85af599b6991fb5ff139884c.webp)

On selection, a canvas container loads into the notebook. When the user clicks "Create",
Benchling sends a `v2.canvas.created` webhook to the app's webhook URL.

With an `ASSAY_RUN` feature defined, admins can select the run schemas where the canvas
should appear. This option is in the app's configuration interface; for interactive runs,
the user can select any number of run schemas where the UI should render:

![An app configuration page with a selector for run schemas where the canvas will appear](/_fern-files/_img/0c4855f487414c4a1e06188d831186e783ce731c09bb52e7af31a4976415467d.webp)

For assay run canvases, a `v2.canvas.initialized` webhook fires when an instance of a
relevant run schema is created in a notebook entry.

App homepage canvases live on the General tab of the app in the App Workspace:

![An app's General tab in the App Workspace showing a homepage canvas](/_fern-files/_img/c7d5046298ddf4e1acf7127a346e385f24204c5ba9751c7b5f11c911ea3048de.webp)

App homepage canvases do not currently trigger a webhook on initialization. We recommend
subscribing to the `v2.app.activateRequested` event and creating the canvas when that
webhook arrives.

After receiving a canvas initialization webhook, an app must create an initial canvas using
the
[Create an App Canvas endpoint](/docs/api/v2/benchling-api/apps/create-app-canvas).
A canvas initialization webhook looks like this:

```json
{
  "version": "0",
  "baseURL": "https://subdomain.bnchdev.org",
  "tenantId": "ten_XXXXXXXXXX",
  "app": {
    "id": "app_XXXXXXXXXXXXXXXX"
  },
  "appDefinition": {
    "id": "appdef_XXXXXXXXXX",
    "versionNumber": "0.0.1"
  },
  "channel": "app_signals",
  "message": {
    "deprecated": false,
    "featureId": "test_interactive_run_feature",
    "resourceId": "51a09c87-72f1-4f44-a34c-8b29ca39cf30",
    "type": "v2.canvas.initialized",
    "userId": "ent_XXXXXXX"
  }
}
```

Importantly, the initialization webhook includes a `featureId` corresponding to the feature
`id` defined in the app manifest.

Assay run and app homepage canvases also include a `resourceId` indicating the public API
identifier of the Benchling resource the interaction is happening on. Both properties are
required when the app `POST`s an initial canvas.

In the example above, the `ASSAY_RUN` feature with ID `test_interactive_run_feature` from
our earlier manifest was triggered, so the `resourceId` in the payload is an assay run ID.
Had the `APP_HOMEPAGE` feature been triggered instead, `resourceId` would be the app's own
API identifier.

> **Info**
>
> ### Acknowledge webhooks quickly
>
> An app must acknowledge a canvas webhook with a response within 3 seconds to prevent the
> UI from timing out, though the app can take longer to `POST` or `PATCH` a canvas. We
> recommend queuing the webhook body for asynchronous processing downstream and responding
> to the webhook immediately. Canvas blocks are processed and rendered in the UI in real
> time, so users don't need to wait and refresh to see updates.

### Interaction webhooks

Users interacting with app canvases also trigger webhooks. Here's an example triggered by a
user clicking a button in a canvas:

```json
{
  "version": "0",
  "baseURL": "https://subdomain.bnchdev.org",
  "tenantId": "ten_XXXXXXXXXX",
  "app": {
    "id": "app_XXXXXXXXXXXXXXXX"
  },
  "appDefinition": {
    "id": "appdef_XXXXXXXXXX",
    "versionNumber": "0.0.1"
  },
  "channel": "app_signals",
  "message": {
    "buttonId": "my_button_id",
    "canvasId": "cnvs_XXXXXXXX",
    "deprecated": false,
    "featureId": "test_interactive_run_feature",
    "type": "v2.canvas.userInteracted",
    "userId": "ent_XXXXXXX"
  }
}
```

Interaction webhooks let the app respond to user input and update the canvas in real time.
Updating an existing canvas means using the
[Update App Canvas](/docs/api/v2/benchling-api/apps/update-app-canvas) endpoint to
PATCH the canvas blocks that make up the UI. The
[App Canvas Block Reference](/docs/app-canvas-block-reference) lists the available blocks.

Updating an app canvas **replaces the existing canvas elements with the content provided in
the request body**. To append blocks to an existing canvas, the update must include both
the new blocks and all existing ones. Here's an example that updates the earlier canvas to
remove its input elements:

```http
PATCH /api/v2/app-canvases/:canvas_id
{
    "blocks": [
        {
            "enabled": true,
            "id": "submit",
            "text": "Click me to submit",
            "type": "BUTTON"
        }
    ],
    "enabled": true
}
```

![An app canvas showing only a submit button after the input elements were removed](/_fern-files/_img/d341f27befa30db112a0aeb3e653c871018b6accf81286ab9d32723271efe6f0.webp)

## App permissions for creating or updating canvases

After creation, apps always have access to instances of their own canvases if the feature
type is `CANVAS`. No special permissions are required.

For an app to create and update `ASSAY_RUN` canvases, it needs read access to the run
schema the canvas is associated with.

Apps are always permitted to read themselves, so app homepage canvases require no
additional permissions.