Collector API
The HealthGuard Collector provides REST APIs for SDK event ingestion and Dashboard data queries.
Base URL: http://your-collector-host:3100
POST /api/events/batch
Batch event ingestion endpoint. The SDK calls this endpoint to report captured events.
Request
{
"appKey": "web_abc123",
"events": [
{
"eventId": "evt_xxx",
"appKey": "web_abc123",
"platform": "web",
"type": "error",
"timestamp": 1716192000000,
"sessionId": "session_xxx",
"anonymousId": "anon_xxx",
"sdkVersion": "0.1.0",
"errorType": "js",
"message": "Cannot read property 'foo' of undefined",
"stack": "...",
"fingerprint": "js:abc123",
"breadcrumbs": []
}
]
}Response
{
"accepted": 1
}Fields
| Field | Type | Required | Description |
|---|---|---|---|
appKey | string | Yes | Unique app identifier |
events | array | Yes | Event array, min 1, max 50 items |
POST /api/auth/register
User registration.
Request
{
"email": "user@example.com",
"password": "your-password"
}Response
{
"token": "session_xxx",
"user": {
"id": "user_xxx",
"email": "user@example.com",
"createdAt": 1716192000000
}
}POST /api/auth/login
User login.
Request
Same as /api/auth/register.
Response
Same as /api/auth/register.
GET /api/auth/me
Get current user info.
Headers
Authorization: Bearer <token>GET /api/apps
List apps for the current user.
Headers
Authorization: Bearer <token>POST /api/apps
Create a new app.
Headers
Authorization: Bearer <token>Request
{
"name": "My Web App",
"type": "web"
}GET /api/overview
Get data overview.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
appKey | string | No | Filter by app |
Response
{
"totals": {
"events": 128,
"errors": 12,
"failedRequests": 3,
"affectedUsers": 8,
"issues": 5
}
}GET /api/issues
List issues.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
appKey | string | No | Filter by app |
GET /api/issues/:id
Get issue details.
Response
{
"issue": {
"id": "web_abc123:js:def456",
"appKey": "web_abc123",
"fingerprint": "js:def456",
"message": "Cannot read property 'foo' of undefined",
"errorType": "js",
"eventCount": 15,
"firstSeenAt": 1716100000000,
"lastSeenAt": 1716192000000
},
"events": [...]
}PATCH /api/apps/:appKey
Updates the display name using the project owner's Authorization: Bearer <SESSION_TOKEN>.
{ "name": "Example driver" }Accepts only name, containing 1–100 characters after trimming. Returns { "app": { ... } } with id, name, appKey, type and createdAt. Errors: 401 unauthenticated; 400 INVALID_APP_NAME invalid fields/name; 404 APP_NOT_FOUND absent or not owned by the caller.
Updates only the name. App keys, historical data and notification rules remain unchanged. No schema migration or SDK upgrade is required; older Collectors must be upgraded first.
PATCH /api/issues/:id/archive
Requires the project owner’s bearer session. Unverified issues may be archived; no code repair is executed.
{ "note": "Server-side fix completed" }The body and note may be omitted. Notes are trimmed and limited to 4000 characters; invalid types, unknown fields or excessive length return 400. Unauthenticated requests return 401; missing or unowned issues return 404. Returns { issue } with archiveRecord: { note, archivedAt }; timestamps are milliseconds. Duplicate requests preserve the saved archive record. New events remain counted without reopening manual archives.
PATCH /api/issues/:id/reopen
Only the project owner may restore an issue. Returns it to Current issues while retaining the previous archive note; no application code changes. Collector startup adds issues.archive_record JSONB without deleting historical repair metadata. Deploy Collector before Dashboard.
Retained lifecycle and PR write APIs also enforce project ownership, preventing legacy calls from bypassing archive permissions.
Event receipt and notification clocks
Issue detail events include nullable receivedAt (Unix milliseconds), set by the Collector at the batch boundary. Client-supplied values are ignored; timestamp remains client-reported occurrence time. Duplicate Event IDs retain first receipt and counts. Historical rows return null without inferring receipt from insertion, event or mail times; existing ordering and filters retain occurrence-time semantics.
Notification history adds eventTimestamp, receivedAt, sendingStartedAt and sentAt; createdAt remains job creation time. Processing start precedes configuration and connection work. sentAt is only set after successful SMTP acceptance and is null for failed, canceled or unknown delivery; neither confirms inbox placement. Unrecorded historical fields may be absent/null and test messages have no event time. Migration adds nullable events.received_at only; retain it on rollback.
Device fields and owner-only queries (Unreleased)
Flutter 0.3.0 adds independent deviceId (hgd1_ plus 64 SHA-256 hex characters), deviceIdStatus, deviceIdSource, deviceIdScope, and unavailable deviceIdReason. Model/OS use deviceInfo.model/system; event App/SDK versions use release/sdkVersion. Lookup failure omits deviceId; legacy events remain accepted and unknown. Collector validates format and consistent identity metadata, persists fields in existing JSONB and adds a project/time index without historical ID backfill.
Project-owner Bearer sessions can call:
GET /api/devices?appKey=<KEY>&kind=stable&search=HG-...&limit=50&offset=0GET /api/devices/events?appKey=<KEY>&key=<URL_ENCODED_FULL_GROUP_KEY>&limit=50&offset=0
Optional platform/start/end filters use millisecond timestamps. Kinds are stable/installation/unknown; page size is 1–100. Lists return devices/total/totals, with stableDevices/installations/unidentifiedEvents; history returns events/total. Category totals cover the platform/time range; list total also applies kind/search. Use the returned full group key for history, never a shortcode. Unauthenticated requests return 401; absent or foreign-owned projects return 404. Counts describe retained anomaly data, not a permanent physical fleet registry.