Reference

Limits, error codes, status thresholds, and the ingestion contract for Onelo Monitor. Numbers below are pulled directly from backend/app/routes/sdk_monitor.py and related migrations — keep this page bookmarked while you instrument.

Plan limits

LimitFreeProBusiness
Events / hour (per app)1,80010,000Unlimited
Storage retention7 days30 days90 days
Dashboard window7d30d90d
Min ping interval15 min1 min1 min

Two separate counts per app: HTTP monitors (pings you add) and tracked features (names the SDK registers). A ping never uses up a tracked-feature slot, nor the other way round. How many each plan allows is on the features page, read live from the plans.

Hard caps (any plan)

These caps are enforced on every request regardless of plan — defense-in-depth against buggy or hostile clients.

CapValueOn breach
Body cap1 MB per batch413 too_large
Events per batch500 max422 validation
Unique feature names per batch50 max422 validation
error string≤ 8 KB422 validation
meta serialized JSON≤ 16 KB422 validation
meta nesting depth≤ 8422 validation
userId≤ 256 chars422 validation
platform≤ 32 chars (also closed-list coerced)Coerced to "unknown"
source≤ 64 chars (also closed-list coerced)Coerced to "event"
sessionId≤ 128 chars422 validation
durationMs≤ 3,600,000 (1 hour)422 validation
featureName≤ 200 chars422 validation
meta.calls (feature_call_summary)≤ 50,000 per summary eventClamped
Aggregates / facets row cap50,000 rows in window409 window_too_wide
Export row cap10,000 rows409 too_large
Time range max365 days403 retention_exceeded

HTTP error codes

CodeMeaningAction
204Empty batch (no events to process).OK — no-op.
422Validation error (missing or invalid field, or a cap above breached).Fix the payload; check the field caps above.
401Invalid publishableKey.Verify the key against the dashboard.
403Origin not allowed (web only); plan retention exceeded; bundle id not registered; etc.Inspect the response detail field. For web, add the page origin to allowed_origins in the dashboard.
409Result too large (export > 10k, aggregates > 50k).Narrow filters or shorten the time range.
413Body > 1 MB.Reduce batch size — trim meta, or flush more often (batches cap at 500 events).
429Per-IP slowapi exhaust OR per-app hourly quota exhausted.Honor Retry-After and X-Onelo-Quota-Remaining.
503origin_check_unavailable — the origin allowlist could not be read (web only).Transient — retry shortly.

Quota headers

Non-empty ingest responses include the headers below so SDKs and operators can self-throttle (an empty batch returns a bare 204 without them).

HeaderDescription
X-Onelo-Quota-LimitEvents/hour for this plan — the literal string "unlimited" when the plan has no cap.
X-Onelo-Quota-RemainingEvents left in the current hour window.
Retry-AfterOnly set on 429 responses. A fixed backoff hint: 60 s for the per-IP limit, 3600 s for the hourly quota — not a live countdown to the window reset.

To shed load before you hit the hourly quota, the Python SDK samples client-side: monitor.init(sample_rate=…, success_sample_rate=…) — keep-probabilities for error vs success/track events (default 1.0 = keep everything). A high-throughput backend can drop the noisy success events while keeping every error.

Status thresholds (Feature Health)

Source: backend/app/routes/monitor_mgmt.py. Thresholds are per-feature and configurable via the alert rule — the values below use the default of 0.10.

ConditionStatus
total_calls == 0 in windowno_data
total_calls < 10low_data
errorRate ≥ threshold (default 0.10)down
errorRate ≥ threshold × 0.5 (default 0.05)degraded
errorRate < threshold × 0.5ok

HTTP ping monitors use a different rule (same file): status is taken from the most recent ping and is only ever no_data, degraded, or ok — there is no down state. It reads degraded when the last ping failed or the error rate over the last 200 checks is ≥ 0.10.

Closed-list field values

Both platform and source are closed lists. Values outside the list are silently coerced to a sentinel — this prevents a malicious or buggy client from inflating another platform's counters in dashboards or billing.

FieldAllowed valuesCoercion sentinel
platformswift, python, js, electron, reactnative, android, flutter, node, phpunknown
sourcefeature_call, feature_call_summary, track, event, global_errorevent

Wire format

The single ingestion endpoint. The SDK's apiUrl is your Onelo API root.

HTTP
POST {apiUrl}/api/sdk/monitor/events/batch
Content-Type: application/json
json
{
  "publishableKey": "onelo_pk_live_…",
  "events": [
    {
      "featureName": "checkout.charge",
      "ok": false,
      "durationMs": 1342,
      "error": "Stripe declined: insufficient_funds",
      "meta": { "amount": 1999 },
      "source": "track",
      "userId": "usr_abc",
      "platform": "swift",
      "sessionId": "sess_2026_05_10_42"
    }
  ]
}

The publishableKey field carries your app key. Client SDKs send the publishable key (onelo_pk_live_*); the backend SDKs (Python, Node.js, PHP) send their server secret key (onelo_sk_live_*) in this same field. The endpoint accepts either.

Two behaviours worth knowing: an optional client-supplied event timestamp is display-only and ignored when it is older than 1 hour or more than 60 seconds in the future (the row falls back to server receive time — quotas, windows and incidents always key on server time). And key lookups are cached for up to 60 seconds, so a revoked key can keep authenticating for up to a minute after revocation.

REST API (dashboard read)

All endpoints below require Authorization: Bearer <session_token> (the dashboard JWT). They are not part of the public SDK contract.

Method · PathDescription
GET /api/monitor/features?app_id=…Feature Health: one row per feature, with status and aggregates over the selected window (range=1h|24h|7d|30d or from/to; response fields are named calls24h / users24h / sessions24h regardless of the window).
POST /api/monitor/featuresAdd an HTTP ping monitor.
DELETE /api/monitor/features/{feature_id}Remove a monitor.
GET /api/monitor/features/{feature_name}/runs?app_id=…Runs list with filters (status, platform, environment, release, q, …) and cursor pagination.
GET /api/monitor/features/{feature_name}/logs?app_id=…Raw event list over the selected window (range or from/to only — no filters).
GET /api/monitor/features/{feature_name}/aggregates?app_id=…&group_by=…Aggregated runs along one facet.
GET /api/monitor/features/{feature_name}/facets?app_id=…Facet value counts for the filter dropdowns.
GET /api/monitor/features/{feature_name}/runs/export?app_id=…&format=csv|jsonStreaming export of the runs /runs lists (status, platform, environment, release, from/to, sort; sdk/model/trigger/app_build/q are refused with 400; capped at 10,000 rows).
GET /api/monitor/features/{feature_name}/alert-rule?app_id=…Read the per-feature alert rule.
PATCH /api/monitor/features/{feature_name}/alert-ruleUpdate threshold / window / enabled for a feature.
GET /api/monitor/issues?app_id=…Grouped error issues.
GET /api/monitor/issues/{issue_id}/samplesSample events for one issue.
GET /api/monitor/uncaught-errors?app_id=…Uncaught errors (the global handler’s, not tied to a feature), grouped by issue, over the plan’s history window: text, count, first/last seen, trend, and which upgrade prompts apply. Never Feature Health rows.
GET /api/monitor/uncaught-errors/{issue_id}/samples?app_id=…Samples of one uncaught error with stack and origin (your app or the Onelo SDK), up to the plan’s sample cap.
GET /api/monitor/sessions/{session_id}/eventsAll events for a single session — used by the run drawer’s “Open session timeline” button.
GET /api/monitor/incidents?app_id=…List incidents for an app.
GET /api/monitor/incidents/{id}Fetch a single incident.
PATCH /api/monitor/incidents/{id}Resolve, change priority, or edit description / notes.
POST /api/monitor/incidents/{id}/notifyRe-send the alert email for an incident.
GET /api/monitor/alert-config?app_id=…Read per-app alert email configuration.
PUT /api/monitor/alert-configSave per-app alert email configuration.
POST | GET /api/monitor/credentials · DELETE /api/monitor/credentials/{id}Manage Monitor Credentials for authenticated HTTP pings.
GET /api/monitor/notificationsIn-app bell-icon notifications.
PATCH /api/monitor/notifications/{id}/readMark a notification as read.
POST /api/monitor/notifications/read-allMark all notifications as read.
Endpoint shape note. Runs, aggregates, facets and export are separate routes, all keyed by {feature_name} (the feature's name, not its UUID). Only DELETE /features/{feature_id} takes the UUID.
Monitor — Reference | Onelo Docs