Documentation menu

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

GET/api/v2/sites/{siteId}/annotationsscope: read

Everyone 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

siteIdstringThe 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

NameTypeDescription
fromstring (ISO date)Inclusive window start. Alias: `startDate`.
tostring (ISO date)Inclusive window end. Alias: `endDate`.
categorystringFilter by category.
limitintegerRows to return. Default 50, minimum 1, maximum 500.
offsetintegerRows 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

POST/api/v2/sites/{siteId}/annotationsscope: write

Defaults: `color` "#3B82F6" and `category` "other", matching the dashboard.

  • Viewers receive 403.

Path parameters

siteIdstringThe 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

FieldTypeRequiredDescription
datestring (date)YesAny parseable date; the point on the timeline.
textstringYesNote body, 1–500 characters after trimming.
colorstringNoDisplay colour. Defaults to "#3B82F6".
category'deployment' | 'campaign' | 'incident' | 'milestone' | 'other'NoDefaults 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

GET/api/v2/sites/{siteId}/annotations/{annotationId}scope: read

Same site-level visibility rule as the list endpoint.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
annotationIdstringIdentifier 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

PATCH/api/v2/sites/{siteId}/annotations/{annotationId}scope: write

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

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
annotationIdstringIdentifier of the annotation.

Request body

Every field is optional, but at least one must be supplied.

FieldTypeRequiredDescription
datestring (date)NoNew timeline position.
textstringNoNew note body, 1–500 characters.
colorstringNoNew display colour.
category'deployment' | 'campaign' | 'incident' | 'milestone' | 'other'NoNew 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

DELETE/api/v2/sites/{siteId}/annotations/{annotationId}scope: adminDestructive

Requires `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

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
annotationIdstringIdentifier of the annotation.

Query parameters

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

FieldTypeRequiredDescription
confirmstringNoMust 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"
}