API Reference
The Worker is a small JSON API. The Swift package and the dashboard use it, and you can call it from anything else, such as a server, a script or another platform's app.
- Base URL: your Worker's address, for example
https://oddlytics-worker.<your-subdomain>.workers.dev. - API key: endpoints marked Key need the Worker's
AUTH_KEYin anX-API-KEYheader. Without it they answer401 {"error": "Unauthorized"}. - CORS: every origin is allowed, for
GET,POST,DELETEandOPTIONS, with theContent-TypeandX-API-KEYheaders. - Errors are JSON:
{"error": "..."}. WhenENVIRONMENTisn'tproduction, server errors also include amessage.
Endpoints
| Method | Path | Key | What it does |
|---|---|---|---|
GET | / | Health check. | |
POST | /track | Key | Record one event or a batch. |
GET | /stats/overview | Totals, apps and top events. | |
GET | /stats/events | Event counts by name. | |
GET | /stats/timeline | Events and users per day. | |
GET | /stats/groups | Event counts per group for one app. | |
GET | /stats/app/:appId | Totals for one app. | |
GET | /stats/app/:appId/settings | One app's display name and icon URL. | |
POST | /stats/app/:appId/settings | Key | Set one app's display name and icon URL. |
GET | /stats/users | Users, newest activity first. | |
GET | /stats/users/:userId | One user's totals and apps. | |
GET | /stats/users/:userId/activity | One user's latest events. | |
GET | /aliases | Event display names. | |
POST | /aliases | Key | Set an event's display name. |
DELETE | /aliases | Key | Remove an event's display name. |
DELETE | /events/:eventId | Key | Delete one event. |
DELETE | /users/:userId | Key | Delete every event for a user ID. |
DELETE | /apps/:appId | Key | Delete every event for an app. |
The GET /stats/... responses are cached for 60 seconds, and GET /aliases and app settings for 5 minutes.
POST /track
Send one event as the body, or several as {"events": [...]}.
curl -X POST https://oddlytics-worker.<your-subdomain>.workers.dev/track \
-H "Content-Type: application/json" \
-H "X-API-KEY: your-auth-key" \
-d '{
"event": "app_opened",
"app_id": "MyApp",
"session_id": "6F1C2A4E-0000-0000-0000-000000000000",
"user_id": "user-123",
"platform": "Android",
"metadata": { "screen": "Home" },
"timestamp": 1760000000
}'| Field | Required | Notes |
|---|---|---|
event | Yes | Non-empty, up to 255 characters. |
app_id | Yes | Non-empty, up to 255 characters. |
session_id | Yes | Non-empty, up to 255 characters. |
user_id | No | Stored as given, or empty. |
platform | No | A string. Defaults to iOS. |
metadata | No | A JSON object, up to 10 KB. |
timestamp | No | Seconds since 1970. Defaults to the time the Worker receives it. |
A batch may hold up to 100 events. If any event fails validation, the whole request is rejected with 400 and nothing is stored. On success:
{ "success": true, "count": 1 }Reading Stats
GET /stats/overview
Query: days, a number of days such as 7 or 7d, or all (the default).
Returns total_events and total_users for the range, previous_total_events for the period of the same length before it, apps (each with app_id, total_events, total_sessions, first_seen, last_seen, icon_url and display_name, all time), and top_events (the ten most common event names, all time).
GET /stats/events
Query: app_id, start_date and end_date (all optional; dates as YYYY-MM-DD). Returns {"events": [{"event_name", "count"}]}, most common first.
GET /stats/timeline
Query: app_id (optional) and days, from 1 to 365 (default 7). Returns {"timeline": [{"date", "count", "user_count"}]}, one entry per day that has events.
GET /stats/groups
Query: app_id (required). Returns {"groups": [{"name", "events": [{"name", "count"}]}]}, grouped by the group key in each event's metadata.
GET /stats/app/:appId
Returns total_events, total_users and total_sessions for the app.
GET /stats/users
Query: limit (default 50), offset (default 0), search (part of a user ID) and app_id. Returns users (each with user_id, total_events, total_sessions, first_seen, last_seen and last_app) and pagination (total, limit, offset).
GET /stats/users/:userId
Returns user_id, stats (total_events, total_sessions, total_apps, first_seen, last_seen) and apps (app_id, event_count, last_used). Answers 404 if the user has no events.
GET /stats/users/:userId/activity
Query: app_id (optional) and limit (default 50). Returns {"events": [...]}, newest first, each with id, event_name, app_id, timestamp, platform, session_id and metadata.
Display Names and App Settings
# Set a display name
curl -X POST https://<worker>/aliases \
-H "Content-Type: application/json" -H "X-API-KEY: your-auth-key" \
-d '{"app_id": "MyApp", "event_name": "screen_view", "alias": "Screen View"}'
# Remove it
curl -X DELETE https://<worker>/aliases \
-H "Content-Type: application/json" -H "X-API-KEY: your-auth-key" \
-d '{"app_id": "MyApp", "event_name": "screen_view"}'
# Set an app's display name and icon
curl -X POST https://<worker>/stats/app/MyApp/settings \
-H "Content-Type: application/json" -H "X-API-KEY: your-auth-key" \
-d '{"display_name": "My App", "icon_url": "https://example.com/icon.png"}'GET /aliases takes an optional app_id and returns {"aliases": [{"app_id", "event_name", "alias"}]}.
When you post app settings, a field you leave out or send empty keeps its current value.
Deleting Data
curl -X DELETE https://<worker>/events/42 -H "X-API-KEY: your-auth-key"
curl -X DELETE https://<worker>/users/<user-id> -H "X-API-KEY: your-auth-key"
curl -X DELETE https://<worker>/apps/MyApp -H "X-API-KEY: your-auth-key"Each returns {"success": true, "deleted": <number of events>}. Deleting an event ID that doesn't exist answers 404.