Skip to main content

Notes

Create a note

Annotate the chart from a deploy script, a campaign tool, or by hand.

POST/sites/{siteId}/notes
Create a note
curl --request POST \
  --url 'https://truestat.io/api/v1/sites/{siteId}/notes' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "body": "<string>",
  "granularity": "date",
  "occurred_at": "2026-10-15T16:11:51.016Z"
}'
{
  "data": {
    "id": "02eae3c2-3790-4edd-a2ff-577eb2a8b0ba",
    "body": "Deployed v2 — new pricing page",
    "author_id": "734c7de3-58f1-46e6-83d1-2be85466ee70",
    "created_at": "2026-09-16T10:02:11.000Z",
    "granularity": "minute",
    "occurred_at": "2026-09-16T10:00:00+00:00"
  },
  "meta": {
    "site_id": "6fed8980-a119-4a9c-a4d4-53e448ef9f9e"
  }
}

The call a release script makes at the end of a deploy.

granularity is a real choice

Use it forWhat it does
minuteA deploy, an incident, a switch thrownPins the note to the instant; moves with the clock like every other timestamp
dateA campaign launch, a price change, a press mentionPins it to the calendar day, and it stays there even if the site's timezone changes later

date is the default because most annotations describe a day rather than a moment, and a day that silently shifts is worse than one that is slightly imprecise.

occurred_at is not "now"

It is when the annotated thing happened. Backfilling is normal — a note about last Tuesday's release belongs on last Tuesday, not on the day somebody got round to recording it.

curl -X POST "https://truestat.io/api/v1/sites/$SITE_ID/notes" \
  -H "Authorization: Bearer $TRUESTAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Deployed v2 — new pricing page",
    "occurred_at": "2026-09-16T10:00:00Z",
    "granularity": "minute"
  }'

There is no site_id field

The site comes from the path. A body carrying site_id is refused, not ignored — a caller who believed it worked would otherwise go on believing it, writing every note to one site while thinking they were spread across several.

The same applies to any other unexpected field: the body is strict.

The author is the key's creator

Not the key, and not nobody. See Notes — it decides who can later edit the note, including from the dashboard.

Authorizations

Authorizationstringheaderrequired

An API key from Settings → API keys. Scopes available: analytics:read, sites:read, sites:write, settings:read, settings:write, goals:read, goals:write, funnels:read, funnels:write, notes:read, notes:write, alerts:read, alerts:write, team:read, team:write, revenue:read, revenue:write.

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path parameters

siteIdstring (uuid)pathrequired
The site's id, from `GET /sites`. A site that is not yours and a site that does not exist both answer 403, so this cannot be used to discover other people's ids.

Request bodyapplication/json

bodystringbodyrequired
The text. Up to 500 characters — a label, not a document.
granularityenum<string>body
`date` pins the note to a calendar day and it stays there even if the site's timezone changes later; `minute` pins it to the instant. A campaign is a day, a deploy is a time.
options:dateminute
occurred_atstring (date-time)bodyrequired
When the annotated thing happened.

Response

application/json

The answer.

dataobject
metaobject