Tracking API

Tiny's public ingest API accepts a bootstrap request first, then event batches for the session created during bootstrap. The browser runtime handles this for you, but the contract is documented here if you need to inspect or reproduce requests directly.

Bootstrap endpoint

Primary endpoint:

POST /api/ingest/v2/:publicKey/bootstrap

Tiny uses bootstrap to validate the source, validate the origin, and create or resume a session for the source.

Events endpoint

Primary endpoint:

POST /api/ingest/v2/:publicKey/events

This endpoint accepts a batch of normalized events for the session created during bootstrap.

Bootstrap request

{
  "ingestVersion": 2,
  "sdkVersion": "0.2.0",
  "anonymousId": "vis_abc123",
  "sessionId": null,
  "userId": null,
  "page": {
    "url": "https://app.example.com/dashboard",
    "path": "/dashboard",
    "title": "Dashboard",
    "referrer": "https://app.example.com/login"
  },
  "context": {
    "locale": "en-GB",
    "timezone": "Europe/London",
    "userAgent": "Mozilla/5.0 ..."
  }
}

Bootstrap validation requires:

  • ingestVersion must be 2
  • sdkVersion must be present
  • anonymousId must be present
  • page must be present and JSON-compatible

Events request

{
  "ingestVersion": 2,
  "sdkVersion": "0.2.0",
  "batchId": "batch_123",
  "sentAt": "2026-04-10T13:45:05.000Z",
  "sessionId": "sess_789",
  "anonymousId": "vis_abc123",
  "userId": "user_42",
  "context": {
    "locale": "en-GB",
    "timezone": "Europe/London",
    "userAgent": "Mozilla/5.0 ..."
  },
  "events": [
    {
      "id": "evt_1",
      "type": "page",
      "timestamp": "2026-04-10T13:45:04.000Z",
      "anonymousId": "vis_abc123",
      "sessionId": "sess_789",
      "page": {
        "url": "https://app.example.com/dashboard",
        "path": "/dashboard",
        "title": "Dashboard",
        "referrer": null
      },
      "properties": {},
      "context": {}
    },
    {
      "id": "evt_2",
      "type": "track",
      "name": "report_generated",
      "timestamp": "2026-04-10T13:45:05.000Z",
      "anonymousId": "vis_abc123",
      "sessionId": "sess_789",
      "properties": {
        "reportType": "weekly"
      },
      "context": {}
    }
  ]
}

Event validation requires:

  • ingestVersion must be 2
  • sdkVersion, batchId, sentAt, sessionId, and anonymousId must be present
  • events must be a non-empty array
  • each event needs an id, type, and ISO timestamp
  • track events require name
  • page events require a page payload

Responses

Successful bootstrap response:

{
  "ok": true,
  "attemptId": "attempt_123",
  "ingestVersion": 2,
  "sourceId": "src_123",
  "sessionId": "sess_789",
  "anonymousId": "vis_abc123",
  "identifiedUserId": null,
  "ingestUrl": "/api/ingest/v2/pk_abc/events"
}

Successful events response:

{
  "ok": true,
  "attemptId": "attempt_124",
  "sourceId": "src_123",
  "sessionId": "sess_789",
  "accepted": 2,
  "duplicates": 0,
  "rejected": 0,
  "results": [
    {
      "eventId": "evt_1",
      "analyticsEventId": "analytics_evt_1",
      "type": "page",
      "name": "page_view",
      "status": "accepted",
      "code": null,
      "message": null
    }
  ]
}

Tiny also returns structured failures for:

  • 403 source protection checks such as origin mismatch or invalid session for source
  • 413 payloads that exceed source limits
  • 422 invalid bootstrap or events payloads, including an issues array
  • 429 request-rate limits

Legacy routes

Legacy ingest routes still exist:

POST /api/ingest/:publicKey/bootstrap
POST /api/ingest/:publicKey/events

Treat them as deprecated compatibility paths. New installs should use the v2 routes only.

Was this page helpful?