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
| Limit | Free | Pro | Business |
|---|---|---|---|
| Events / hour (per app) | 1,800 | 10,000 | Unlimited |
| Storage retention | 7 days | 30 days | 90 days |
| Dashboard window | 7d | 30d | 90d |
| Min ping interval | 15 min | 1 min | 1 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.
| Cap | Value | On breach |
|---|---|---|
| Body cap | 1 MB per batch | 413 too_large |
| Events per batch | 500 max | 422 validation |
| Unique feature names per batch | 50 max | 422 validation |
| error string | ≤ 8 KB | 422 validation |
| meta serialized JSON | ≤ 16 KB | 422 validation |
| meta nesting depth | ≤ 8 | 422 validation |
| userId | ≤ 256 chars | 422 validation |
| platform | ≤ 32 chars (also closed-list coerced) | Coerced to "unknown" |
| source | ≤ 64 chars (also closed-list coerced) | Coerced to "event" |
| sessionId | ≤ 128 chars | 422 validation |
| durationMs | ≤ 3,600,000 (1 hour) | 422 validation |
| featureName | ≤ 200 chars | 422 validation |
| meta.calls (feature_call_summary) | ≤ 50,000 per summary event | Clamped |
| Aggregates / facets row cap | 50,000 rows in window | 409 window_too_wide |
| Export row cap | 10,000 rows | 409 too_large |
| Time range max | 365 days | 403 retention_exceeded |
HTTP error codes
| Code | Meaning | Action |
|---|---|---|
| 204 | Empty batch (no events to process). | OK — no-op. |
| 422 | Validation error (missing or invalid field, or a cap above breached). | Fix the payload; check the field caps above. |
| 401 | Invalid publishableKey. | Verify the key against the dashboard. |
| 403 | Origin 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. |
| 409 | Result too large (export > 10k, aggregates > 50k). | Narrow filters or shorten the time range. |
| 413 | Body > 1 MB. | Reduce batch size — trim meta, or flush more often (batches cap at 500 events). |
| 429 | Per-IP slowapi exhaust OR per-app hourly quota exhausted. | Honor Retry-After and X-Onelo-Quota-Remaining. |
| 503 | origin_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).
| Header | Description |
|---|---|
| X-Onelo-Quota-Limit | Events/hour for this plan — the literal string "unlimited" when the plan has no cap. |
| X-Onelo-Quota-Remaining | Events left in the current hour window. |
| Retry-After | Only 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.
| Condition | Status |
|---|---|
| total_calls == 0 in window | no_data |
| total_calls < 10 | low_data |
| errorRate ≥ threshold (default 0.10) | down |
| errorRate ≥ threshold × 0.5 (default 0.05) | degraded |
| errorRate < threshold × 0.5 | ok |
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.
| Field | Allowed values | Coercion sentinel |
|---|---|---|
| platform | swift, python, js, electron, reactnative, android, flutter, node, php | unknown |
| source | feature_call, feature_call_summary, track, event, global_error | event |
Wire format
The single ingestion endpoint. The SDK's apiUrl is your Onelo API root.
POST {apiUrl}/api/sdk/monitor/events/batch
Content-Type: application/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 · Path | Description |
|---|---|
| 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/features | Add 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|json | Streaming 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-rule | Update threshold / window / enabled for a feature. |
| GET /api/monitor/issues?app_id=… | Grouped error issues. |
| GET /api/monitor/issues/{issue_id}/samples | Sample 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}/events | All 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}/notify | Re-send the alert email for an incident. |
| GET /api/monitor/alert-config?app_id=… | Read per-app alert email configuration. |
| PUT /api/monitor/alert-config | Save 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/notifications | In-app bell-icon notifications. |
| PATCH /api/monitor/notifications/{id}/read | Mark a notification as read. |
| POST /api/monitor/notifications/read-all | Mark all notifications as read. |
{feature_name} (the feature's name, not its UUID). Only DELETE /features/{feature_id} takes the UUID.