# How to Create and Manage Annotation Layers for Contextual Data Visualization in Apache Superset

> Learn how to create and manage annotation layers in Apache Superset. Overlay contextual events onto charts for enhanced data interpretation and create impactful visualizations.

- Repository: [The Apache Software Foundation/superset](https://github.com/apache/superset)
- Tags: how-to-guide
- Published: 2026-03-03

---

**Annotation layers in Apache Superset allow you to overlay time-bound contextual events—such as product releases, marketing campaigns, or system outages—directly onto charts and dashboards to provide meaningful background for data interpretation.**

Creating and managing annotation layers for contextual data visualization involves working with Superset's backend data model, REST API endpoints, and the React-based Explore view controls. The implementation spans SQLAlchemy models for data persistence, Flask-AppBuilder APIs for CRUD operations, and a dedicated frontend component that integrates annotation queries into the chart rendering pipeline.

## Understanding the Annotation Layer Data Model

Superset stores annotation data using two primary entities defined in [`superset/models/annotations.py`](https://github.com/apache/superset/blob/main/superset/models/annotations.py).

### The AnnotationLayer and Annotation Entities

The `AnnotationLayer` class acts as a logical namespace that groups related annotations, while the `Annotation` class represents individual time-range entries that belong to a layer.

```python
class AnnotationLayer(Model, AuditMixinNullable):
    __tablename__ = "annotation_layer"
    id = Column(Integer, primary_key=True)
    name = Column(String(250))
    descr = Column(Text)


class Annotation(Model, AuditMixinNullable):
    __tablename__ = "annotation"
    id = Column(Integer, primary_key=True)
    start_dttm = Column(DateTime)
    end_dttm = Column(DateTime)
    layer_id = Column(Integer, ForeignKey("annotation_layer.id"), nullable=False)
    short_descr = Column(String(500))
    long_descr = Column(Text)
    layer = relationship(AnnotationLayer, backref="annotation")

```

Both models inherit from `AuditMixinNullable`, which automatically captures `created_on`, `changed_on`, and user references for audit purposes.

## Backend Architecture and Data Access

Business logic validation and data access are abstracted through dedicated DAOs to ensure data integrity when you create and manage annotation layers.

### Data Access Objects (DAOs) for Validation

The [`superset/daos/annotation_layer.py`](https://github.com/apache/superset/blob/main/superset/daos/annotation_layer.py) file contains `AnnotationLayerDAO` and `AnnotationDAO` classes that enforce uniqueness constraints before persistence.

- `AnnotationLayerDAO.validate_update_uniqueness()` ensures layer names remain unique across the system during create and update operations.
- `AnnotationDAO.validate_update_uniqueness()` enforces unique short descriptions within a specific layer.

These validation methods are invoked by command objects (such as `CreateAnnotationLayerCommand`) before committing transactions to the database.

## REST API Endpoints for Managing Annotation Layers

Superset exposes comprehensive CRUD operations through Flask-AppBuilder REST endpoints defined in [`superset/annotation_layers/api.py`](https://github.com/apache/superset/blob/main/superset/annotation_layers/api.py).

| Endpoint | HTTP Method | Purpose |
|----------|-------------|---------|
| `/api/v1/annotation_layer/` | **POST** | Creates a new layer via `CreateAnnotationLayerCommand`. |
| `/api/v1/annotation_layer/<int:pk>` | **PUT** | Updates an existing layer via `UpdateAnnotationLayerCommand`. |
| `/api/v1/annotation_layer/<int:pk>` | **DELETE** | Deletes a single layer via `DeleteAnnotationLayerCommand`. |
| `/api/v1/annotation_layer/` | **GET** | Lists all layers with support for filtering, ordering, and pagination. |
| `/api/v1/annotation_layer/bulk_delete` | **DELETE** | Removes multiple layers simultaneously. |

### API Schema Validation

Request and response payloads are validated using Marshmallow schemas in [`superset/annotation_layers/schemas.py`](https://github.com/apache/superset/blob/main/superset/annotation_layers/schemas.py):

```python
class AnnotationLayerPostSchema(Schema):
    name = fields.String(required=True, validate=[Length(1, 250)])
    descr = fields.String(allow_none=True)

class AnnotationLayerPutSchema(Schema):
    name = fields.String(required=False, validate=[Length(1, 250)])
    descr = fields.String(required=False)

```

Permissions are enforced through `class_permission_name = "Annotation"` and `method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP`, ensuring only authorized users can modify annotation layers.

### Creating and Managing Layers via API

You can create and manage annotation layers for contextual data visualization programmatically using standard HTTP requests.

**Creating a new layer:**

```bash
curl -X POST "http://localhost:8088/api/v1/annotation_layer/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <YOUR_JWT>" \
  -d '{
        "name": "Release Calendar",
        "descr": "Key release dates for the product"
      }'

```

**Successful response:**

```json
{
  "id": 7,
  "result": {
    "name": "Release Calendar",
    "descr": "Key release dates for the product"
  }
}

```

**Updating an existing layer:**

```bash
curl -X PUT "http://localhost:8088/api/v1/annotation_layer/7" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <YOUR_JWT>" \
  -d '{"descr":"Updated description for release tracking"}'

```

**Deleting layers:**

```bash

# Single layer deletion

curl -X DELETE "http://localhost:8088/api/v1/annotation_layer/7" \
  -H "Authorization: Bearer <YOUR_JWT>"

# Bulk deletion (ids 8, 9, and 10)

curl -X DELETE "http://localhost:8088/api/v1/annotation_layer/bulk_delete?q=%5B8%2C9%2C10%5D" \
  -H "Authorization: Bearer <YOUR_JWT>"

```

## Frontend Implementation in the Explore View

The user interface for managing annotation layers is implemented as a React control integrated into Superset's chart exploration workflow.

### The AnnotationLayerControl Component

Located at [`superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx`](https://github.com/apache/superset/blob/main/superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx), the `AnnotationLayerControl` manages the list of active annotation layers for a chart. It renders existing layers and provides interfaces to add, edit, or remove entries.

Key interface definition:

```tsx
export interface Annotation {
  name: string;
  show?: boolean;
  annotation: string;
  timeout: Date;
  key: string;
  formData: QueryFormData | null;
  isDashboardRequest?: boolean;
  force?: boolean;
}

```

### Adding and Editing Layers via the UI

When users add a new layer, the control opens a pop-over interface defined in [`superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx`](https://github.com/apache/superset/blob/main/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx). This form captures:

- **Layer name** and **description**
- **Time range** (start and end datetimes)
- Visualization configuration

Upon saving, the `refreshAnnotationData` callback dispatches `runAnnotationQuery` to fetch annotation values from the backend.

### Querying Annotation Data

The `runAnnotationQuery` function, defined in [`superset-frontend/src/components/Chart/chartAction.ts`](https://github.com/apache/superset/blob/main/superset-frontend/src/components/Chart/chartAction.ts), constructs and executes queries against the `Annotation` table. It retrieves time-range data that matches the chart's current time filter, enabling the rendering of visual overlays such as vertical bands or markers on time-series charts.

Integration example:

```tsx
<AnnotationLayerControl
  name="annotation_layers"
  value={currentLayers}
  onChange={updated => setFormData({ ...formData, annotation_layers: updated })}
  refreshAnnotationData={payload => dispatch(runAnnotationQuery(payload))}
  colorScheme={theme.colors.primary}
  vizType={formData.viz_type}
/>

```

The control connects to Redux via `mapStateToProps` and `mapDispatchToProps`, accessing theme settings, current chart state, and error messages.

## End-to-End Workflow Example

To create and manage annotation layers for contextual data visualization in a production scenario:

1. **Create the layer namespace** – Use the Explore UI or POST to `/api/v1/annotation_layer/` to establish a logical group (e.g., "Marketing Campaigns").

2. **Populate with annotations** – Through the UI pop-over or API, add time-range entries specifying `start_dttm`, `end_dttm`, and descriptions (e.g., "Holiday Sale: Nov 24-27").

3. **Attach to charts** – In the Explore view, add the layer via `AnnotationLayerControl`. The component calls `runAnnotationQuery` to fetch relevant annotations based on the chart's time range.

4. **Manage lifecycle** – Update descriptions via PUT requests, or delete layers (checking for existing annotations first to avoid 422 errors) via DELETE or `bulk_delete` endpoints.

## Summary

- **Annotation layers** provide logical namespaces (`AnnotationLayer`) for grouping time-bound contextual events (`Annotation`), defined in [`superset/models/annotations.py`](https://github.com/apache/superset/blob/main/superset/models/annotations.py).
- **Backend validation** occurs through `AnnotationLayerDAO` and `AnnotationDAO` in [`superset/daos/annotation_layer.py`](https://github.com/apache/superset/blob/main/superset/daos/annotation_layer.py), enforcing unique names and descriptions.
- **REST API endpoints** in [`superset/annotation_layers/api.py`](https://github.com/apache/superset/blob/main/superset/annotation_layers/api.py) provide full CRUD operations, including bulk deletion, with Marshmallow schema validation in [`superset/annotation_layers/schemas.py`](https://github.com/apache/superset/blob/main/superset/annotation_layers/schemas.py).
- **Frontend integration** centers on `AnnotationLayerControl` in [`superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx`](https://github.com/apache/superset/blob/main/superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx), which manages UI state and dispatches `runAnnotationQuery` from [`superset-frontend/src/components/Chart/chartAction.ts`](https://github.com/apache/superset/blob/main/superset-frontend/src/components/Chart/chartAction.ts) to retrieve overlay data.

## Frequently Asked Questions

### What is the difference between an annotation layer and an annotation?

An **annotation layer** is a logical container or namespace—such as "Product Releases" or "Marketing Campaigns"—that groups related contextual events together. An **annotation** is a specific time-range entry within that layer, containing `start_dttm`, `end_dttm`, and description fields that render as visual overlays on charts.

### Can I delete an annotation layer that contains existing annotations?

No, the backend prevents deletion of layers that still host annotations to maintain referential integrity. The `DeleteAnnotationLayerCommand` checks for existing annotations via DAO validation methods, returning a 422 error if dependencies are detected. You must first delete or reassign all annotations within the layer before the system permits layer deletion.

### How do I apply annotation layers to specific charts?

In the Superset Explore view, locate the **Annotation Layers** control in the chart configuration panel. The `AnnotationLayerControl` component allows you to select existing layers, which triggers `runAnnotationQuery` to fetch time-range data. The annotations then render as overlays—typically vertical bands, markers, or highlighted regions—on time-series charts based on the layer's defined time ranges.

### What permissions are required to manage annotation layers?

Management requires permissions associated with the `Annotation` class, as defined in [`superset/annotation_layers/api.py`](https://github.com/apache/superset/blob/main/superset/annotation_layers/api.py) via `class_permission_name = "Annotation"`. Standard REST permissions (`can_read`, `can_write`, `can_delete`) apply to the endpoints. Users must have appropriate role-based access control (RBAC) permissions granted through Superset's security model to create, update, or delete layers via either the UI or REST API.