Base URL and conventions
The control-plane API is rooted at {API_ORIGIN}/api/v1. The API serves /healthz, /readyz, and /metrics on HTTP_PORT (8080 in the bundled deployments); its declared METRICS_PORT does not have a separate listener. Public rendered status pages are served by the separate status-page service.
export PROBARA_API_ORIGIN='https://monitoring.example.com'
curl "$PROBARA_API_ORIGIN/healthz"
curl "$PROBARA_API_ORIGIN/readyz"| Convention | Behavior |
|---|---|
| Content type | Send application/json for JSON request bodies; import endpoints also accept supported file content |
| Identifiers | Resources use UUID-shaped IDs unless the endpoint uses a public slug or capability token |
| Times | Use RFC 3339 timestamps for filters and scheduled fields |
| Tenant scope | Resolved from an admin membership selection or permanently from the API key |
| Deletion | Many operational resources are soft-deleted first and cleaned asynchronously |
| Errors | Most application handlers return JSON error information, but a few low-level validation paths can return plain HTTP error text |
Authentication
- Administrator session
- Login establishes an HTTP-only access cookie and a rotating refresh session. It is the credential used by the web application and can switch among allowed tenants.
- API key
- Send
Authorization: Bearer pk_<secret>. The key has read or write scope and is bound to its creation tenant. - Push token
- Used only in
/api/v1/push/{token}. No cookie or bearer key is required because possession of the URL token authorizes a heartbeat. - MCP access token
Bearer pbm_at_…, issued to an AI agent by the MCP sign-in. Valid only on/api/v1/mcp: every tool call there runs as one of the routes below with the same checks. A direct REST call with it returns 401.
curl \
-H 'Authorization: Bearer pk_<secret>' \
-H 'Accept: application/json' \
"$PROBARA_API_ORIGIN/api/v1/monitors?page=1&page_size=20"| Public authentication route | Purpose |
|---|---|
POST /api/v1/auth/login | Local username/password login |
POST /api/v1/auth/refresh | Rotate and refresh an admin session |
POST /api/v1/auth/logout | Revoke the current refresh session and clear cookies |
GET /api/v1/auth/me | Read the current session user when present |
GET /api/v1/auth/oidc/status | Report OIDC availability |
GET /api/v1/auth/oidc/start | Begin authorization-code + PKCE login |
GET /api/v1/auth/oidc/callback | Complete the provider callback |
GET /api/v1/users/bootstrap/status | Check whether first-user bootstrap remains available |
POST /api/v1/users/bootstrap/first | Create the one-time first superadministrator |
Tenant selection and permissions
For an administrator cookie, send X-Tenant-ID: <tenant-id> to select an allowed membership. A supported tenant_id query fallback can select context where middleware permits it. API keys ignore caller-selected tenant values and remain pinned to one tenant.
curl \
--cookie cookie-jar.txt \
-H 'X-Tenant-ID: <tenant-id>' \
"$PROBARA_API_ORIGIN/api/v1/auth-context"| Principal | Reads | Mutations |
|---|---|---|
| Tenant viewer / read API key | Tenant data | Only approved compute-only POST diagnostics |
| Tenant editor / write API key | Tenant data | Normal operational writes; not tenant-admin-only governance |
| Tenant admin | Tenant data and audit | All tenant writes, API-key management, and tenant settings |
| Platform superadmin | Any selected tenant plus platform users | Platform user administration and tenant operations |
Compute-only POST exceptions include /monitors/test, /monitors/import/preview, /monitors/dependency-suggestions, /mesh/probe, and /ai-settings/test. They do not persist monitor/result/graph state. Channel test sends a real outbound notification and therefore is not read-only. The AI label routes persist operator verdicts, so they are writes: viewers and read keys get HTTP 403.
Pagination and common filters
Collection APIs generally accept 1-based page and page_size. The common maximum is 100; locations allow up to 200, and dashboard recent-item limits are capped at 50. Use returned total/page metadata where provided rather than inferring completion from a short page.
| Resource | Important filters |
|---|---|
| Monitors | tag (repeatable where supported), enabled, page, page_size (default 20, max 100); newest first by creation time, ties broken by id, so pages never overlap |
| Monitor results | limit, optional RFC 3339 since |
| Batch monitor results | ids (comma-separated or repeated, up to 100), limit per monitor (default 20, max 100) |
| Monitor analytics | range: 1h, 6h, 24h, 7d, 30d, 90d, 365d |
| Alerts | status, monitor_id, since, q, suppressed, sort, dir, page, page_size (max 100) |
| Maintenance | status, monitor_id, page, page_size |
| Mesh history | source, target, and hours |
| Dashboard | range, repeatable tag, and bounded recent-item limits |
| Audit log | action, outcome, actor_id, RFC 3339 from / to, pagination |
Monitor endpoints
| Method and path | Purpose |
|---|---|
POST /api/v1/monitors | Create a monitor |
GET /api/v1/monitors | List tenant monitors |
POST /api/v1/monitors/test | Execute a supplied config without persistence or alerting |
GET /api/v1/monitors/{id} | Get full monitor detail |
PATCH /api/v1/monitors/{id} | Update monitor fields/config |
DELETE /api/v1/monitors/{id} | Soft-delete a monitor |
POST /api/v1/monitors/{id}/run | Queue a persisted on-demand check (refused with 400 for group, push and agent monitors, which never run a check of their own) |
GET /api/v1/monitors/{id}/results | List raw check results |
GET /api/v1/monitors/results/batch?ids=...&limit=20 | The newest results for up to 100 monitors in one request, plus each group's direct member ids (see below) |
GET /api/v1/monitors/{id}/metrics-snapshots?range=24h | Per-check metrics_data over a range, newest snapshot per bucket (≤720), clamped to 30-day raw retention |
GET /api/v1/monitors/{id}/analytics | Get summary, series, latency, downtime, and coverage |
DELETE /api/v1/monitors/{id}/history | Delete the monitor's stored result history |
GET /api/v1/monitors/{id}/artifacts/screenshot?path=... | Retrieve an authorized synthetic-browser screenshot path |
POST /api/v1/monitors/{id}/snooze | Create a one-monitor snooze by duration or end time |
{
"name": "Public API",
"type": "http",
"config": {
"url": "https://api.example.com/health",
"method": "GET",
"expected_status_classes": ["2xx"],
"json_assertions": [
{"path": "status", "op": "equals", "value": "ok"}
],
"max_latency_ms": 1500
},
"interval_seconds": 60,
"timeout_seconds": 15,
"enabled": true,
"tags": ["api", "production"],
"consecutive_failures_threshold": 2,
"notification_mode": "default",
"location_ids": [],
"location_quorum": 1
}GET /monitors/results/batch answers {"limit", "results": {monitor_id: [...]}, "members": {group_id: [member_id, ...]}}. Each results list is what GET /monitors/{id}/results?limit=N returns for that monitor: a group's list merges its nested members' newest results under a derived group-status row, and members lists a group's direct members (by name). Ids that are not live monitors of the workspace are left out rather than failing the request; more than 100 ids, a malformed id, or a limit outside 1-100 is a 400. Raw results are kept 30 days, so a monitor that has not run for longer than that comes back with an empty list (here and on the per-monitor endpoint alike). Viewers and read-scope keys may call it.
Monitor relationships, bulk, and portability
| Method and path | Purpose |
|---|---|
GET /api/v1/monitors/{id}/members | List group members |
POST /api/v1/monitors/{id}/members | Add monitors to a group |
DELETE /api/v1/monitors/{id}/members | Remove supplied monitors from a group |
GET /api/v1/monitors/{id}/dependencies | List upstream dependencies |
POST /api/v1/monitors/{id}/dependencies | Create an upstream edge |
DELETE /api/v1/monitors/{id}/dependencies/{dependsOnId} | Remove an upstream edge |
GET /api/v1/monitors/{id}/dependents | List downstream dependents |
GET /api/v1/monitors/dependency-graph | Get all participating nodes and directed edges |
POST /api/v1/monitors/dependency-suggestions | Compute advisory AI suggestions without saving edges. 503 not_configured when local AI is off; 503 unavailable with Retry-After while the service is busy or restarting |
POST /api/v1/monitors/dependency-suggestions/label | Write; record an operator verdict on a suggestion as an AI label |
POST /api/v1/monitors/bulk/alerting | Update notification routing for selected monitors |
POST /api/v1/monitors/bulk/delete | Soft-delete selected monitors |
GET /api/v1/monitors/export | Export a versioned YAML configuration bundle |
POST /api/v1/monitors/import/preview | Parse, map, and validate without creating resources |
POST /api/v1/monitors/import | Execute import and return per-row outcomes |
Dependency creation rejects self-reference and transitive cycles with HTTP 409. Export/import is not a universal secret-preserving round trip: masked fields and newer monitor types require explicit preview and remediation. Import preview reports a schema for recognized sources (portable_monitor_export, uptime_kuma_export), per-row warnings, and skipped_rows naming source records it declined to translate.
Locations, mesh, and maintenance
| Method and path | Purpose |
|---|---|
POST /api/v1/locations | Create a private location and credential |
GET /api/v1/locations | List tenant locations |
GET /api/v1/locations/{id} | Get location detail |
PATCH /api/v1/locations/{id} | Update location metadata/state |
DELETE /api/v1/locations/{id} | Delete and detach a location |
GET /api/v1/locations/{id}/deploy | Generate credential-scoped deployment information |
GET /api/v1/mesh | Read current directional mesh topology/state |
GET /api/v1/mesh/history | Read filtered edge history |
POST /api/v1/mesh/probe | Run a compute-only immediate directional diagnostic |
POST /api/v1/maintenance-windows | Create scheduled maintenance |
GET /api/v1/maintenance-windows | List/filter maintenance |
GET /api/v1/maintenance-windows/{id} | Get a maintenance window |
PATCH /api/v1/maintenance-windows/{id} | Update a maintenance window |
DELETE /api/v1/maintenance-windows/{id} | Delete a maintenance window |
{
"title": "Database upgrade",
"description": "Planned primary database maintenance",
"starts_at": "2026-08-01T22:00:00Z",
"ends_at": "2026-08-01T23:30:00Z",
"monitor_ids": ["<monitor-id>"]
}SLA endpoints
| Endpoint | Purpose |
|---|---|
GET /api/v1/slas | List SLAs with the running period's status; monitor_id keeps SLAs covering that monitor |
POST /api/v1/slas | Create an SLA |
GET /api/v1/slas/{id} | Get an SLA |
PATCH /api/v1/slas/{id} | Partially update an SLA; monitor_ids replaces the explicit monitor set |
DELETE /api/v1/slas/{id} | Delete an SLA and its issued reports |
GET /api/v1/slas/{id}/report | Live report for period (2026-09, 2026-Q3, 2026-W39), from/to local dates, or the running period; format=json|csv|pdf returns a download |
GET /api/v1/slas/{id}/reports | List issued (frozen) reports |
POST /api/v1/slas/{id}/reports | Issue a closed period's report ({"period": "2026-09"}, default the last closed period); 409 when already issued |
GET /api/v1/sla-reports/{id} | An issued report as frozen; format=json|csv|pdf returns a download |
{
"name": "Checkout platform",
"target_pct": 99.9,
"aggregation": "serial",
"period": "monthly",
"timezone": "Europe/Paris",
"degraded_counts_as_down": false,
"tags": ["checkout"],
"monitor_ids": ["<monitor-id>"]
}Report semantics, composite rules and limits are covered in SLA reporting.
Dashboard endpoints
| Method and path | Response focus |
|---|---|
GET /api/v1/dashboard/overview | Stats, trend, activity, platform health, problems, recent failures/alerts, tags |
GET /api/v1/dashboard/summary | Curated tag-group service summary and ungrouped monitors |
GET /api/v1/dashboard/problem-monitors | Monitors needing attention |
GET /api/v1/dashboard/recent-failures | Bounded recent failing results |
GET /api/v1/dashboard/recent-alerts | Bounded recent alert records |
GET /api/v1/dashboard/group-sparkline | Deferred time series for one dashboard group |
Overview ranges are 1h, 24h, 7d, 30d, 90d, and 365d, defaulting to 24h. Dashboard group rows are tag-derived presentation, not group-monitor resources. Problem monitors are ordered by current state (down, degraded, unknown, up), then failed checks, before the limit applies, so a monitor that is down now is never cut. A group's attention_count counts members down or degraded now; failures earlier in the range show only in its uptime.
Alert and notification endpoints
| Method and path | Purpose |
|---|---|
GET /api/v1/alerts | Paginated/filterable alert list (status, monitor_id, since — alerts open at any point after it, so still-open alerts always match — suppressed=true|false, and q, a case-insensitive search of the subject: the monitor name or either location of a mesh alert, or an exact alert ID, up to 200 characters), ordered by sort (triggered_at, the default; status, ranked active, acknowledged, resolved; monitor, the subject name, case-insensitive; duration, counting open alerts to now) and dir (asc/desc; defaults to desc for triggered_at and duration, so newest and longest first, and asc otherwise: lifecycle rank for status, A-Z for monitor). Under a single status filter a status sort is the default newest-first order. Ties read newest first, then by ID, so pages never overlap; any other value is HTTP 400. Items carry suppression_reason and impacted_count when dependency suppression applies, and triage when the alert has an automatic triage |
GET /api/v1/alerts/recent | Recent alerts with bounded limit |
GET /api/v1/alerts/stream | Authenticated server-sent event stream |
GET /api/v1/alerts/{id} | Alert detail; a labelable triage also carries label_context |
POST /api/v1/alerts/{id}/acknowledge | Mark an open condition acknowledged |
POST /api/v1/alerts/{id}/resolve | Manually close an alert lifecycle |
POST /api/v1/alerts/bulk/acknowledge | Write; acknowledge many alerts, body {"ids": [...]} (1-500). Never all-or-nothing: HTTP 200 with items (the alerts acknowledged, in their new state) and failed ({id, reason}, reason not_found (missing, another tenant's, or its monitor was deleted), already_acknowledged or already_resolved). Each requested alert gets its own alert.acknowledge audit event, as the single endpoint records it, with details.bulk |
POST /api/v1/alerts/bulk/resolve | Write; resolve many open alerts, same body and response shape (reason not_found or already_resolved), one alert.resolve audit event per alert |
POST /api/v1/alerts/{id}/triage/label | Write; confirm or correct a ready triage as an AI label |
GET /api/v1/alert-channels | List configured channels |
POST /api/v1/alert-channels | Create a plugin-backed channel |
GET /api/v1/alert-channels/{id} | Read channel with protected config masked |
PATCH /api/v1/alert-channels/{id} | Update config or active state |
DELETE /api/v1/alert-channels/{id} | Delete a channel |
POST /api/v1/alert-channels/{id}/test | Send a real test notification |
GET /api/v1/alert-channel-plugins | List channel plugin form manifests |
GET /api/v1/alert-channel-plugins/{type} | Get one plugin manifest |
GET /api/v1/notification-settings | Read tenant defaults, reminders, anomaly and auto-incident settings |
PUT /api/v1/notification-settings | Replace tenant notification settings |
Alert reads (list, detail, recent alerts, and the acknowledge and resolve responses) and the alerts[] of GET /incidents/{id} include a read-only triage object when the alert has a triage row. The object is omitted when there is no row, and stream events never carry it. Fields that do not apply are null: id, status (pending, ready, failed, skipped), hypothesis (catalogue key) and title, confidence (low, medium, high) and summary when ready, skip_reason (resolved, cooldown, not_modeled, disabled) when skipped, error_message (a fixed string) when failed, created_at (claim time), completed_at, labelled, and labelable (ready and at least one decision was sent to the model). Only GET /alerts/{id} adds label_context, in the same shape as an incident analysis's, when the triage is labelable.
Incident endpoints
| Method and path | Purpose |
|---|---|
GET /api/v1/incidents | List incidents (paginated; total counts all, open_total counts unresolved) |
POST /api/v1/incidents | Create an incident |
GET /api/v1/incidents/{id} | Get full incident context |
PATCH /api/v1/incidents/{id} | Update metadata |
POST /api/v1/incidents/{id}/state | Transition lifecycle state |
POST /api/v1/incidents/{id}/timeline | Add an internal note, public update, or supported timeline event |
POST /api/v1/incidents/{id}/alerts | Attach an alert |
DELETE /api/v1/incidents/{id}/alerts/{alertId} | Detach an alert |
POST /api/v1/incidents/{id}/monitors | Attach a monitor |
DELETE /api/v1/incidents/{id}/monitors/{monitorId} | Detach a monitor |
PUT /api/v1/incidents/{id}/status-pages/{statusPageId} | Publish incident to one status page |
DELETE /api/v1/incidents/{id}/status-pages/{statusPageId} | Unpublish from one status page |
POST /api/v1/incidents/{id}/ai-analysis | Queue asynchronous local AI analysis; a queueing failure records the run as failed |
GET /api/v1/incidents/{id}/ai-analysis | Read pending, ready, or failed analysis state |
POST /api/v1/incidents/{id}/ai-analysis/label | Write; confirm or correct a ready analysis as an AI label |
A ready analysis that can be labelled also carries label_context on the analysis GET and on GET /incidents/{id}: catalogue_version, the incident-level predicted key, the selectable choices (key, title), one entry per monitor decision (id, monitor_id, monitor_name, which is null once the monitor is purged, and predicted), and the current label or null. It is omitted for pending, failed and legacy analyses, analyses that sent no decisions, and analyses completed before labels existed. The analysis GET and GET /incidents/{id} also add monitor_names, a map from each monitor ID cited in metadata (evidence references and incomplete-evidence notes) to the monitor's current name; purged monitors are absent, and the map is omitted when nothing resolves.
AI label endpoints
| Method and path | Who and purpose |
|---|---|
POST /api/v1/incidents/{id}/ai-analysis/label | Editor, admin, superadmin or write key; label an incident analysis |
POST /api/v1/alerts/{id}/triage/label | Editor, admin, superadmin or write key; label an alert's automatic triage |
POST /api/v1/monitors/dependency-suggestions/label | Editor, admin, superadmin or write key; label a dependency suggestion |
POST /api/v1/ai-labels/export | Tenant admin or superadmin session only (no API key); download training rows |
Bodies are strict JSON up to 64 KiB, and unknown fields are rejected with validation_error, so evidence can never be supplied: the server copies it from the stored decision snapshot or recomputes it. Labelling never calls the model and works with local AI disabled. A label is one row per analysis, triage or unordered monitor pair; sending again replaces the verdict and answers (latest wins). Every label route returns the label: subject_type, subject_id (analyses and triages only), verdict, incident_label (for a triage, its alert-level cause), answers keyed by opaque decision ID (label, plus source: operator, confirmed or propagated), catalogue_version, model_version, labeled_by (null for API keys) and labeled_at.
{
"analysis_id": "<optional; defaults to the newest ready analysis>",
"verdict": "corrected",
"incident_label": "dns_failure",
"answers": { "<decision id>": "insufficient_evidence" }
}verdict is confirmed or corrected. Confirming keeps every decision's shown answer and accepts incident_label only when it equals the prediction; correcting requires an incident_label from the analysis's choices. Optional answers override single decisions. Errors: 400 validation_error (bad verdict, a cause or override that is not a choice of that decision, unknown decision ID), 404 not_found (incident or analysis_id not in this tenant), 409 analysis_not_ready (pending, failed, or no ready analysis), 409 analysis_not_labelable (legacy, no-decision, or pre-label analysis).
{
"verdict": "corrected",
"label": "dns_failure",
"answers": { "<decision id>": "insufficient_evidence" }
}The triage label works like the incident analysis label, with label in place of incident_label. Confirming accepts label only when it equals the triage's hypothesis, and correcting requires one of its choices. The label is stored with subject type alert_triage, and its evidence comes from the triage's stored decision snapshot. Errors: 400 validation_error (bad verdict, a cause or override that is not a choice, unknown decision ID, unknown fields), 404 not_found (alert not in this tenant), 409 triage_not_ready (no triage, or one that is pending, failed or skipped), 409 triage_not_labelable (the triage sent no decisions).
{
"monitor_id": "<dependent monitor>",
"depends_on_id": "<suggested upstream>",
"verdict": "rejected",
"corrected": "reversed"
}verdict is confirmed or rejected; corrected is allowed only with rejected and is reversed, shared_upstream, insufficient_evidence or no_dependency (the default). Either orientation addresses the same pair. Errors: 400 validation_error (identical or malformed IDs, bad verdict), 404 not_found (a monitor is not a live monitor of this tenant), 409 pair_evidence_unavailable (the pair is no longer a candidate: disabled, a group, outside the 250-monitor set, or no shared signal left).
Export takes an optional body with since and until (RFC 3339, a half-open window on the label's last update) and subject_types (incident_analysis, alert_triage, dependency_pair); an empty body exports everything, and an inverted window or unknown type is a validation_error. It returns application/x-ndjson as probara-ai-labels-YYYYMMDD.jsonl with X-Probara-Export-Rows and X-Probara-Export-Skipped headers: one training row per decision, and rows whose stored evidence fails validation are skipped and counted. Triage rows share a split group with the earliest incident that links the alert, or form their own group when none does. More than 5,000 labels or 20,000 rows returns 400 export_too_large; narrow the window. Rows contain no identifiers (see security).
Status page endpoints
| Method and path | Purpose |
|---|---|
POST /api/v1/status-pages | Create a page |
GET /api/v1/status-pages | List pages |
GET /api/v1/status-pages/{id} | Get page configuration |
PATCH /api/v1/status-pages/{id} | Update page and settings |
DELETE /api/v1/status-pages/{id} | Delete a page |
GET /api/v1/status-pages/{id}/template | Read published/draft/version state |
DELETE /api/v1/status-pages/{id}/template | Reset to the built-in template as a new version |
GET /api/v1/status-pages/{id}/template/default | Export current built-in source |
GET /api/v1/status-pages/{id}/template/versions/{version}/source | Read archived version source |
PUT /api/v1/status-pages/{id}/template/draft | Validate and save the one draft |
DELETE /api/v1/status-pages/{id}/template/draft | Discard draft |
POST /api/v1/status-pages/{id}/template/publish | Publish draft and archive current source |
POST /api/v1/status-pages/{id}/template/revert | Republish archived source as a new version |
| Template-library route | Purpose |
|---|---|
GET /api/v1/status-page-templates | List tenant template metadata |
POST /api/v1/status-page-templates | Create a validated library entry |
GET /api/v1/status-page-templates/{templateId} | Get entry metadata |
GET /api/v1/status-page-templates/{templateId}/source | Retrieve source |
PATCH /api/v1/status-page-templates/{templateId} | Rename/update source |
DELETE /api/v1/status-page-templates/{templateId} | Delete library entry |
The separate status-page service exposes unauthenticated GET /public/status/{slug}, /data, and /stream, plus draft preview. Do not use those public routes to mutate configuration.
Agent and push endpoints
| Method and path | Authorization and purpose |
|---|---|
POST /api/v1/otlp/v1/metrics | Tenant write API key (Bearer) plus X-Probara-Agent-Id; OTLP/HTTP metric ingest for collector agents |
POST /api/v1/agent/metrics | Retired legacy-agent ingest (sunset 18 Nov 2026): an authenticated request gets 410 Gone, a JSON error naming the OTLP endpoint, and a Link: </api/v1/otlp/v1/metrics>; rel="successor-version" header. The body is not read and nothing is stored |
GET /api/v1/monitors/{id}/agent/install | Authenticated; generated install command/information incl. collector_config and collector_version |
GET /api/v1/monitors/{id}/agent/config.yaml | Authenticated; raw collector YAML for one platform (linux/darwin/windows) |
GET /api/v1/monitors/{id}/agent/install/script.sh | Authenticated Unix installer |
GET /api/v1/monitors/{id}/agent/install/script.ps1 | Authenticated Windows installer |
GET /api/v1/monitors/{id}/agent/uninstall/script.sh | Authenticated Unix uninstaller |
GET /api/v1/monitors/{id}/agent/uninstall/script.ps1 | Authenticated Windows uninstaller |
GET /api/v1/monitors/{id}/metrics/series | Authenticated; discover stored metric series for an agent monitor |
POST /api/v1/monitors/{id}/metrics/query | Authenticated (read-only despite POST, viewer-accessible); batch range queries — ≤12 queries, ≤50 series and ≤2,000 points per series per response, automatic raw-vs-rollup selection for ranges over 48 h or steps ≥1 h |
GET /api/v1/monitors/{id}/push/info | Authenticated generated push URL/config information |
GET /api/v1/push/{token} | Public capability token; success heartbeat plus query metrics |
POST /api/v1/push/{token} | Public capability token; status/error plus arbitrary metric values |
Discovery endpoints
| Method and path | Authorization and purpose |
|---|---|
GET/POST /api/v1/discovery/integrations | Authenticated; list or create a Kubernetes discovery integration. Create (write access) returns the collector token once; the first namespace becomes home_namespace. An integration carries namespaces (active), home_namespace, pending_namespaces (always an array) and pending_status (always an object of {reason, checked_at} per pending namespace with a recorded skip) |
PATCH /api/v1/discovery/integrations/{id} | Write access; any of name (1–100 characters), location_id (an enabled location, null clears, absent keeps), add_namespaces, remove_namespaces (each at most 20). Added namespaces become pending; the home namespace cannot be removed; at most 20 namespaces active and pending together. Namespaces change only as this delta: an unknown field such as namespaces is a 400. Returns 200 with the integration (no token); an edit that changes nothing returns 200 and is not audited. See Change namespaces |
GET /api/v1/discovery/integrations/{id}/namespaces | Authenticated; items: home first, then the other active namespaces, then pending ones, each with namespace, state (active/pending), home, reason and checked_at (pending with a recorded skip only), services (stored, present or missing) and linked_services (linked to a monitor that is not deleted), counted whatever the state |
DELETE /api/v1/discovery/integrations/{id} | Write access; removes inventory and links, keeps monitors |
POST /api/v1/discovery/integrations/{id}/rotate-token | Write access; returns a new collector token and invalidates the old one |
GET /api/v1/discovery/integrations/{id}/coverage | Authenticated; page, q (service or namespace), gaps=true, sort=name|gaps apply across all services in the integration's active namespaces; summary is unfiltered. summary.missing counts resource_missing only, summary.missing_grace counts resource_missing_grace. inventory_stale is reported once at the top level and prefixed to every row's gaps, but the summary counts, gaps=true and sort=gaps ignore it |
POST /api/v1/discovery/services/{id}/approve | Write access; create and link a monitor from a suggestion. Returns {monitor_id, created} with 200; a repeat for an already linked suggestion returns that monitor with created: false, writes nothing and is not audited |
POST /api/v1/discovery/services/{id}/links, DELETE …/links/{monitorID} | Write access; explicitly link or unlink an existing monitor |
GET /api/v1/discovery-collectors/{id}/config, POST …/snapshot | Integration-scoped collector token only (Bearer); never valid on other routes. Config returns {namespaces, pending, revision}; a snapshot reports pending namespaces it could not scan in skipped ({namespace: reason}) |
POST /api/v1/discovery-collectors/{id}/snapshot returns 204 when the snapshot is applied, 401 for a missing or malformed bearer, 404 for an unknown integration or wrong token, 400 for an invalid body (unknown JSON fields are ignored, so a newer collector still syncs; known fields are fully validated), 409 for a stale or replayed revision (no Retry-After), 409 with Retry-After: 10 when it scanned a namespace that was removed meanwhile (not counted against the rate limit), 429 rate_limited with Retry-After (whole seconds) when a snapshot was applied, or refused with 422, less than 60 seconds earlier, and 422 inventory_limit when the integration would store more than 10,000 resources after reconciling its active namespaces (pending namespaces that would exceed it are left pending as inventory_limit and the snapshot is applied without them). A refused snapshot leaves the inventory, revision and last-seen time unchanged; a 422 still counts against the rate limit, since it did the reconciliation work. The token and the rate limit are checked before the body of up to 4 MiB is read.
Administration endpoints
| Method and path | Required role and purpose |
|---|---|
GET /api/v1/auth-context | Authenticated; effective credential, tenant, role, and scope |
GET /api/v1/tenants | Admin session; list membership-filtered tenants or all for superadmin |
GET /api/v1/tenant-settings | Authenticated; read retention and dashboard tags |
PATCH /api/v1/tenant-settings | Tenant admin; update retention/dashboard tags |
GET /api/v1/api-keys | Authenticated tenant context; list safe key metadata |
POST /api/v1/api-keys | Tenant admin; create and return secret once |
DELETE /api/v1/api-keys/{id} | Tenant admin; revoke |
GET /api/v1/audit-log | Tenant admin; filtered, sortable audit records |
GET /api/v1/audit-log/actions | Tenant admin; distinct action catalog |
GET /api/v1/ai-settings | Authenticated; workspace switch, installation switch, cached local-service readiness, and the auto-triage settings (auto_triage_enabled, triage_delay_seconds, triage_cooldown_seconds, triage_max_per_hour, triage_notifications_enabled) |
PUT /api/v1/ai-settings | Authorized write; partial update of enabled and the five auto-triage fields (at least one; ranges in AI settings); unknown fields are rejected |
POST /api/v1/ai-settings/test | Fresh local-service readiness probe; refreshes this replica's readiness cache |
GET /api/v1/users | Superadmin; list platform users |
POST /api/v1/users | Superadmin; create user and memberships |
GET /api/v1/users/{id} | Superadmin; user detail |
PATCH /api/v1/users/{id} | Superadmin; update role/auth/membership fields |
DELETE /api/v1/users/{id} | Superadmin; delete subject to last-admin/self safeguards |
GET /api/v1/oidc-group-mappings | Superadmin; list OIDC group→role mappings, groups seen at past SSO logins, and groups-claim config facts |
POST /api/v1/oidc-group-mappings | Superadmin; map an IdP group to a tenant role or platform superadmin |
PATCH /api/v1/oidc-group-mappings/{id} | Superadmin; change a mapping's role and/or display label |
DELETE /api/v1/oidc-group-mappings/{id} | Superadmin; remove a mapping |
Server-sent events
GET /api/v1/alerts/stream is an authenticated tenant alert stream. The public status-page service exposes a different slug-scoped stream at /public/status/{slug}/stream. Use a native EventSource-compatible client where cookie authentication is required, or a streaming HTTP client that can attach the bearer key.
curl -N \
-H 'Authorization: Bearer pk_<secret>' \
"$PROBARA_API_ORIGIN/api/v1/alerts/stream"- Reconnect with backoff after network or proxy interruption.
- Treat events as refresh/invalidation signals and refetch durable resource state.
- Keep proxy buffering disabled for the streaming route.
- Do not assume the stream is a replayable audit log.
Build a safe client
- Set explicit HTTP timeouts and retry only operations whose idempotency you understand.
- Retry GET requests with bounded exponential backoff; do not blindly retry create, run-now, import execution, notification tests, or incident timeline writes.
- Use import preview before import execution and monitor test before saving complex check configuration.
- Respect
401,403,404,409,410,422, and rate/transport failures as distinct conditions. - Send
***, or leave the field out, to keep a masked designated secret field on a monitor; send an empty string when you deliberately want to clear it. Notification channel configs differ: an empty string also keeps the stored secret, andnullclears an optional one (a required one is then rejected by validation). - Page collections and retain returned coverage metadata for analytics.
- Store full API keys only in a secret manager; the API returns them once.
- Log resource IDs and status codes without logging bearer keys, push tokens, installer output, WebSocket headers, connection strings, or webhook secrets.