Documentation menu

Sites

Create, inspect, configure and delete the websites you track. Every other resource hangs off a site, so start here to obtain a siteId.

5 endpoints, all relative to https://analytics.appfor.you. Shared rules live in Conventions, Rate limits and Error codes.

List sites

GET/api/v2/sitesscope: read

Returns the sites owned by the API key's user plus the sites where they are an accepted team member, newest first. If the key was created with a site restriction, only those sites are returned.

  • `total` is the full accessible count before pagination is applied.
  • Owned sites and sites you joined as a team member are interleaved into ONE global `createdAt` descending order, and `limit`/`offset` are applied by the database — so paging walks the whole accessible set consistently instead of restarting per source.
  • Backward compatible with the pre-scopes API: every previously returned field is unchanged. `trackingId` and `isPublic` are new additions.

Query parameters

NameTypeDescription
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?limit=25"

Example response

{
  "sites": [
    {
      "siteId": "6507f1f77bcf86cd799439011",
      "trackingId": "a1b2c3d4e5",
      "name": "My Website",
      "domain": "example.com",
      "isActive": true,
      "isPublic": false,
      "createdAt": "2025-01-15T10:30:00.000Z"
    }
  ],
  "total": 1,
  "limit": 25,
  "offset": 0
}

Create a site

POST/api/v2/sitesscope: write

Creates a site with a freshly generated 10-character public `trackingId`. Every setting other than name and domain takes its schema default: isActive true, isPublic false, dataRetentionDays 90, botDetectionEnabled true, botLogEnabled true, recordingEnabled false, recordingSamplingRate 100, recordingMaxDuration 600, recordingPrivacyLevel "standard". The response includes the ready-to-paste tracking snippet.

  • A site-restricted API key (one created with an explicit site list) cannot create sites and receives 403.
  • No plan-based site-count limit is enforced, matching the dashboard behaviour.

Request body

FieldTypeRequiredDescription
namestringYesDisplay name. Trimmed; must be non-empty.
domainstringYesDomain being tracked, e.g. "example.com" (no protocol, no trailing slash). Trimmed; must be non-empty.

Example request

curl -X POST "https://analytics.appfor.you/api/v2/sites" \
  -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Marketing site", "domain": "example.com" }'

Example response (201)

{
  "siteId": "6507f1f77bcf86cd799439011",
  "trackingId": "a1b2c3d4e5",
  "name": "Marketing site",
  "domain": "example.com",
  "isActive": true,
  "isPublic": false,
  "publicSlug": null,
  "publicUrl": null,
  "dataRetentionDays": 90,
  "maxRetentionDays": 90,
  "plan": "free",
  "botDetectionEnabled": true,
  "botLogEnabled": true,
  "recordingEnabled": false,
  "recordingSamplingRate": 100,
  "recordingMaxDuration": 600,
  "recordingPrivacyLevel": "standard",
  "role": "owner",
  "createdAt": "2025-03-21T09:00:00.000Z",
  "updatedAt": "2025-03-21T09:00:00.000Z",
  "tracking": {
    "scriptUrl": "https://analytics.appfor.you/api/sites/a1b2c3d4e5/tracker.js",
    "snippet": "<script defer data-site=\"a1b2c3d4e5\" src=\"https://analytics.appfor.you/api/sites/a1b2c3d4e5/tracker.js\"></script>"
  }
}

Get site detail

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

Returns identity, active/public state, data-retention settings with the owner's plan cap, bot-detection flags, session-recording settings, the caller's role on the site, and the tracker script URL plus ready-to-paste snippet.

  • Returns 404 (never 403) when the site does not exist or you have no access, so other users’ sites are never revealed.

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.

Example request

curl -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011"

Example response

{
  "siteId": "6507f1f77bcf86cd799439011",
  "trackingId": "a1b2c3d4e5",
  "name": "My Website",
  "domain": "example.com",
  "isActive": true,
  "isPublic": true,
  "publicSlug": "sunny-otter-42",
  "publicUrl": "/share/sunny-otter-42",
  "dataRetentionDays": 365,
  "maxRetentionDays": 365,
  "plan": "starter",
  "botDetectionEnabled": true,
  "botLogEnabled": true,
  "recordingEnabled": false,
  "recordingSamplingRate": 100,
  "recordingMaxDuration": 600,
  "recordingPrivacyLevel": "standard",
  "role": "owner",
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-03-20T12:00:00.000Z",
  "tracking": {
    "scriptUrl": "https://analytics.appfor.you/api/sites/a1b2c3d4e5/tracker.js",
    "snippet": "<script defer data-site=\"a1b2c3d4e5\" src=\"https://analytics.appfor.you/api/sites/a1b2c3d4e5/tracker.js\"></script>"
  }
}

Update a site

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

Every field is optional but at least one must be supplied. This one endpoint consolidates what the dashboard splits across site settings, sharing, retention, bot detection and session recording.

  • Team members with the `viewer` role are read-only and receive 403.
  • `dataRetentionDays` is capped by the SITE OWNER's plan — free 90, starter 365, growth 730, business 1095. Exceeding it returns 400 with a message naming the plan and its limit.
  • `isPublic` is OWNER-ONLY. A PATCH carrying it from an accepted team member — including one with the `admin` role — returns 403, and that check runs before any field is applied, so a mixed PATCH cannot partially succeed. Publishing a site exposes its analytics on an unauthenticated `/share/<slug>` URL, so this mirrors the dashboard sharing route, which resolves only for the owner.
  • Setting `isPublic` to true mints a `publicSlug` if the site has none; setting it to false clears the slug and revokes the public dashboard URL.

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

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

FieldTypeRequiredDescription
namestringNoNew display name (trimmed, non-empty).
domainstringNoNew tracked domain (trimmed, non-empty).
isActivebooleanNofalse pauses the site.
isPublicbooleanNoPublish or unpublish the read-only public dashboard. OWNER ONLY — a team member of any role, admin included, receives 403 for a PATCH carrying this field.
dataRetentionDaysintegerNoDays of raw data to keep, 30–1095, further capped by the owner plan.
botDetectionEnabledbooleanNoToggle automatic bot filtering.
botLogEnabledbooleanNoToggle logging of detected bot traffic.
recordingEnabledbooleanNoToggle session-recording capture.
recordingSamplingRatenumberNoPercentage of sessions to record, 0–100.
recordingMaxDurationintegerNoMaximum recording length in seconds, 60–3600.
recordingPrivacyLevel'standard' | 'strict'No"strict" masks more input content in recordings.

Example request

curl -X PATCH "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011" \
  -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Marketing site (EU)", "dataRetentionDays": 365, "isPublic": true }'

Example response

{
  "siteId": "6507f1f77bcf86cd799439011",
  "trackingId": "a1b2c3d4e5",
  "name": "Marketing site (EU)",
  "domain": "example.com",
  "isActive": true,
  "isPublic": true,
  "publicSlug": "sunny-otter-42",
  "publicUrl": "/share/sunny-otter-42",
  "dataRetentionDays": 365,
  "maxRetentionDays": 365,
  "plan": "starter",
  "role": "owner",
  "updatedAt": "2025-03-21T09:14:00.000Z"
}

Delete a site

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

Requires the `admin` scope, requires the caller to be the site OWNER (accepted team members with the admin role receive 403), and requires an explicit confirmation. Pass `confirm=<site id>` as a query parameter or as a `confirm` field in the JSON body, repeating whichever identifier you used in the URL (the Mongo _id or the public trackingId).

Irreversible. Requires the admin scope and confirm={siteId} — see Destructive operations.

  • Cascade — every child document is removed before the Site itself, across all 23 collections listed in `cascaded`: RecordingChunk, PageView, Event, Goal, Funnel, TeamMember, UserProfile, SessionRecording, SessionSummary, RevenueEvent, WebVital, BotEvent, BotRule, Segment, Alert, Webhook, Dashboard, Annotation, ReportConfig, Insight, Integration, CustomDimension, CustomDimensionValue. No visitor PII (session recordings, visitor profiles, revenue events, Web Vitals) is left orphaned.
  • RecordingChunk is keyed by recording, not by site, so it is resolved from the site’s SessionRecording ids and cleared first.
  • Children are deleted BEFORE the Site document, so a failure part-way through leaves the site intact and the call safe to retry.
  • API keys restricted to this site are deliberately NOT rewritten. An empty site list means "all of the owner’s sites", so pulling the deleted id out of a single-site key would silently widen it. A stale id simply never matches again.
  • `deletedCounts` reports the number of documents removed per collection and `totalDeleted` their sum; the Site document itself is not counted.
  • Without a confirmation you get 400 naming the exact value to send.

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
confirmstringDestructive-operation confirmation. Must exactly equal the `siteId` in the URL. May be sent as a `confirm` field in the JSON body instead.

Request body

Optional. Either send no body and use `?confirm=<siteId>`, or send `{ "confirm": "<siteId>" }`.

FieldTypeRequiredDescription
confirmstringNoMust exactly equal the site identifier used in the URL.

Example request

curl -X DELETE "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011?confirm=6507f1f77bcf86cd799439011" \
  -H "Authorization: Bearer $ANALYTICS_API_KEY"

Example response

{
  "deleted": true,
  "siteId": "6507f1f77bcf86cd799439011",
  "trackingId": "a1b2c3d4e5",
  "cascaded": ["RecordingChunk","PageView","Event","Goal","Funnel","TeamMember","UserProfile","SessionRecording","SessionSummary","RevenueEvent","WebVital","BotEvent","BotRule","Segment","Alert","Webhook","Dashboard","Annotation","ReportConfig","Insight","Integration","CustomDimension","CustomDimensionValue"],
  "deletedCounts": {
    "RecordingChunk": 18402,
    "PageView": 152110,
    "Event": 4820,
    "Goal": 3,
    "Funnel": 1,
    "TeamMember": 2,
    "UserProfile": 9120,
    "SessionRecording": 640,
    "SessionSummary": 640,
    "RevenueEvent": 212,
    "WebVital": 30110,
    "BotEvent": 880,
    "BotRule": 0,
    "Segment": 4,
    "Alert": 2,
    "Webhook": 1,
    "Dashboard": 1,
    "Annotation": 7,
    "ReportConfig": 1,
    "Insight": 12,
    "Integration": 1,
    "CustomDimension": 2,
    "CustomDimensionValue": 5504
  },
  "totalDeleted": 222475
}