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
Monitored features per app (SDK + ping, shared pool)110Unlimited
Min ping interval15 min1 min1 min

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, kotlin, 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 (same filters as /runs; 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/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