Annotations
Notes pinned to the analytics timeline — releases, campaigns, incidents, milestones. SITE-LEVEL for reading: everyone with site access sees all annotations. AUTHOR-ONLY for writing.
5 endpoints, all relative to https://analytics.appfor.you. Shared rules live in Conventions, Rate limits and Error codes.
List annotations
/api/v2/sites/{siteId}/annotationsscope: readEveryone with site access sees all annotations, each carrying its author’s `userId`. With no date parameters you get the full history.
- 400 is returned for an unparseable `from`/`to` or an unknown `category`.
Path parameters
| siteId | string | The Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere. |
Query parameters
| Name | Type | Description |
|---|---|---|
| from | string (ISO date) | Inclusive window start. Alias: `startDate`. |
| to | string (ISO date) | Inclusive window end. Alias: `endDate`. |
| category | string | Filter by category. |
| limit | integer | Rows to return. Default 50, minimum 1, maximum 500. |
| offset | integer | Rows to skip, for paging. Default 0. |
Example request
curl -H "Authorization: Bearer $ANALYTICS_API_KEY" \ "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/annotations?category=deployment"
Example response
{
"annotations": [
{
"annotationId": "6512ff06bcf86cd7994390f6",
"siteId": "6507f1f77bcf86cd799439011",
"userId": "6507aaa7bcf86cd799439000",
"date": "2025-03-18T00:00:00.000Z",
"text": "Shipped the new pricing page",
"color": "#3B82F6",
"category": "deployment",
"createdAt": "2025-03-18T09:00:00.000Z",
"updatedAt": "2025-03-18T09:00:00.000Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}Create an annotation
/api/v2/sites/{siteId}/annotationsscope: writeDefaults: `color` "#3B82F6" and `category` "other", matching the dashboard.
- Viewers receive 403.
Path parameters
| siteId | string | The Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| date | string (date) | Yes | Any parseable date; the point on the timeline. |
| text | string | Yes | Note body, 1–500 characters after trimming. |
| color | string | No | Display colour. Defaults to "#3B82F6". |
| category | 'deployment' | 'campaign' | 'incident' | 'milestone' | 'other' | No | Defaults to "other". |
Example request
curl -X POST "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/annotations" \
-H "Authorization: Bearer $ANALYTICS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "date": "2025-03-18", "text": "Shipped the new pricing page", "category": "deployment" }'Example response (201)
{
"annotationId": "6512ff06bcf86cd7994390f6",
"siteId": "6507f1f77bcf86cd799439011",
"userId": "6507aaa7bcf86cd799439000",
"date": "2025-03-18T00:00:00.000Z",
"text": "Shipped the new pricing page",
"color": "#3B82F6",
"category": "deployment",
"createdAt": "2025-03-21T09:00:00.000Z",
"updatedAt": "2025-03-21T09:00:00.000Z"
}Get an annotation
/api/v2/sites/{siteId}/annotations/{annotationId}scope: readSame site-level visibility rule as the list endpoint.
Path parameters
| siteId | string | The Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere. |
| annotationId | string | Identifier of the annotation. |
Example request
curl -H "Authorization: Bearer $ANALYTICS_API_KEY" \ "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/annotations/6512ff06bcf86cd7994390f6"
Example response
{
"annotationId": "6512ff06bcf86cd7994390f6",
"siteId": "6507f1f77bcf86cd799439011",
"userId": "6507aaa7bcf86cd799439000",
"date": "2025-03-18T00:00:00.000Z",
"text": "Shipped the new pricing page",
"color": "#3B82F6",
"category": "deployment",
"createdAt": "2025-03-18T09:00:00.000Z",
"updatedAt": "2025-03-18T09:00:00.000Z"
}Update an annotation
/api/v2/sites/{siteId}/annotations/{annotationId}scope: writeAUTHOR-ONLY: another user’s annotation returns 404 even for the site owner, mirroring the dashboard. At least one field is required.
- Viewers receive 403.
Path parameters
| siteId | string | The Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere. |
| annotationId | string | Identifier of the annotation. |
Request body
Every field is optional, but at least one must be supplied.
| Field | Type | Required | Description |
|---|---|---|---|
| date | string (date) | No | New timeline position. |
| text | string | No | New note body, 1–500 characters. |
| color | string | No | New display colour. |
| category | 'deployment' | 'campaign' | 'incident' | 'milestone' | 'other' | No | New category. |
Example request
curl -X PATCH "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/annotations/6512ff06bcf86cd7994390f6" \
-H "Authorization: Bearer $ANALYTICS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "text": "Shipped the new pricing page (EU rollout)" }'Example response
{
"annotationId": "6512ff06bcf86cd7994390f6",
"siteId": "6507f1f77bcf86cd799439011",
"userId": "6507aaa7bcf86cd799439000",
"date": "2025-03-18T00:00:00.000Z",
"text": "Shipped the new pricing page (EU rollout)",
"color": "#3B82F6",
"category": "deployment",
"updatedAt": "2025-03-21T09:45:00.000Z"
}Delete an annotation
/api/v2/sites/{siteId}/annotations/{annotationId}scope: adminDestructiveRequires `confirm=<annotationId>` as a query parameter or a `confirm` body field. Another user’s annotation returns 404.
Irreversible. Requires the admin scope and confirm={annotationId} — see Destructive operations.
- Viewers receive 403.
Path parameters
| siteId | string | The Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere. |
| annotationId | string | Identifier of the annotation. |
Query parameters
| Name | Type | Description |
|---|---|---|
| confirm | string | Destructive-operation confirmation. Must exactly equal the `annotationId` in the URL. May be sent as a `confirm` field in the JSON body instead. |
Request body
Optional — send `{ "confirm": "<annotationId>" }` instead of the query parameter if you prefer.
| Field | Type | Required | Description |
|---|---|---|---|
| confirm | string | No | Must equal the annotationId in the URL. |
Example request
curl -X DELETE "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/annotations/6512ff06bcf86cd7994390f6?confirm=6512ff06bcf86cd7994390f6" \ -H "Authorization: Bearer $ANALYTICS_API_KEY"
Example response
{
"deleted": true,
"annotationId": "6512ff06bcf86cd7994390f6"
}